Browse Source

Code dump

master
Nils 6 months ago
commit
4136a6aed7
  1. 4
      .gitignore
  2. 251
      LICENSE_jReadJWrite.html
  3. 10
      Makefile
  4. 94
      camera.c
  5. 18
      camera.h
  6. 52
      constants.h
  7. 144
      draw.c
  8. 36
      draw.h
  9. 99
      draw_activity.c
  10. 6
      draw_activity.h
  11. 115
      draw_meterbridge.c
  12. 6
      draw_meterbridge.h
  13. 121
      draw_port_grids.c
  14. 6
      draw_port_grids.h
  15. 107
      draw_xpitches_yports.c
  16. 6
      draw_xpitches_yports.h
  17. 103
      draw_xports_ypitches.c
  18. 6
      draw_xports_ypitches.h
  19. 181
      drawhelper.c
  20. 14
      drawhelper.h
  21. 100
      effect_starfield.c
  22. 6
      effect_starfield.h
  23. BIN
      font.ttf
  24. 40
      glsl330/bloom.fs
  25. 393
      gui.c
  26. 7
      gui.h
  27. 659
      gui_file_dialog.h
  28. 789
      jRead.c
  29. 134
      jRead.h
  30. 566
      jWrite.c
  31. 218
      jWrite.h
  32. 266
      jackclient.c
  33. 19
      jackclient.h
  34. 405
      main.c
  35. 689
      nsm.h
  36. 248
      programstate.c
  37. 79
      programstate.h
  38. 3734
      raygui.h

4
.gitignore

@ -0,0 +1,4 @@
*.out
*.gch
*.json
tgvs

251
LICENSE_jReadJWrite.html

