Raylib 3
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
5.6 KiB

//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.\
\nVelocity 'shoots' notes upwards, then they fall back\
\ninfluenced by the fade-out setting.\
\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.\
\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) {
//Z-Order is created by call-order. First is bottom.
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
BeginMode2D(programState.camera); //Switch back to previously active camera mode