@ -0,0 +1,251 @@
?<html>
<head>
<title>The Code Project Open License (CPOL)</title>
<Style>
BODY, P, TD { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 10pt }
H1,H2,H3,H4,H5 { color: #ff9900; font-weight: bold; }
H1 { font-size: 14pt;color:black }
H2 { font-size: 13pt; }
H3 { font-size: 12pt; }
H4 { font-size: 10pt; color: black; }
PRE { BACKGROUND-COLOR: #FBEDBB; FONT-FAMILY: "Courier New", Courier, mono; WHITE-SPACE: pre; }
CODE { COLOR: #990000; FONT-FAMILY: "Courier New", Courier, mono; }
.SpacedList li { padding: 5px 0px 5px 0px;}
</style>
</head>
<body bgcolor="#FFFFFF" color=#000000>
<h1>The Code Project Open License (CPOL) 1.02</h1>
<br />
<center>
<div style="text-align: left; border: 2px solid #000000; width: 660; background-color: #FFFFD9; padding: 20px;">
<h2>Preamble</h2>
<p>
This License governs Your use of the Work. This License is intended to allow developers
to use the Source Code and Executable Files provided as part of the Work in any
application in any form.
</p>
<p>
The main points subject to the terms of the License are:</p>
<ul>
<li>Source Code and Executable Files can be used in commercial applications;</li>
<li>Source Code and Executable Files can be redistributed; and</li>
<li>Source Code can be modified to create derivative works.</li>
<li>No claim of suitability, guarantee, or any warranty whatsoever is provided. The software is
provided "as-is".</li>
<li>The Article accompanying the Work may not be distributed or republished without the
Author's consent</li>
</ul>
<p>
This License is entered between You, the individual or other entity reading or otherwise
making use of the Work licensed pursuant to this License and the individual or other
entity which offers the Work under the terms of this License ("Author").</p>
<h2>License</h2>
<p>
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CODE PROJECT OPEN
LICENSE ("LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE
LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT
LAW IS PROHIBITED.</p>
<p>
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HEREIN, YOU ACCEPT AND AGREE TO BE
BOUND BY THE TERMS OF THIS LICENSE. THE AUTHOR GRANTS YOU THE RIGHTS CONTAINED HEREIN
IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. IF YOU DO NOT
AGREE TO ACCEPT AND BE BOUND BY THE TERMS OF THIS LICENSE, YOU CANNOT MAKE ANY
USE OF THE WORK.</p>
<ol class="SpacedList">
<li><strong>Definitions.</strong>
<ol class="SpacedList" style="list-style-type: lower-alpha;">
<li><strong>"Articles"</strong> means, collectively, all articles written by Author
which describes how the Source Code and Executable Files for the Work may be used
by a user.</li>
<li><b>"Author"</b> means the individual or entity that offers the Work under the terms
of this License.<strong></strong></li>
<li><strong>"Derivative Work"</strong> means a work based upon the Work or upon the
Work and other pre-existing works.</li>
<li><b>"Executable Files"</b> refer to the executables, binary files, configuration
and any required data files included in the Work.</li>
<li>"<b>Publisher</b>" means the provider of the website, magazine, CD-ROM, DVD or other
medium from or by which the Work is obtained by You.</li>
<li><b>"Source Code"</b> refers to the collection of source code and configuration files
used to create the Executable Files.</li>
<li><b>"Standard Version"</b> refers to such a Work if it has not been modified, or
has been modified in accordance with the consent of the Author, such consent being
in the full discretion of the Author. </li>
<li><b>"Work"</b> refers to the collection of files distributed by the Publisher, including
the Source Code, Executable Files, binaries, data files, documentation, whitepapers
and the Articles. </li>
<li><b>"You"</b> is you, an individual or entity wishing to use the Work and exercise
your rights under this License.
</li>
</ol>
</li>
<li><strong>Fair Use/Fair Use Rights.</strong> Nothing in this License is intended to
reduce, limit, or restrict any rights arising from fair use, fair dealing, first
sale or other limitations on the exclusive rights of the copyright owner under copyright
law or other applicable laws.
</li>
<li><strong>License Grant.</strong> Subject to the terms and conditions of this License,
the Author hereby grants You a worldwide, royalty-free, non-exclusive, perpetual
(for the duration of the applicable copyright) license to exercise the rights in
the Work as stated below:
<ol class="SpacedList" style="list-style-type: lower-alpha;">
<li>You may use the standard version of the Source Code or Executable Files in Your
own applications. </li>
<li>You may apply bug fixes, portability fixes and other modifications obtained from
the Public Domain or from the Author. A Work modified in such a way shall still
be considered the standard version and will be subject to this License.</li>
<li>You may otherwise modify Your copy of this Work (excluding the Articles) in any
way to create a Derivative Work, provided that You insert a prominent notice in
each changed file stating how, when and where You changed that file.</li>
<li>You may distribute the standard version of the Executable Files and Source Code
or Derivative Work in aggregate with other (possibly commercial) programs as part
of a larger (possibly commercial) software distribution. </li>
<li>The Articles discussing the Work published in any form by the author may not be
distributed or republished without the Author&#39;s consent. The author retains
copyright to any such Articles. You may use the Executable Files and Source Code
pursuant to this License but you may not repost or republish or otherwise distribute
or make available the Articles, without the prior written consent of the Author.</li>
</ol>
Any subroutines or modules supplied by You and linked into the Source Code or Executable
Files of this Work shall not be considered part of this Work and will not be subject
to the terms of this License.
</li>
<li><strong>Patent License.</strong> Subject to the terms and conditions of this License,
each Author hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have made, use, import,
and otherwise transfer the Work.</li>
<li><strong>Restrictions.</strong> The license granted in Section 3 above is expressly
made subject to and limited by the following restrictions:
<ol class="SpacedList" style="list-style-type: lower-alpha;">
<li>You agree not to remove any of the original copyright, patent, trademark, and
attribution notices and associated disclaimers that may appear in the Source Code
or Executable Files. </li>
<li>You agree not to advertise or in any way imply that this Work is a product of Your
own. </li>
<li>The name of the Author may not be used to endorse or promote products derived from
the Work without the prior written consent of the Author.</li>
<li>You agree not to sell, lease, or rent any part of the Work. This does not restrict
you from including the Work or any part of the Work inside a larger software
distribution that itself is being sold. The Work by itself, though, cannot be sold,
leased or rented.</li>
<li>You may distribute the Executable Files and Source Code only under the terms of
this License, and You must include a copy of, or the Uniform Resource Identifier
for, this License with every copy of the Executable Files or Source Code You distribute
and ensure that anyone receiving such Executable Files and Source Code agrees that
the terms of this License apply to such Executable Files and/or Source Code. You
may not offer or impose any terms on the Work that alter or restrict the terms of
this License or the recipients&#39; exercise of the rights granted hereunder. You
may not sublicense the Work. You must keep intact all notices that refer to this
License and to the disclaimer of warranties. You may not distribute the Executable
Files or Source Code with any technological measures that control access or use
of the Work in a manner inconsistent with the terms of this License. </li>
<li>You agree not to use the Work for illegal, immoral or improper purposes, or on pages
containing illegal, immoral or improper material. The Work is subject to applicable
export laws. You agree to comply with all such laws and regulations that may apply
to the Work after Your receipt of the Work.
</li>
</ol>
</li>
<li><strong>Representations, Warranties and Disclaimer.</strong> THIS WORK IS PROVIDED
"AS IS", "WHERE IS" AND "AS AVAILABLE", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES
OR CONDITIONS OR GUARANTEES. YOU, THE USER, ASSUME ALL RISK IN ITS USE, INCLUDING
COPYRIGHT INFRINGEMENT, PATENT INFRINGEMENT, SUITABILITY, ETC. AUTHOR EXPRESSLY
DISCLAIMS ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS, INCLUDING
WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY
OR FITNESS FOR A PARTICULAR PURPOSE, OR ANY WARRANTY OF TITLE OR NON-INFRINGEMENT,
OR THAT THE WORK (OR ANY PORTION THEREOF) IS CORRECT, USEFUL, BUG-FREE OR FREE OF
VIRUSES. YOU MUST PASS THIS DISCLAIMER ON WHENEVER YOU DISTRIBUTE THE WORK OR DERIVATIVE
WORKS.
</li>
<li><b>Indemnity. </b>You agree to defend, indemnify and hold harmless the Author and
the Publisher from and against any claims, suits, losses, damages, liabilities,
costs, and expenses (including reasonable legal or attorneys’ fees) resulting from
or relating to any use of the Work by You.
</li>
<li><strong>Limitation on Liability.</strong> EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
LAW, IN NO EVENT WILL THE AUTHOR OR THE PUBLISHER BE LIABLE TO YOU ON ANY LEGAL
THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK OR OTHERWISE, EVEN IF THE AUTHOR
OR THE PUBLISHER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
</li>
<li><strong>Termination.</strong>
<ol style="list-style-type: lower-alpha;">
<li>This License and the rights granted hereunder will terminate automatically upon
any breach by You of any term of this License. Individuals or entities who have
received Derivative Works from You under this License, however, will not have their
licenses terminated provided such individuals or entities remain in full compliance
with those licenses. Sections 1, 2, 6, 7, 8, 9, 10 and 11 will survive any termination
of this License. </li>
<li>If You bring a copyright, trademark, patent or any other infringement claim against
any contributor over infringements You claim are made by the Work, your License
from such contributor to the Work ends automatically.</li>
<li>Subject to the above terms and conditions, this License is perpetual (for the duration
of the applicable copyright in the Work). Notwithstanding the above, the Author
reserves the right to release the Work under different license terms or to stop
distributing the Work at any time; provided, however that any such election will
not serve to withdraw this License (or any other license that has been, or is required
to be, granted under the terms of this License), and this License will continue
in full force and effect unless terminated as stated above.
</li>
</ol>
</li>
<li><strong>Publisher</strong>. The parties hereby confirm that the Publisher shall
not, under any circumstances, be responsible for and shall not have any liability
in respect of the subject matter of this License. The Publisher makes no warranty
whatsoever in connection with the Work and shall not be liable to You or any party
on any legal theory for any damages whatsoever, including without limitation any
general, special, incidental or consequential damages arising in connection to this
license. The Publisher reserves the right to cease making the Work available to
You at any time without notice</li>
<li><strong>Miscellaneous</strong>
<ol class="SpacedList" style="list-style-type: lower-alpha;">
<li>This License shall be governed by the laws of the location of the head office of
the Author or if the Author is an individual, the laws of location of the principal
place of residence of the Author.</li>
<li>If any provision of this License is invalid or unenforceable under applicable law,
it shall not affect the validity or enforceability of the remainder of the terms
of this License, and without further action by the parties to this License, such
provision shall be reformed to the minimum extent necessary to make such provision
valid and enforceable. </li>
<li>No term or provision of this License shall be deemed waived and no breach consented
to unless such waiver or consent shall be in writing and signed by the party to
be charged with such waiver or consent. </li>
<li>This License constitutes the entire agreement between the parties with respect to
the Work licensed herein. There are no understandings, agreements or representations
with respect to the Work not specified herein. The Author shall not be bound by
any additional provisions that may appear in any communication from You. This License
may not be modified without the mutual written agreement of the Author and You.
</li>
</ol>
</li>
</ol>
</div>
</center>
</body>
</html>

10
Makefile

@ -0,0 +1,10 @@
.PHONY: clean
all:
gcc -o tgvs main.c draw.c gui.c jackclient.c drawhelper.c draw_xpitches_yports.c draw_xports_ypitches.c draw_port_grids.c draw_meterbridge.c draw_activity.c programstate.c jRead.c jWrite.c camera.c effect_starfield.c -lraylib -lGL -lm -lpthread -ldl -lrt -lX11 -ljack -llo
clean:
rm tgvs
rm *.gch

94
camera.c

@ -0,0 +1,94 @@
//Standard lib
//Third party
#include "raylib.h"
//Our own files
#include "constants.h"
#include "programstate.h"
#include "camera.h"
extern ProgramState programState;
// https://www.raylib.com/examples/web/core/loader.html?name=core_2d_camera
//Camera controls, such as zoom and reset, are in gui.c
static int screenWidth;
static int screenHeight;
void cameraMoveUpdate() {
programState.camera.offset = (Vector2){ screenWidth/2+programState.cameraCenterOnX, screenHeight/2+programState.cameraCenterOnY };
}
void cameraLeft() {
programState.cameraCenterOnX -= 10;
cameraMoveUpdate();
}
void cameraRight() {
programState.cameraCenterOnX += 10;
cameraMoveUpdate();
}
void cameraUp() {
programState.cameraCenterOnY -= 10;
cameraMoveUpdate();
}
void cameraDown() {
programState.cameraCenterOnY += 10;
cameraMoveUpdate();
}
void cameraMaybeZoom() {
//float rememberZoom = programState.camera.zoom;
if (!programState.lockInput) {
programState.camera.zoom += ((float)GetMouseWheelMove()*0.05f);
if (programState.camera.zoom < 0)
programState.camera.zoom = 0.0f;
//if (rememberZoom != programState.camera.zoom) {}
}
}
void cameraReset() {
//Only called through user action. Camera startup values are either loaded from file or set in programstate.c
programState.camera.zoom = 1.0f;
programState.camera.rotation = 0.0f;
programState.cameraCenterOnX = 0;
programState.cameraCenterOnY = 0;
cameraMoveUpdate();
}
void cameraNormalizeRotation() {
if (programState.camera.rotation > 359) programState.camera.rotation -= 360;
else if (programState.camera.rotation < 0) programState.camera.rotation += 360;
}
void cameraRotateLeft() {
programState.camera.rotation--;
cameraNormalizeRotation();
}
void cameraRotateRight() {
programState.camera.rotation++;
cameraNormalizeRotation();
}
void cameraRotate180() {
programState.camera.rotation += 180.0f;
cameraNormalizeRotation();
}
void cameraFlip() {
programState.camera.zoom *= -1.0f; //this is the same as rotation by 180, and not mirror
//programState.camera.rotation += 180.0f;
}
void mainLoop_cameraReposition() {
if (programState.guiRedrawNeeded) {
screenWidth = GetScreenWidth(); //Changes on resize
screenHeight = GetScreenHeight(); //Changes on resize
programState.camera.target = (Vector2){ screenWidth/2, screenHeight/2 };
programState.camera.offset = (Vector2){ screenWidth/2+programState.cameraCenterOnX, screenHeight/2+programState.cameraCenterOnY };
}
}

18
camera.h

@ -0,0 +1,18 @@
#ifndef CAMERA_H
#define CAMERA_H
void cameraReset();
void cameraMaybeZoom();
void mainLoop_cameraReposition();
void cameraRotateLeft();
void cameraRotateRight();
void cameraRotate180();
//void cameraFlip();
void cameraLeft();
void cameraRight();
void cameraUp();
void cameraDown();
#endif // not defined CAMERA_H

52
constants.h

@ -0,0 +1,52 @@
#ifndef CONSTANTS_H
#define CONSTANTS_H
#include "raylib.h"
#ifndef MAX
#define MAX(a,b) ( (a) < (b) ? (b) : (a) )
#endif
#define VIS_PORTS 16 // Program is able to handle <100 ports. But we never intend anything different than 16 to be honest.
#define VIS_PORTS_SQRT 4 //please choose by hand. If not possibe choose the next higher. e.g. 13 ports -> 4²
//Pixel values
#define NOTE_SMALL_SIDE 14
#define NOTE_LONG_SIDE 20
#define NOTE_BORDER 4 //halved for left/right padding. the NOTE sides already include the border.
#define NOTE_GAP 3
//For GUI
#define HEIGHT 25
#define SPACING 35
typedef struct { // C-N00b: using typedef lets us type Note nt; or (Note){...} instead of struct Note.
int active;
int port;
int x;
int y;
int pitch;
int velocity; //converted to color, size etc. by raylib
float countdown; //0.0f is not existent, 1 is permanently visible, everything between is the fade out process with auto-decrement. To start the timer set to 0.999 or so
} Note;
enum {
//ports are 0-15
background=VIS_PORTS+0,
backgroundLight=VIS_PORTS+1,
backgroundLighter=VIS_PORTS+2,
};
enum {
//Layers in order.
layerIndex_notes,
layerIndex_effects,
layerIndex_sprites,
layerIndex_count, //last one
};
#endif // not defined CONSTANTS_H

144
draw.c

@ -0,0 +1,144 @@
//Standard lib
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
//Third party
#include "raylib.h"
//Our own files
#include "constants.h"
#include "programstate.h"
#include "drawhelper.h"
#include "draw.h"
//Drawing Routines
#include "draw_xpitches_yports.h"
#include "draw_xports_ypitches.h"
#include "draw_port_grids.h"
#include "draw_meterbridge.h"
#include "draw_activity.h"
//Effect Routines
#include "effect_starfield.h"
#define GLSL_VERSION 330
extern ProgramState programState;
Effect effects[NR_OF_EFFECTS] = {
{ false, "Starfield", false, 1.0f, 1, 2, effect_starfield},
{ false, "Snow Fall", false, 1.0f, 1, 3, effect_starfield},
{ false, "Falling Leafs", false, 1.0f, 1, 4, effect_starfield},
{ false, "Rain", false, 1.0f, 1, 5, effect_starfield},
{ false, "Embers", false, 1.0f, 1, 5, effect_starfield},
};
//This needs to be manually synced to gui.c combox
//This order is permanent. No re-ordering after the first release!
//The save file and the GUI depend on it.
void (*drawFunctions[5])() = {
draw_xpitches_yports,
draw_xports_ypitches,
draw_port_grids,
draw_meterbridge,
draw_activity,
};
static Texture2D backgroundTexture;
static RenderTexture2D renderTarget;
static Shader shader;
void loadBackgroundImage(char * path, bool newImage) {
if (programState.nsm) {
char * temporaryAbsolutePath = malloc (sizeof(char) * 4096); //4096 is the max path length in linux
snprintf(temporaryAbsolutePath, (sizeof(char) * 4096), "%s/%s", programState.nsmDirectory, "background.png");
if (newImage) { //User clicked on the GUI button and chose a file
unlink(temporaryAbsolutePath);
symlink(path, temporaryAbsolutePath); //source-file, link
}
programState.pathBackgroundImage = "background.png";
backgroundTexture = LoadTexture(temporaryAbsolutePath);
free(temporaryAbsolutePath);
}
else {
programState.pathBackgroundImage = path;
backgroundTexture = LoadTexture(programState.pathBackgroundImage);
}
}
void init_draw() {
renderTarget = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
shader = LoadShader(0, FormatText("glsl%i/bloom.fs", GLSL_VERSION)); //vs file, fs file
}
void mainLoop_draw(Note *allGUINotes) {
if (IsWindowResized()) {
//printf("Window resized to: %i x %i\n", GetScreenWidth(), GetScreenHeight());
programState.guiRedrawNeeded = true;
}
//Drawing Layers from bottom to top:
//Background Image (or plain color)
//Background Sprites (internal Z-Order = Y)
//Background Effects (e.g. Starfield)
//Main Visualization (Notes)
//Foreground Sprites (internal Z-Order = Y)
//Foreground Effects (e.g. Rain)
if (programState.showBackgroundImageLayer) {
//void DrawTexture(Texture2D texture, int posX, int posY, Color tint);
//Draw centered but not scaled.
DrawTexture(backgroundTexture, GetScreenWidth()/2-backgroundTexture.width/2, GetScreenHeight()/2-backgroundTexture.height/2, WHITE);
}
//BeginTextureMode(renderTarget); // Enable drawing to texture
//ClearBackground(BLACK); //Texture background
//Background Effects Pass
if (programState.showEffectLayer) {
for (int i=0; i<NR_OF_EFFECTS; i++) {
if (effects[i].enabled && !effects[i].foreground) {
effects[i].function(programState.guiRedrawNeeded, effects[i].opacity, effects[i].speed, effects[i].size);
}
}
}
//EndTextureMode(); // End drawing to texture (now we have a texture available for next passes)
//BeginShaderMode(shader);
// DrawTextureRec(renderTarget.texture, (Rectangle){ 0, 0, renderTarget.texture.width, -renderTarget.texture.height }, (Vector2){ 0, 0 }, WHITE);
//EndShaderMode();
if (programState.showDrawMode) {
BeginMode2D(programState.camera);
drawFunctions[programState.drawMode](allGUINotes, programState.guiRedrawNeeded); //One Mode at a time
EndMode2D();
}
//Foreground Effects Pass
if (programState.showEffectLayer) {
for (int i=0; i<NR_OF_EFFECTS; i++) {
if (effects[i].enabled && effects[i].foreground) {
effects[i].function(programState.guiRedrawNeeded, effects[i].opacity, effects[i].speed, effects[i].size);
}
}
}
//DrawCircle(GetScreenWidth()/2, GetScreenHeight()/2, 10, RAYWHITE); //show center point
programState.guiRedrawNeeded = false;
}

36
draw.h

@ -0,0 +1,36 @@
#ifndef DRAW_H
#define DRAW_H
#define NR_OF_EFFECTS 5
typedef void (*effectFunction)(bool guiRedrawNeeded, float opacity, int speedModifier, int size);
typedef struct {
bool enabled;
char * name;
bool foreground;
float opacity;
int speed;
int size; // effect depends on the drawing mode. This could be everything
effectFunction function;
} Effect;
typedef struct {
bool enabled;
char * path; //NSM = subpath, Standalone: Absolute Path
bool foreground;
float opacity;
Color tint;
int x;
int y;
float rotation;
float zoom;
//If you want animation or different versions you have to put them all in one image and then devide into tiles:
int tileSize; //from the whole picture, how big is the square that should be displayed.
int tileIndex; //When the image is divided through size which of the resulting tiles should be displayed. 1D, starting from 0, max is 127
} Sprite;
void mainLoop_draw(Note *allGUINotes);
void loadBackgroundImage(char * path, bool newImage);
void init_draw();
#endif // not defined DRAW_H

99
draw_activity.c

@ -0,0 +1,99 @@
//Standard lib
#include <stdio.h>
#include <math.h>
//Third party
#include "raylib.h"
//Our own files
#include "constants.h"
#include "programstate.h"
#include "drawhelper.h"
#include "draw_activity.h"
static int screenWidth;
static int screenHeight;
static int radius = 80;
const float distanceFactor = 2.5;
static int xOffset;
static int yOffset;
extern ProgramState programState;
static float fadeout[VIS_PORTS];
static void reposition(Note *allGUINotes) {
//This is both init and update. We only call it internally, the name of the function doesn't matter.
//Once guaranteed called on program start and then each window resize
screenWidth = GetScreenWidth();
screenHeight = GetScreenHeight();
xOffset = (int)(screenWidth/2) - (VIS_PORTS_SQRT/2)*radius*distanceFactor;
yOffset = (int)(screenHeight/2) - (VIS_PORTS_SQRT/2)*radius*distanceFactor;
programState.modeDescription = "Each port is one polygon.\nNumber of sites equals active notes.\nThere is no time-dimension.";
}
void drawShape(Vector2 center, int port, float rotation) {
//DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color);
//float fadeValue = pow(sinf(rotation / 180 * PI), 2); //sine wants radians, not degree
//float fadeValue = (float)(GetRandomValue(1,10)-5.0f) / 100.0f + 1.0f; //sparkle
//Color color = Fade(programState.colors[port], fadeValue);
Color border = Fade(BLACK, fadeout[port]);
Color color = Fade(programState.colors[port], fadeout[port]);
if (programState.portActivity[port] < 3) {
//DrawCircleV(Vector2 center, float radius, Color color);
DrawCircleV(center, radius*0.7+NOTE_BORDER, border);
DrawCircleV(center, radius*0.7, color);
}
else {
DrawPoly(center, programState.portActivity[port], radius+NOTE_BORDER*2, rotation, border);
DrawPoly(center, programState.portActivity[port], radius, rotation, color);
}
}
void draw_activity(Note *allGUINotes, int redrawNeeded) {
float ft = GetFrameTime();
int bpmFactor = programState.bpm/40 * programState.bpm/20; //exponential curve. The higher bpm the faster we get. Below 90 it should feel really slow
float rotation = fmod(GetTime() * bpmFactor * 5, 360.0f);
if (redrawNeeded) {
reposition(allGUINotes);
}
for (int port=0; port<VIS_PORTS; port++) {
int portRow = port / VIS_PORTS_SQRT;
int portColumn = port % VIS_PORTS_SQRT;
int blockY = portRow * radius * distanceFactor;
int blockX = portColumn * radius * distanceFactor;
Vector2 center = (Vector2){ xOffset + blockX + radius, yOffset + blockY + radius };
if (programState.showPortBackground && portShouldDraw(port)) {
DrawCircleV(center, radius-NOTE_BORDER, programState.colors[backgroundLight]);
}
if (programState.portActivity[port] > 0) {
//printf("port %i notes %i\n", port, programState.portActivity[port]);
fadeout[port] = 1.0f;
drawShape(center, port, rotation);
}
else if (fadeout[port] > 0.0f) {
if (fadeout[port] == 1.0f) { fadeout[port] = 0.999; } //begin countdown
fadeout[port] = calculateCountdown(fadeout[port], ft);
drawShape(center, port, rotation);
}
if (programState.showConnectedPortnames) {
drawJackPortName(programState.connectedPortNames[port], xOffset+blockX, yOffset+blockY, 18, RAYWHITE, false); //text, x, y, int fontsize, color, vertical
}
}
}

6
draw_activity.h

@ -0,0 +1,6 @@
#ifndef DRAW_ACTIVITY_H
#define DRAW_ACTIVITY_H
void draw_activity(Note *allGUINotes, int redrawNeeded);
#endif // not defined DRAW_ACTIVITY_H

115
draw_meterbridge.c

@ -0,0 +1,115 @@
//Standard lib
#include <stdio.h>
//Third party
#include "raylib.h"
#include "raygui.h" // Required for GUI controls
//Our own files
#include "constants.h"
#include "programstate.h"
#include "drawhelper.h"
#include "draw_meterbridge.h"
extern ProgramState programState;
static Rectangle backgroundRect;
static bool guiGroupEditMode= false;
static int screenHeight;
static int bottom;
static void reposition(Note *allGUINotes) {
//This is both init and update. We only call it internally, the name of the function doesn't matter.
//Once guaranteed called on program start and then each window resize
int screenWidth = GetScreenWidth();
int screenHeight = GetScreenHeight();
int xOffset = (int)(screenWidth/2) - (int)(60*(NOTE_LONG_SIDE+NOTE_GAP)); //we want all tracks around the center.
int yOffset = (int)(screenHeight/2) - (int)(NOTE_SMALL_SIDE+NOTE_GAP)*2; //we want all tracks below the center.
programState.modeDescription = "All port are in one horizontal row.\
\nPitches are left to right, low to high.\
\n\
\nVelocity 'shoots' notes upwards, then they fall back\
\ninfluenced by the fade-out setting.\
\n\
\nYou can set the a 'grouping' parameter for this mode\
\nwhich combines several pitches into a single indicator.\
\nGrouping uses the pitch marker pitch as baseline.\
\nThink of it like the tonic or root note.\
\n\
\n\
\nNotes are only lit up when they are played.\
\nThere is no time-dimension.";
int rootNote = programState.pitchMarkerValue % 12; //factor out the octave. used to start groups on the pitchmarker as root note of the scale
int xpos = xOffset;
int pitchToTheCenter = 64 - (128/programState.meterbridge_grouping/2);
for (int port=0; port<VIS_PORTS; port++) {
for (int midiPitch=0; midiPitch<128; midiPitch++) { //We prepare the full range of pitches, but the main loop will only activate the current pitch range.
int adjustedNotePitch = (int)((midiPitch-rootNote)/programState.meterbridge_grouping) + pitchToTheCenter;
xpos = xOffset + adjustedNotePitch*NOTE_LONG_SIDE + adjustedNotePitch*NOTE_GAP/2;
allGUINotes[port*128 + midiPitch].x = xpos;
allGUINotes[port*128 + midiPitch].y = yOffset + NOTE_SMALL_SIDE + NOTE_GAP*2;
}
}
backgroundRect.x = allGUINotes[programState.pitchMin].x; //includes offset
backgroundRect.y = (float)(yOffset + NOTE_SMALL_SIDE + NOTE_GAP*2 - NOTE_GAP);
backgroundRect.width = allGUINotes[programState.pitchMax].x - allGUINotes[programState.pitchMin].x;
backgroundRect.height = (float)NOTE_SMALL_SIDE;
}
static void drawBackground() {
if (programState.showPortBackground) {
DrawRectangleRec(backgroundRect, programState.colors[backgroundLight]);
}
}
void draw_meterbridge(Note *allGUINotes, int redrawNeeded) {
Note * nt;
float ft = GetFrameTime();
if (redrawNeeded) {
reposition(allGUINotes);
}
//Z-Order is created by call-order. First is bottom.
drawBackground();
if (programState.showPitchMarker) {
nt = &allGUINotes[programState.pitchMarkerValue];
Rectangle rec = (Rectangle){nt->x, nt->y+NOTE_SMALL_SIDE, NOTE_LONG_SIDE-NOTE_BORDER, NOTE_SMALL_SIDE-NOTE_BORDER}; //TODO: Ha! :) This falls down as well, together with the note.
DrawRectangleRounded(rec, 0.5, 8, programState.colors[backgroundLighter]);
}
//Debug: Show 0 and 127
//DrawLine(allGUINotes[0].x, 0, allGUINotes[0].x, GetScreenHeight(), RAYWHITE); //(int startPosX, int startPosY, int endPosX, int endPosY, Color color);
//DrawLine(allGUINotes[127].x, 0, allGUINotes[127].x, GetScreenHeight(), RAYWHITE); //(int startPosX, int startPosY, int endPosX, int endPosY, Color color);
for (int port=0; port<VIS_PORTS; port++) {
for (int midiPitch=programState.pitchMin; midiPitch<=programState.pitchMax; midiPitch++) {
nt = &allGUINotes[port*128 + midiPitch];
if (nt->active) {
nt->y = backgroundRect.y - 2*(nt->countdown * nt->velocity); //Shift the notes upwards. Don't use -= or +=, that is exponential modifications
reduceCountdown(nt, ft/2); //handles 0.0 and 1.0 as special cases
drawNoteRect(nt, 1, 0); //note, rotated, square. Will not draw if not active/countdown == 0
}
}
}
//Our own GUI section
if (programState.guiVisible) {
EndMode2D(); //We are already in camera mode because this is a draw_ function. GUI is not under camera control.
int row = 0;
screenHeight = GetScreenHeight(); //Changes on resize
bottom = screenHeight - 2*SPACING;
int remember = programState.meterbridge_grouping;
GuiLabel((Rectangle){ 34, bottom-row*SPACING, 105, HEIGHT }, "Meterbridge Note Grouping"); // void GuiLabel(Rectangle bounds, const char *text);
if (GuiSpinner((Rectangle){ 250, bottom-row*SPACING, 80, HEIGHT }, "", &programState.meterbridge_grouping, 0, 127, guiGroupEditMode)) guiGroupEditMode = !guiGroupEditMode; // bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode)
if (programState.meterbridge_grouping > 128) {programState.meterbridge_grouping=128;}
if (programState.meterbridge_grouping < 1) {programState.meterbridge_grouping=1;}
if (remember != programState.meterbridge_grouping) { reposition(allGUINotes); } //we cannot set guiRedrawNeeded to true here because parent draw() will reset it after we return
row++;
BeginMode2D(programState.camera); //Switch back to previously active camera mode
}
}

6
draw_meterbridge.h

@ -0,0 +1,6 @@
#ifndef DRAW_METERBRIDGE_H
#define DRAW_METERBRIDGE_H
void draw_meterbridge(Note *allGUINotes, int redrawNeeded);
#endif // not defined DRAW_METERBRIDGE_H

121
draw_port_grids.c

@ -0,0 +1,121 @@
//Standard lib
#include <stdio.h>
//Third party
#include "raylib.h"
//Our own files
#include "constants.h"
#include "programstate.h"
#include "drawhelper.h"
#include "draw_port_grids.h"
/* Each port is a grid square, ordered in rows, like text
* Pitches are put on a 12x12 square for octaves.
* The screen does not move or scroll. Notes fade out and make room for the next note-on.
*/
extern ProgramState programState;
static Rectangle backgroundRects[VIS_PORTS]; // 4 floats: x, y, w, h
static void reposition(Note *allGUINotes) {
//This is both init and update. We only call it internally, the name of the function doesn't matter.
//Once guaranteed called on program start and then each window resize
int screenWidth = GetScreenWidth();
int screenHeight = GetScreenHeight();
int blockSize = 13 * (NOTE_SMALL_SIDE + NOTE_GAP); //space the blocks one row/column apart = 13
int xOffset = (int)(screenWidth/2) - (VIS_PORTS_SQRT/2)*blockSize; //shift half of the grid blocks from the center to the left
int yOffset = (int)(screenHeight/2) - (VIS_PORTS_SQRT/2)*blockSize; //shift half of the grid blocks from the center to the left
//top left corner of each block
int blockX;
int blockY;
int portRow;
int portColumn;
int pitchRow;
int pitchColumn;
programState.modeDescription = "Each port is one grid.\
\nEach pitch is one square, from top left\nto bottom right.\
\nPitch rows wrap, just like normal text.\n\n\
\n\
The higher the velocity of a note the more\nvibrant its color.\n\
\n\
Notes are only lit up when they are played.\n\
There is no time-dimension.";
for (int port=0; port<VIS_PORTS; port++) {
//We loop in a way that is guaranteed to have >= grid blocks available for all notes
//But we will never try to access more notes than exist in allGUINotes.
portRow = port / VIS_PORTS_SQRT;
portColumn = port % VIS_PORTS_SQRT;
blockY = portRow * blockSize;
blockX = portColumn * blockSize;
backgroundRects[port].x = (float)(xOffset + blockX);
backgroundRects[port].y = (float)(yOffset + blockY);
backgroundRects[port].width = (float)(12 * (NOTE_SMALL_SIDE + NOTE_GAP)); //blocksize without filling the extra gap
backgroundRects[port].height = (float)(12 * (NOTE_SMALL_SIDE + NOTE_GAP));
//Build a 12x12 Grid for octaves.
for (int midiPitch=0; midiPitch<128; midiPitch++) {
pitchRow = midiPitch / 12 +1; //shift one down because it is prettier
pitchColumn = midiPitch % 12;
allGUINotes[port*128 + midiPitch].x = xOffset + blockX + pitchColumn * (NOTE_SMALL_SIDE + NOTE_GAP);
allGUINotes[port*128 + midiPitch].y = yOffset + blockY + pitchRow * (NOTE_SMALL_SIDE + NOTE_GAP);
}
}
}
static void drawBackground() {
//Each port is one track
//The background rects already have their positions and dimensions set.
if (programState.showPortBackground) {
for (int port=0; port<VIS_PORTS; port++) {
if (portShouldDraw(port)) {
DrawRectangleRec(backgroundRects[port], programState.colors[backgroundLight]);
}
}
}
}
void draw_port_grids(Note *allGUINotes, int redrawNeeded) {
Note * nt;
float ft = GetFrameTime();
if (redrawNeeded) {
reposition(allGUINotes);
}
//Z-Order is created by call-order. First is bottom.
drawBackground();
if (programState.showConnectedPortnames) {
for (int port=0; port<VIS_PORTS; port++) {
nt = &allGUINotes[port*128]; // top left note of each port
drawJackPortName(programState.connectedPortNames[port], nt->x, nt->y-1.5*(NOTE_SMALL_SIDE + NOTE_GAP), 18, RAYWHITE, false); //text, x, y, int fontsize, color, vertical
}
}
if (programState.showPitchMarker) {
//Each block has a pitch marker at one note that we draw with a different color.
//We use the already set note positions but create a background note behind the real one.
for (int port=0; port<VIS_PORTS; port++) {
if (portShouldDraw(port)) {
nt = &allGUINotes[port*128 + programState.pitchMarkerValue];
Rectangle rec = (Rectangle){nt->x, nt->y, NOTE_SMALL_SIDE-NOTE_BORDER, NOTE_SMALL_SIDE-NOTE_BORDER};
DrawRectangleRounded(rec, 0.5, 8, programState.colors[backgroundLighter]);
}
}
}
for (int port=0; port<VIS_PORTS; port++) {
for (int midiPitch=0; midiPitch<128; midiPitch++) { //128 is a fixed midi value, no need to dynamically get the size
nt = &allGUINotes[port*128 + midiPitch];
if (nt->active) {
reduceCountdown(nt, ft); //handles 0.0 and 1.0 as special cases
drawNoteRect(nt, 0, 1); //note, rotated, square. Will not draw if not active/countdown == 0
}
}
}
}

6
draw_port_grids.h

@ -0,0 +1,6 @@
#ifndef DRAW_PORT_GRIDS_H
#define DRAW_PORT_GRIDS_H
void draw_port_grids(Note *allGUINotes, int redrawNeeded);
#endif // not defined DRAW_PORT_GRIDS_H

107
draw_xpitches_yports.c

@ -0,0 +1,107 @@
//Standard lib
#include <stdio.h>
//Third party
#include "raylib.h"
//Our own files
#include "constants.h"
#include "programstate.h"
#include "drawhelper.h"
#include "draw_xpitches_yports.h"
/* Each port is a track, which are ordered top to bottom
* Each pitch is a rectangle in that track. midi note 0 on the left to 127 on the right.
* The screen does not move or scroll. Notes fade out and make room for the next note-on.
*/
extern ProgramState programState;
static Rectangle backgroundRects[VIS_PORTS]; // 4 floats: x, y, w, h
static void reposition(Note *allGUINotes) {
//This is both init and update. We only call it internally, the name of the function doesn't matter.
//Once guaranteed called on program start and then each window resize
int screenWidth = GetScreenWidth();
int screenHeight = GetScreenHeight();
int xOffset = (int)(screenWidth/2) - (int)(60*(NOTE_SMALL_SIDE+NOTE_GAP)); //we want all tracks around the center.
int yOffset = (int)(screenHeight/2) - (int)(VIS_PORTS*(NOTE_LONG_SIDE+NOTE_GAP))/2; //we want all tracks around the center.
programState.modeDescription = "Each port is a horizontal track.\n\
Each pitch is one square, going from left lowest\nto right highest.\n\
\n\
The higher the velocity of a note the more\nvibrant its color.\n\
\n\
Notes are only lit up when they are played.\n\
There is no time-dimension.";
for (int port=0; port<VIS_PORTS; port++) {
backgroundRects[port].x = xOffset + programState.pitchMin * (NOTE_SMALL_SIDE + NOTE_GAP/2);
backgroundRects[port].y = (float)(yOffset + port*NOTE_LONG_SIDE + port*NOTE_GAP*2 - NOTE_GAP);
backgroundRects[port].width = (programState.pitchMax-programState.pitchMin+1)*(NOTE_SMALL_SIDE + NOTE_GAP/2);
backgroundRects[port].height = (float)NOTE_LONG_SIDE;
for (int midiPitch=0; midiPitch<128; midiPitch++) { //We prepare the full range of pitches, but the main loop will only activate the current pitch range.
allGUINotes[port*128 + midiPitch].x = xOffset + midiPitch*(NOTE_SMALL_SIDE + NOTE_GAP/2);
allGUINotes[port*128 + midiPitch].y = yOffset + port*NOTE_LONG_SIDE + port*NOTE_GAP*2;
}
}
}
static void drawBackground() {
//Each port is one track
//The background rects already have their positions and dimensions set.
if (programState.showPortBackground) {
for (int port=0; port<VIS_PORTS; port++) {
if (portShouldDraw(port))
{
DrawRectangleRec(backgroundRects[port], programState.colors[backgroundLight]);
}
}
}
}
void draw_xpitches_yports(Note *allGUINotes, int redrawNeeded) {
Note * nt;
float ft = GetFrameTime();
if (redrawNeeded) {
reposition(allGUINotes);
}
//Z-Order is created by call-order. First is bottom.
drawBackground();
if (programState.showConnectedPortnames) {
for (int port=0; port<VIS_PORTS; port++) {
nt = &allGUINotes[port*128+programState.pitchMin]; // choose note where to start writing
drawJackPortName(programState.connectedPortNames[port], nt->x, nt->y, 18, RAYWHITE, false); //text, x, y, int fontsize, color, vertical
}
}
if (programState.showPitchMarker) {
for (int port=0; port<VIS_PORTS; port++) {
if (portShouldDraw(port)) {
nt = &allGUINotes[port*128 + programState.pitchMarkerValue];
Rectangle rec = (Rectangle){nt->x, nt->y, NOTE_SMALL_SIDE-NOTE_BORDER, NOTE_LONG_SIDE-NOTE_BORDER};
DrawRectangleRounded(rec, 0.5, 8, programState.colors[backgroundLighter]);
}
}
}
//Debug: Show 0 and 127
//DrawLine(allGUINotes[0].x, 0, allGUINotes[0].x, GetScreenHeight(), RAYWHITE); //(int startPosX, int startPosY, int endPosX, int endPosY, Color color);
//DrawLine(allGUINotes[127].x, 0, allGUINotes[127].x, GetScreenHeight(), RAYWHITE); //(int startPosX, int startPosY, int endPosX, int endPosY, Color color);
for (int port=0; port<VIS_PORTS; port++) {
for (int midiPitch=programState.pitchMin; midiPitch<=programState.pitchMax; midiPitch++) {
nt = &allGUINotes[port*128 + midiPitch];
if (nt->active) {
reduceCountdown(nt, ft); //handles 0.0 and 1.0 as special cases
drawNoteRect(nt, 0, 0); //note, rotated, square. Will not draw if countdown == 0
}
}
}
}

6
draw_xpitches_yports.h

@ -0,0 +1,6 @@
#ifndef DRAW_XPITCHES_YPORTS_H
#define DRAW_XPITCHES_YPORTS_H
void draw_xpitches_yports(Note *allGUINotes, int redrawNeeded);
#endif // not defined DRAW_XPITCHES_YPORTS_H

103
draw_xports_ypitches.c

@ -0,0 +1,103 @@
//Standard lib
//Third party
#include "raylib.h"
//Our own files
#include "constants.h"
#include "programstate.h"
#include "drawhelper.h"
#include "draw_xports_ypitches.h"
/* Each port is a column, ordered from left to right
* Each pitch is a rectangle in that column. midi note 0 on the bottom to 127 on top
* The screen does not move or scroll. Notes fade out and make room for the next note-on.
*/
extern ProgramState programState;
static Rectangle backgroundRects[VIS_PORTS]; // 4 floats: x, y, w, h
static void reposition(Note *allGUINotes) {
//This is both init and update. We only call it internally, the name of the function doesn't matter.
//Once guaranteed called on program start and then each window resize
int screenWidth = GetScreenWidth();
int screenHeight = GetScreenHeight();
int xOffset = (int)(screenWidth/2) - (int)((VIS_PORTS*NOTE_LONG_SIDE)/2);
//int yOffset = -1 * ((int)(screenWidth/2) - ((programState.pitchMax - programState.pitchMin +1 ) * NOTE_SMALL_SIDE))/2;
int yOffset = -1 * ((int)(screenHeight/2) - (60 * NOTE_SMALL_SIDE)/2); //we want all tracks around the center.
programState.modeDescription = "Each port is a vertical column.\n\
Each pitch is one square, going from top highest\nto bottomw lowest.\n\
\n\
The higher the velocity of a note the more\nvibrant its color.\n\
\n\
Notes are only lit up when they are played.\n\
There is no time-dimension.";
for (int port=0; port<VIS_PORTS; port++) {
backgroundRects[port].x = xOffset + port*(NOTE_LONG_SIDE + NOTE_GAP) - NOTE_GAP/2;
backgroundRects[port].y = yOffset + (127-programState.pitchMax) * NOTE_SMALL_SIDE;
backgroundRects[port].width = (float)NOTE_LONG_SIDE;
backgroundRects[port].height = (programState.pitchMax - programState.pitchMin +1 ) * NOTE_SMALL_SIDE;
for (int midiPitch=0; midiPitch<128; midiPitch++) { //We prepare the full range of pitches, but the main loop will only activate the current pitch range.
allGUINotes[port*128 + midiPitch].x = xOffset + port*(NOTE_LONG_SIDE + NOTE_GAP);
allGUINotes[port*128 + midiPitch].y = yOffset + (127-midiPitch) * NOTE_SMALL_SIDE ;
}
}
}
static void drawBackground() {
//Each port is one track
//The background rects already have their positions and dimensions set.
if (programState.showPortBackground) {
for (int port=0; port<VIS_PORTS; port++) {
if (portShouldDraw(port)) {
DrawRectangleRec(backgroundRects[port], programState.colors[backgroundLight]);
}
}
}
}
void draw_xports_ypitches(Note *allGUINotes, int redrawNeeded) {
Note * nt;
float ft = GetFrameTime();
if (redrawNeeded) {
reposition(allGUINotes);
}
//Z-Order is created by call-order. First is bottom.
drawBackground();
if (programState.showConnectedPortnames) {
for (int port=0; port<VIS_PORTS; port++) {
nt = &allGUINotes[port*128+programState.pitchMin]; // bottom left note of each port
drawJackPortName(programState.connectedPortNames[port], nt->x, nt->y+20, 18, RAYWHITE, true); //text, x, y, int fontsize, color, vertical
}
}
if (programState.showPitchMarker) {
for (int port=0; port<VIS_PORTS; port++) {
if (portShouldDraw(port)) {
nt = &allGUINotes[port*128 + programState.pitchMarkerValue];
Rectangle rec = (Rectangle){nt->x, nt->y, NOTE_LONG_SIDE-NOTE_BORDER, NOTE_SMALL_SIDE-NOTE_BORDER};
DrawRectangleRounded(rec, 0.5, 8, programState.colors[backgroundLighter]);
}
}
}
for (int port=0; port<VIS_PORTS; port++) {
for (int midiPitch=programState.pitchMin; midiPitch<=programState.pitchMax; midiPitch++) {
nt = &allGUINotes[port*128 + midiPitch];
if (nt->active) {
reduceCountdown(nt, ft); //handles 0.0 and 1.0 as special cases
drawNoteRect(nt, 1, 0); //note, rotated, square. Will not draw if countdown == 0
}
}
}
}

6
draw_xports_ypitches.h

@ -0,0 +1,6 @@
#ifndef DRAW_XPORTS_YPITCHES_H
#define DRAW_XPORTS_YPITCHES_H
void draw_xports_ypitches(Note *allGUINotes, int redrawNeeded);
#endif // not defined DRAW_XPORTS_YPITCHES_H

181
drawhelper.c

@ -0,0 +1,181 @@
//Standard lib
#include <string.h> //strchr
#include <stdio.h> //print error messages
//Third party
#include "raylib.h"
//Our own files
#include "constants.h"
#include "programstate.h"
#include "drawhelper.h"
extern ProgramState programState;
float intToHue(int number, int maxnumber) {
//previously used to auto-color ports this is now only used for the default port colors.
//portnum is 0-VIS_PORTS
//hue is 0-360
//return (input-inputLowest) / (inputHighest-inputLowest) * (outputHighest-outputLowest) + outputLowest
return (float)number / (float)maxnumber * 360.0f;
}
Color intToColor(int number, int maxnumber) {
Color result;
Vector3 hsv; // 3 floats: hue/color, saturation, value/brightness
hsv = (Vector3){intToHue(number, VIS_PORTS), 1.0f, (float)(127+40)/(float)167}; //Saturation is always 1, we tweak Value to never go into the black range.
result = ColorFromHSV(hsv);
return result;
}
Color midiToColor(Note * note) {
Color result;
Vector3 hsv; // 3 floats: hue/color, saturation, value/brightness
//hsv = (Vector3){intToHue(note->port, VIS_PORTS), 1.0f, (float)(note->velocity+40)/(float)167}; //Saturation is always 1, we tweak Value to never go into the black range.
float hue=ColorToHSV(programState.colors[note->port]).x;
hsv = (Vector3){hue, 1.0f, (float)(note->velocity+40)/(float)167}; //Saturation is always 1, we tweak Value to never go into the black range.
result = ColorFromHSV(hsv);
return result;
}
void drawNoteRect(Note * note, int rotated, int square) {
if (note->active) {
//opacity is between 0.0f and 1.0f
int w,h;
if (square==1) {
w = NOTE_SMALL_SIDE - NOTE_BORDER;
h = NOTE_SMALL_SIDE - NOTE_BORDER;
}
else { //Not Square
if (rotated==0) {
w = NOTE_SMALL_SIDE - NOTE_BORDER;
h = NOTE_LONG_SIDE - NOTE_BORDER;
}
else {
h = NOTE_SMALL_SIDE - NOTE_BORDER;
w = NOTE_LONG_SIDE - NOTE_BORDER;
}
}
const Rectangle rec = (Rectangle){note->x, note->y, w,h};
const Rectangle outer = (Rectangle){note->x-NOTE_BORDER/2, note->y-NOTE_BORDER/2, w+NOTE_BORDER, h+NOTE_BORDER};
Color bordercolor = Fade(BLACK, note->countdown);
Color color = Fade(programState.colors[note->port], note->countdown);
//Drawing order is reverse Z-Order
DrawRectangleRounded(outer, 0.5, 8, bordercolor); //float roundness, int segments/smoothness, Color
DrawRectangleRounded(rec, 0.5, 8, color);
}
}
bool portShouldDraw(int port) {
//Check various conditions if a port background, port name and pitch marker should be drawn
if ( programState.showPortBackgroundForUnconnected || ( programState.connectedPortNames[port] && programState.connectedPortNames[port][0] != '\0' )) {
return true;
}
else {
return false;
}
}
void drawJackPortName(const char * portname, int x, int y, int fontsize, Color color, bool vertical) {
if (portname && portname[0] != '\0') {
const char * finalPortName;
if (programState.includeClientInPortNameDisplay) {
finalPortName = portname; //TODO: Pretty Names don't contain the client name. Add it ourselves?
}
else {
if (strchr(portname, ':') == NULL ) //Pretty Names don't contain the client name.
{
finalPortName = portname;
}
else {
finalPortName = strchr(portname, ':')+1;
}
} //final port name is ready.
//Vertical Mode
//void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float scale, Color tint); // Draw one character (codepoint)
if (vertical) {
//TODO: Wow, that is ugly!
int counter = 0;
char ch = finalPortName[counter];
Font defaultFont = GetFontDefault();
while (ch != '\0') {
DrawTextCodepoint(defaultFont, ch, (Vector2){ x, y+counter*(fontsize/1.5) }, 1.8f, color);
counter++;
ch = finalPortName[counter];
}
}
else {
// Horizontal Mode
DrawText(finalPortName, x, y, fontsize, color);
}
}
}
float calculateCountdown(float countdown, float ft) {
float bpmFactor;
switch (programState.fadeOutMode) {
case 0: // framelegnth * user factor
if (countdown < 1.0f) { countdown -= (float)programState.fadeOutFactor * ft; } //note off has been triggered. Fade out.
if (countdown <= 0.0f) { countdown = 0; } // Is it now, or was already, below 0?
break;
case 1: // bpm
bpmFactor = (float)(programState.bpm/40);
if (countdown < 1.0f) { countdown -= bpmFactor * ft; } //note off has been triggered. Fade out.
if (countdown <= 0.0f) { countdown = 0.0f; } // Is it now, or was already, below 0?
break;
case 2: // no fadeout
if (countdown < 1.0f) { countdown = 0.0f; } // As soon as note off arrives
break;
default: //just in case
countdown = 0.0f;
break;
}
return countdown;
}
void reduceCountdown(Note * nt, float ft) {
//Notes countdown is set to 0 (note-off), 1 (note-on) or 0.999 (note-off) by our jack midi client.
float bpmFactor;
if (nt->active == 1) {
switch (programState.fadeOutMode) {
case 0: // framelegnth * user factor
if (nt->countdown < 1.0f) { nt->countdown -= (float)programState.fadeOutFactor * ft; } //note off has been triggered. Fade out.
if (nt->countdown <= 0.0f) { nt->active = false; } // Is it now, or was already, below 0?
break;
case 1: // bpm
bpmFactor = (float)(programState.bpm/40);
if (nt->countdown < 1.0f) { nt->countdown -= bpmFactor * ft; } //note off has been triggered. Fade out.
if (nt->countdown <= 0.0f) { nt->active = false; } // Is it now, or was already, below 0?
break;
case 2: // no fadeout
if (nt->countdown < 1.0f) { nt->active = false; } // As soon as note off arrives
break;
default: //just in case
nt->active = false;
break;
}
}
}

14
drawhelper.h

@ -0,0 +1,14 @@
#ifndef DRAWHELPER_H
#define DRAWHELPER_H
#include "constants.h"
Color midiToColor(Note * note);
Color intToColor(int number, int maxnumber);
void drawNoteRect(Note * note, int rotated, int square);
void reduceCountdown(Note * nt, float ft);
float calculateCountdown(float countdown, float ft);
void drawJackPortName(const char * portname, int x, int y, int fontsize, Color color, bool vertical);
bool portShouldDraw(int port);
#endif // not defined DRAWHELPER_H

100
effect_starfield.c

@ -0,0 +1,100 @@
//Standard lib
#include <stdio.h>
//Third party
#include "raylib.h"
//Our own files
#include "constants.h"
#include "programstate.h"
#include "effect_starfield.h"
extern ProgramState programState;
static int screenWidth;
static int screenHeight;
static const int numberOfStars=540;
static const int starLayers = 3;
typedef struct {
int x;
int y;
int z;
Color color;
float rand;
} Star;
static Star stars[540]; //1080 * 0.5 Not quite one star per line
void reposition() {
//Recreate all stars and begin from blank.
//We want a certain star density, no matter the screen resolution
screenWidth = GetScreenWidth();
screenHeight = GetScreenHeight();
Color color;
int rand;
int layer;
int partitionSize=(starLayers+1)*2 / 8; //8 partions
for (int i=0; i<numberOfStars; i++) {
rand = GetRandomValue(1, (starLayers+1)*2);
if (rand<= 5*partitionSize) layer = 1;
else if (rand<= 7*partitionSize) layer = 2;
else if (rand<= 8*partitionSize) layer = 3;
switch (layer) {
//0 does not exist
case 1:
color = BROWN;
break;
case 2:
color = BEIGE;
break;
case 3:
color = PURPLE;
break;
default:
color = DARKBLUE;
}
float randomFactor = ((float)GetRandomValue(0, 20)-10.0f) / 100.0f;
stars[i] = (Star){ GetRandomValue(0, screenWidth), GetRandomValue(0, screenHeight), layer, color, randomFactor };
}
}
void effect_starfield(bool redrawNeeded, float opacity, int speedModifier, int size) {
float ft = GetFrameTime();
int bpmFactor = programState.bpm/40 * programState.bpm/20; //exponential curve. The higher bpm the faster we get. Below 90 it should feel really slow
Star * star;
if (redrawNeeded) {
reposition();
}
for (int i=0; i<numberOfStars; i++) {
star = &stars[i];
if (programState.transportRolling) {
//Move Forward
star->x -= (star->z * ft * bpmFactor * 3 * speedModifier); //... the mysterious number 3.
//wrap around
if (star->x <= 0) {
star->x += screenWidth;
star->y = GetRandomValue(0, screenHeight);
}
else if (star->x > screenWidth) {
star->x -= screenWidth;
star->y = GetRandomValue(0, screenHeight);
}
}
//Wobble wobble
int rand = GetRandomValue(1, 200);
if (rand == 1) {star->y += 1;}
else if (rand == 2) {star->y -= 1;}
DrawRectangle(star->x, star->y, size, size, Fade(star->color, opacity));
}
}

6
effect_starfield.h

@ -0,0 +1,6 @@
#ifndef effect_starfield_H
#define effect_starfield_H
void effect_starfield(bool redrawNeeded, float opacity, int speedModifier, int size);
#endif // not defined effect_starfield_H

BIN
font.ttf

Binary file not shown.

40
glsl330/bloom.fs

@ -0,0 +1,40 @@
#version 330
// Input vertex attributes (from vertex shader)
in vec2 fragTexCoord;
in vec4 fragColor;
// Input uniform values
uniform sampler2D texture0;
uniform vec4 colDiffuse;
// Output fragment color
out vec4 finalColor;
// NOTE: Add here your custom variables
const vec2 size = vec2(1920, 1080); // render size
const float samples = 5.0; // pixels per axis; higher = bigger glow, worse performance
const float quality = 2.5; // lower = smaller glow, better quality
void main()
{
vec4 sum = vec4(0);
vec2 sizeFactor = vec2(1)/size*quality;
// Texel color fetching from texture sampler
vec4 source = texture(texture0, fragTexCoord);
const int range = 2; // should be = (samples - 1)/2;
for (int x = -range; x <= range; x++)
{
for (int y = -range; y <= range; y++)
{