From 711e9931bb2fa9ff10153a635f638317296dd197 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Sun, 30 Jul 2023 17:36:22 +0200 Subject: [PATCH 1/9] System: ensure external libraries don't reset the locale. This fixes config loading on Ubuntu, where the file picker (or Wayland, or Nautilus?) would change the locale (#133). --- .gitignore | 1 + src/helpers/Recorder.cpp | 3 +++ src/helpers/System.cpp | 5 +++++ src/helpers/System.h | 2 ++ src/main.cpp | 4 +++- src/rendering/Renderer.cpp | 8 ++++++++ 6 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f50caa8..322b97b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ x64/ *.user *.xcodeproj *.xcuserstate +*.vscode */xcuserdata/* dependencies/ libs/ffmpeg diff --git a/src/helpers/Recorder.cpp b/src/helpers/Recorder.cpp index 317e23c..8857d65 100755 --- a/src/helpers/Recorder.cpp +++ b/src/helpers/Recorder.cpp @@ -1,4 +1,5 @@ #include "Recorder.h" +#include "System.h" #include "../rendering/State.h" #include @@ -305,6 +306,7 @@ bool Recorder::drawGUI(float scale){ if(_config.format == Export::Format::PNG){ nfdresult_t result = NFD_PickFolder(NULL, &outPath); + System::forceLocale(); if(result == NFD_OKAY) { _config.path = std::string(outPath); shouldStart = true; @@ -313,6 +315,7 @@ bool Recorder::drawGUI(float scale){ } else { const std::string & ext = _formats.at(int(_config.format)).ext; nfdresult_t result = NFD_SaveDialog(ext.c_str(), NULL, &outPath); + System::forceLocale(); if(result == NFD_OKAY) { _config.path = std::string(outPath); const std::string fullExt = "." + ext; diff --git a/src/helpers/System.cpp b/src/helpers/System.cpp index 97364f5..9585f58 100644 --- a/src/helpers/System.cpp +++ b/src/helpers/System.cpp @@ -203,3 +203,8 @@ void System::writeStringToFile(const std::string& path, const std::string& conte file.close(); } } + +void System::forceLocale(){ + // Helps managing configuration files loading/saving. + std::setlocale(LC_ALL, "C"); +} \ No newline at end of file diff --git a/src/helpers/System.h b/src/helpers/System.h index 1c84d46..67c296f 100644 --- a/src/helpers/System.h +++ b/src/helpers/System.h @@ -39,5 +39,7 @@ class System { static bool createDirectory(const std::string & directory); static std::string getApplicationDataDirectory(); + + static void forceLocale(); }; diff --git a/src/main.cpp b/src/main.cpp index d516d52..02d10a7 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -140,7 +140,9 @@ int main( int argc, char** argv) { std::cerr << "[ERROR]: could not start GLFW3" << std::endl; return 2; } - + // Ensure we are using the C locale. + System::forceLocale(); + // Retrieve the settings directory for all applications. std::string applicationDataPath = System::getApplicationDataDirectory(); // If this is not empty (ie the working directory), be a good citizen diff --git a/src/rendering/Renderer.cpp b/src/rendering/Renderer.cpp index 8562fe9..5ef380b 100755 --- a/src/rendering/Renderer.cpp +++ b/src/rendering/Renderer.cpp @@ -406,6 +406,7 @@ SystemAction Renderer::drawGUI(const float currentTime) { // Read arguments. nfdchar_t *outPath = NULL; nfdresult_t result = NFD_OpenDialog(NULL, NULL, &outPath); + System::forceLocale(); if (result == NFD_OKAY) { loadFile(std::string(outPath)); } @@ -433,6 +434,7 @@ SystemAction Renderer::drawGUI(const float currentTime) { if(ImGui::Button("Export MIDI file...")) { nfdchar_t *savePath = NULL; nfdresult_t result = NFD_SaveDialog("mid", NULL, &savePath); + System::forceLocale(); if (result == NFD_OKAY && savePath != nullptr) { std::ofstream outFile = System::openOutputFile(std::string(savePath), true); _scene->save(outFile); @@ -759,6 +761,7 @@ void Renderer::showParticleOptions(){ // Read arguments. nfdpathset_t outPaths; nfdresult_t result = NFD_OpenDialogMultiple("png;jpg,jpeg;", NULL, &outPaths); + System::forceLocale(); if (result == NFD_OKAY) { std::vector paths; @@ -948,6 +951,7 @@ void Renderer::showBackgroundOptions(){ // Read arguments. nfdchar_t *outPath = NULL; nfdresult_t result = NFD_OpenDialog("jpg,jpeg;png", NULL, &outPath); + System::forceLocale(); if (result == NFD_OKAY) { _state.background.imagePath = std::string(outPath); glDeleteTextures(1, &_state.background.tex); @@ -985,6 +989,7 @@ void Renderer::showBottomButtons(){ // Read arguments. nfdchar_t *savePath = NULL; nfdresult_t result = NFD_SaveDialog("ini", NULL, &savePath); + System::forceLocale(); if (result == NFD_OKAY) { _state.save(std::string(savePath)); } @@ -995,6 +1000,7 @@ void Renderer::showBottomButtons(){ // Read arguments. nfdchar_t *outPath = NULL; nfdresult_t result = NFD_OpenDialog("ini", NULL, &outPath); + System::forceLocale(); if (result == NFD_OKAY) { if(_state.load(std::string(outPath))){ setState(_state); @@ -1179,6 +1185,7 @@ void Renderer::showSetEditor(){ if(ImGui::Button("Save control points...")){ nfdchar_t *savePath = NULL; nfdresult_t result = NFD_SaveDialog("csv", NULL, &savePath); + System::forceLocale(); if (result == NFD_OKAY) { const std::string content = _state.setOptions.toKeysString("\n"); System::writeStringToFile(std::string(savePath), content); @@ -1189,6 +1196,7 @@ void Renderer::showSetEditor(){ // Read arguments. nfdchar_t *outPath = NULL; nfdresult_t result = NFD_OpenDialog("csv", NULL, &outPath); + System::forceLocale(); if (result == NFD_OKAY) { const std::string str = System::loadStringFromFile(std::string(outPath)); _state.setOptions.fromKeysString(str); From 9a39045f9a4e41f12257d8ee00d9a230c967e045 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Mon, 31 Jul 2023 22:31:05 +0200 Subject: [PATCH 2/9] macOS: fix build. --- src/helpers/System.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/helpers/System.cpp b/src/helpers/System.cpp index 9585f58..be3ea6b 100644 --- a/src/helpers/System.cpp +++ b/src/helpers/System.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -207,4 +208,4 @@ void System::writeStringToFile(const std::string& path, const std::string& conte void System::forceLocale(){ // Helps managing configuration files loading/saving. std::setlocale(LC_ALL, "C"); -} \ No newline at end of file +} From d6d0cf2fe12187c413ffe56f422358503074990b Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Mon, 31 Jul 2023 23:42:15 +0200 Subject: [PATCH 3/9] Keys: improve rendering, add edges to minor keys (#130). --- .gitignore | 1 + resources/shaders/keys.frag | 84 ----------------- resources/shaders/majorKeys.frag | 42 +++++++++ .../shaders/{keys.vert => majorKeys.vert} | 6 +- resources/shaders/minorKeys.frag | 58 ++++++++++++ resources/shaders/minorKeys.vert | 78 ++++++++++++++++ src/packager.cpp | 2 +- src/rendering/Renderer.cpp | 17 +++- src/rendering/State.cpp | 7 +- src/rendering/State.h | 4 +- src/rendering/scene/MIDIScene.cpp | 93 +++++++++++++------ src/rendering/scene/MIDIScene.h | 5 +- 12 files changed, 277 insertions(+), 120 deletions(-) delete mode 100644 resources/shaders/keys.frag create mode 100644 resources/shaders/majorKeys.frag rename resources/shaders/{keys.vert => majorKeys.vert} (86%) create mode 100644 resources/shaders/minorKeys.frag create mode 100644 resources/shaders/minorKeys.vert diff --git a/.gitignore b/.gitignore index 322b97b..20a856a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build/ +build*/ .DS_Store .thumb ext/ diff --git a/resources/shaders/keys.frag b/resources/shaders/keys.frag deleted file mode 100644 index 7d2b927..0000000 --- a/resources/shaders/keys.frag +++ /dev/null @@ -1,84 +0,0 @@ -#version 330 - -in INTERFACE { - vec2 uv; -} In ; - -#define SETS_COUNT 8 -#define MAJOR_COUNT 75 - -uniform vec2 inverseScreenSize; -uniform float minorsWidth = 1.0; -uniform vec3 keysColor = vec3(0.0); -uniform vec3 minorColor[SETS_COUNT]; -uniform vec3 majorColor[SETS_COUNT]; -uniform bool highlightKeys; -uniform bool horizontalMode = false; -uniform int actives[128]; - -uniform int minNoteMajor; -uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1) - -const bool isMinor[MAJOR_COUNT] = bool[](true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, false); - -const int majorIds[MAJOR_COUNT] = int[](0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24, 26, 28, 29, 31, 33, 35, 36, 38, 40, 41, 43, 45, 47, 48, 50, 52, 53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83, 84, 86, 88, 89, 91, 93, 95, 96, 98, 100, 101, 103, 105, 107, 108, 110, 112, 113, 115, 117, 119, 120, 122, 124, 125, 127); -const int minorIds[MAJOR_COUNT] = int[](1, 3, 0, 6, 8, 10, 0, 13, 15, 0, 18, 20, 22, 0, 25, 27, 0, 30, 32, 34, 0, 37, 39, 0, 42, 44, 46, 0, 49, 51, 0, 54, 56, 58, 0, 61, 63, 0, 66, 68, 70, 0, 73, 75, 0, 78, 80, 82, 0, 85, 87, 0, 90, 92, 94, 0, 97, 99, 0, 102, 104, 106, 0, 109, 111, 0, 114, 116, 118, 0, 121, 123, 0, 126, 0); - -vec2 minorShift(int id){ - if(id == 1 || id == 6){ - return vec2(0.0, 0.2); - } - if(id == 3 || id == 10){ - return vec2(0.2, 0.0); - } - return vec2(0.1,0.1); -} - -out vec4 fragColor; - - -void main(){ - // White keys: white - // Black keys: keyColor - // Lines between keys: keyColor - // Active key: activeColor - - // White keys, and separators. - float widthScaling = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x; - float intensity = int(abs(fract(In.uv.x * notesCount)) >= 2.0 * notesCount * widthScaling); - - // If the current major key is active, the majorColor is specific. - int majorId = majorIds[clamp(int(In.uv.x * notesCount) + minNoteMajor, 0, 74)]; - int cidMajor = actives[majorId]; - vec3 backColor = (highlightKeys && cidMajor >= 0) ? majorColor[cidMajor] : vec3(1.0); - - vec3 frontColor = keysColor; - // Upper keyboard. - if(In.uv.y > 0.4){ - int minorLocalId = min(int(floor(In.uv.x * notesCount + 0.5) + minNoteMajor) - 1, 74); - // Handle black keys. - // Hide keys that are on the edges. - if(minorLocalId >= 0 && isMinor[minorLocalId] && In.uv.x > 0.5/notesCount && In.uv.x < 1.0 - 0.5/notesCount){ - int minorId = minorIds[minorLocalId]; - // Get the shift for non-centered minor keys. - vec2 shifts = minorsWidth * minorShift(minorId % 12); - // Compensate total width. - float marginSize = minorsWidth * 1.2; - // Rescale UV to take shift into account. - float localUv = fract(In.uv.x * notesCount + 0.5); - localUv = abs( (localUv - shifts.x) / (1.0 - shifts.x - shifts.y) * 2.0 - 1.0); - // Detect edges. - intensity = step(marginSize, localUv); - //float roundEdge = (1.0 - exp(50.0 * (-In.uv.y + 0.4)))*1.1; - //intensity += smoothstep(roundEdge - 0.1, roundEdge + 0.1, localUv); - //intensity = clamp(intensity, 0.0, 1.0); - int cidMinor = actives[minorId]; - if(highlightKeys && cidMinor >= 0){ - frontColor = minorColor[cidMinor]; - } - } - } - - fragColor.rgb = mix(frontColor, backColor, intensity); - fragColor.a = 1.0; -} diff --git a/resources/shaders/majorKeys.frag b/resources/shaders/majorKeys.frag new file mode 100644 index 0000000..c617eaf --- /dev/null +++ b/resources/shaders/majorKeys.frag @@ -0,0 +1,42 @@ +#version 330 + +in INTERFACE { + vec2 uv; +} In ; + +#define SETS_COUNT 8 +#define MAJOR_COUNT 75 + +uniform bool highlightKeys; +uniform bool horizontalMode = false; +uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1) +uniform int actives[128]; +uniform int minNoteMajor; +uniform vec2 inverseScreenSize; +uniform vec3 edgeColor = vec3(0.0); + +uniform vec3 majorColor[SETS_COUNT]; + +const int majorIds[MAJOR_COUNT] = int[](0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24, 26, 28, 29, 31, 33, 35, 36, 38, 40, 41, 43, 45, 47, 48, 50, 52, 53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83, 84, 86, 88, 89, 91, 93, 95, 96, 98, 100, 101, 103, 105, 107, 108, 110, 112, 113, 115, 117, 119, 120, 122, 124, 125, 127); + +out vec4 fragColor; + + +void main(){ + + // White keys, and separators. + float widthScaling = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x; + float majorUvX = fract(In.uv.x * notesCount); + + // Edges on the sides + float centerIntensity = 1.0 - step( 1.0 - 2.0 * notesCount * widthScaling, abs(majorUvX * 2.0 - 1.0)); + + // If the current major key is active, the majorColor is specific. + int majorId = majorIds[clamp(int(In.uv.x * notesCount) + minNoteMajor, 0, 74)]; + int cidMajor = actives[majorId]; + vec3 frontColor = (highlightKeys && cidMajor >= 0) ? majorColor[cidMajor] : vec3(1.0); + + // Mix. + fragColor.rgb = mix(edgeColor, frontColor, centerIntensity); + fragColor.a = 1.0; +} diff --git a/resources/shaders/keys.vert b/resources/shaders/majorKeys.vert similarity index 86% rename from resources/shaders/keys.vert rename to resources/shaders/majorKeys.vert index a380cee..3d93697 100644 --- a/resources/shaders/keys.vert +++ b/resources/shaders/majorKeys.vert @@ -6,8 +6,8 @@ out INTERFACE { vec2 uv; } Out ; -uniform float keyboardHeight = 0.25; uniform bool horizontalMode = false; +uniform float keyboardHeight = 0.25; vec2 flipIfNeeded(vec2 inPos){ return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos; @@ -16,10 +16,10 @@ vec2 flipIfNeeded(vec2 inPos){ void main(){ // Input are in -0.5,0.5 // We directly output the position. - // [-0.5, 0.5] to [-1, 2.0*keyboardHeight-1.0] + // [-0.5, 0.5] to [-1, -1+2.0*keyboardHeight] float yShift = keyboardHeight * (2.0 * v.y + 1.0) - 1.0; - vec2 pos2D = vec2(v.x*2.0, yShift); + vec2 pos2D = vec2(v.x * 2.0, yShift); gl_Position.xy = flipIfNeeded(pos2D); gl_Position.zw = vec2(0.0, 1.0); diff --git a/resources/shaders/minorKeys.frag b/resources/shaders/minorKeys.frag new file mode 100644 index 0000000..1e8cdca --- /dev/null +++ b/resources/shaders/minorKeys.frag @@ -0,0 +1,58 @@ +#version 330 + +in INTERFACE { + vec2 uv; + float id; +} In ; + +#define SETS_COUNT 8 + +uniform bool highlightKeys; +uniform bool horizontalMode = false; +uniform float keyboardHeight = 0.25; +uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1) +uniform int minNoteMajor; +uniform vec2 inverseScreenSize; +uniform vec3 edgeColor = vec3(0.0); + +uniform vec3 minorColor[SETS_COUNT]; +uniform bool edgeOnMinors; +uniform float minorsHeight = 0.6; +uniform float minorsWidth = 1.0; + +vec2 minorShift(int id){ + if(id == 1 || id == 6){ + return vec2(0.0, 0.3); + } + if(id == 3 || id == 10){ + return vec2(0.3, 0.0); + } + return vec2(0.1,0.1); +} + +out vec4 fragColor; + + +void main(){ + + // Center uvs + vec2 ndc = 2.0 * In.uv - 1.0; + + float widthScaling = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x; + float heightScaling = horizontalMode ? inverseScreenSize.x : inverseScreenSize.y; + float uvWidth = minorsWidth / float(notesCount) * 0.5; + float uvHeight = minorsHeight * keyboardHeight * 0.5; + + // Edges on the sides and bottom. + float xStep = step(1.0 - 2.0 * widthScaling / uvWidth, abs(ndc.x)); + float yStep = step(1.0 - 2.0 * heightScaling / uvHeight, -ndc.y); + float centerIntensity = edgeOnMinors ? ((1.0 - xStep) * (1.0 - yStep)) : 1.0; + + // Key color changes when active. + int cidMinor = int(In.id); + vec3 frontColor = (highlightKeys && cidMinor >= 0) ? minorColor[cidMinor] : edgeColor; + + // Mix + fragColor.rgb = mix(edgeColor, frontColor, centerIntensity); + fragColor.a = 1.0; +} diff --git a/resources/shaders/minorKeys.vert b/resources/shaders/minorKeys.vert new file mode 100644 index 0000000..a9eed0e --- /dev/null +++ b/resources/shaders/minorKeys.vert @@ -0,0 +1,78 @@ +#version 330 + +layout(location = 0) in vec2 v; + +out INTERFACE { + vec2 uv; + float id; +} Out ; + +uniform bool horizontalMode = false; +uniform float keyboardHeight = 0.25; +uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1) +uniform int actives[128]; +uniform int minNoteMajor; + +uniform float minorsHeight = 0.6; +uniform float minorsWidth = 1.0; + +vec2 flipIfNeeded(vec2 inPos){ + return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos; +} + +#define MINOR_COUNT 53 + +const int minorIds[MINOR_COUNT] = int[](1, 3, 6, 8, 10, 13, 15, 18, 20, 22, 25, 27, 30, 32, 34, 37, 39, 42, 44, 46, 49, 51, 54, 56, 58, 61, 63, 66, 68, 70, 73, 75, 78, 80, 82, 85, 87, 90, 92, 94, 97, 99, 102, 104, 106, 109, 111, 114, 116, 118, 121, 123, 126); + +const int noteDelta[12] = int[](0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6); + + +float minorShift(int id){ + if(id == 1 || id == 6){ + return -0.1; + } + if(id == 3 || id == 10){ + return 0.1; + } + return 0.0; +} + +void main(){ + + float noteWidth = 2.0 / notesCount; + // Size of the note : width, height based on duration and current speed. + vec2 noteSize = vec2(0.9 * noteWidth * minorsWidth, 2.0 * minorsHeight * keyboardHeight); + + // Compute note shift. + // Horizontal shift based on note id, width of keyboard, and if the note is minor or not. + //float a = (1.0/(notesCount-1.0)) * (2.0 - 2.0/notesCount); + //float b = -1.0 + 1.0/notesCount; + // This should be in -1.0, 1.0. + // input: noteId is in [0 MAJOR_COUNT] + // we want minNote to -1+1/c, maxNote to 1-1/c + float a = 2.0; + float b = -notesCount + 1.0 - 2.0 * float(minNoteMajor); + + int minorId = minorIds[gl_InstanceID]; + int noteId = (minorId/12) * 7 + noteDelta[minorId % 12]; + float horizLoc = (float(noteId) * a + b + 1.0) / notesCount; + + float vertLoc = 2.0 * (1.0 - minorsHeight) * keyboardHeight - 1.0 + noteSize.y * 0.5; + vec2 noteShift = vec2(horizLoc, vertLoc); + + noteShift.x += minorShift(minorId % 12) * noteSize.x; + + // Output position. + gl_Position = vec4(flipIfNeeded(noteSize * v + noteShift), 0.0 , 1.0) ; + + // Discard keys that are too close to the screen edges. + if(abs(noteShift.x) >= 1.0 - 0.5 * noteWidth){ + gl_Position = vec4(-40000.0); + } + + // Output the UV coordinates computed from the positions. + Out.uv = v.xy + 0.5; + // And the active channel if present. + Out.id = float(actives[minorId]); + +} diff --git a/src/packager.cpp b/src/packager.cpp index bb184c5..ce676b1 100755 --- a/src/packager.cpp +++ b/src/packager.cpp @@ -38,7 +38,7 @@ int main( int argc, char** argv) { const std::string outputDir = baseDir + "/src/resources/"; std::vector imagesToLoad = { "flash", "font", "particles"}; - std::vector shadersToLoad = { "background", "flashes", "notes", "particles", "particlesblur", "screenquad", "keys", "backgroundtexture", "pedal", "wave", "fxaa"}; + std::vector shadersToLoad = { "background", "flashes", "notes", "particles", "particlesblur", "screenquad", "majorKeys", "minorKeys", "backgroundtexture", "pedal", "wave", "fxaa"}; // Header file. std::ofstream headerFile(outputDir + "data.h"); diff --git a/src/rendering/Renderer.cpp b/src/rendering/Renderer.cpp index 5ef380b..172c042 100755 --- a/src/rendering/Renderer.cpp +++ b/src/rendering/Renderer.cpp @@ -812,7 +812,7 @@ void Renderer::showParticleOptions(){ void Renderer::showKeyboardOptions(){ ImGuiPushItemWidth(25); - if (ImGui::ColorEdit3("Color##Keys", &_state.background.keysColor[0], ImGuiColorEditFlags_NoInputs)) { + if (ImGui::ColorEdit3("Fill Color##Keys", &_state.background.keysColor[0], ImGuiColorEditFlags_NoInputs)) { _score->setColors(_state.background.linesColor, _state.background.textColor, _state.background.keysColor); } ImGui::PopItemWidth(); @@ -827,6 +827,20 @@ void Renderer::showKeyboardOptions(){ } ImGui::PopItemWidth(); + ImGuiPushItemWidth(25); + if (ImGui::Checkbox("Minor edges##Keys", &_state.keyboard.minorEdges)){ + _scene->setMinorEdgesAndHeight(_state.keyboard.minorEdges, _state.keyboard.minorHeight); + } + ImGui::PopItemWidth(); + ImGuiSameLine(COLUMN_SIZE); + + ImGuiPushItemWidth(100); + if(ImGuiSliderPercent("Minor height##Keys", &_state.keyboard.minorHeight, 0.0f, 1.0f)){ + _state.keyboard.minorHeight = glm::clamp(_state.keyboard.minorHeight, 0.0f, 1.0f); + _scene->setMinorEdgesAndHeight(_state.keyboard.minorEdges, _state.keyboard.minorHeight); + } + ImGui::PopItemWidth(); + ImGui::Checkbox("Highlight keys", &_state.keyboard.highlightKeys); if (_state.keyboard.highlightKeys) { @@ -1388,6 +1402,7 @@ void Renderer::applyAllSettings() { _score->setDisplay(_state.background.digits, _state.background.hLines, _state.background.vLines); _score->setColors(_state.background.linesColor, _state.background.textColor, _state.background.keysColor); _scene->setKeyboardSizeAndFadeout(_state.keyboard.size, _state.notesFadeOut); + _scene->setMinorEdgesAndHeight(_state.keyboard.minorEdges, _state.keyboard.minorHeight); _score->setKeyboardSize(_state.keyboard.size); _score->setPlayDirection(_state.reverseScroll); diff --git a/src/rendering/State.cpp b/src/rendering/State.cpp index 954ed8b..db3b6f8 100755 --- a/src/rendering/State.cpp +++ b/src/rendering/State.cpp @@ -97,6 +97,7 @@ void State::defineOptions(){ _sharedInfos["bg-img-behind-keyboard"] = {"Should the background image extend behind the keyboard", OptionInfos::Type::BOOLEAN}; _sharedInfos["keyboard-highlight"] = {"Should the pressed keys be highlighted", OptionInfos::Type::BOOLEAN}; _sharedInfos["keyboard-custom-colors"] = {"Override notes color for pressed keys", OptionInfos::Type::BOOLEAN}; + _sharedInfos["keyboard-minor-edges"] = {"Show edges around minor keys", OptionInfos::Type::BOOLEAN}; _sharedInfos["show-score"] = {"Should the score (lines, numbers) be shown", OptionInfos::Type::BOOLEAN}; _sharedInfos["show-bg-img"] = {"Use a background texture", OptionInfos::Type::BOOLEAN}; _sharedInfos["show-notes"] = {"Should the notes be shown", OptionInfos::Type::BOOLEAN}; @@ -159,7 +160,7 @@ void State::defineOptions(){ _sharedInfos["sets-separator-key"].category = OptionInfos::Category::SETS; _sharedInfos["keyboard-size"] = {"Vertical size of the keyboard", OptionInfos::Type::FLOAT, {0.0f, 1.0f}}; - + _sharedInfos["keyboard-minor-height"] = {"Vertical fraction of the keyboard taken by minor keys", OptionInfos::Type::FLOAT, {0.0f, 1.0f}}; _sharedInfos["min-key"] = {"Lowest key to display", OptionInfos::Type::KEY, {0.0f, 127.0f}}; _sharedInfos["max-key"] = {"Highest key to display", OptionInfos::Type::KEY, {0.0f, 127.0f}}; @@ -241,6 +242,7 @@ void State::updateOptions(){ _boolInfos["bg-img-behind-keyboard"] = &background.imageBehindKeyboard; _boolInfos["keyboard-highlight"] = &keyboard.highlightKeys; _boolInfos["keyboard-custom-colors"] = &keyboard.customKeyColors; + _boolInfos["keyboard-minor-edges"] = &keyboard.minorEdges; _boolInfos["show-score"] = &showScore; _boolInfos["show-bg-img"] = &background.image; _boolInfos["show-notes"] = &showNotes; @@ -278,6 +280,7 @@ void State::updateOptions(){ _intInfos["sets-mode"] = (int*)&setOptions.mode; _intInfos["sets-separator-key"] = &setOptions.key; _floatInfos["keyboard-size"] = &keyboard.size; + _floatInfos["keyboard-minor-height"] = &keyboard.minorHeight; _intInfos["min-key"] = &minKey; _intInfos["max-key"] = &maxKey; @@ -592,8 +595,10 @@ void State::reset(){ scrollSpeed = 1.0f; notesFadeOut = 0.0f; keyboard.highlightKeys = true; + keyboard.minorEdges = true; keyboard.customKeyColors = false; keyboard.size = 0.25f; + keyboard.minorHeight = 0.6f; for (int i = 0; i < layersMap.size(); ++i) { layersMap[i] = i; diff --git a/src/rendering/State.h b/src/rendering/State.h index b4dfa7d..9fab1ed 100755 --- a/src/rendering/State.h +++ b/src/rendering/State.h @@ -13,7 +13,7 @@ #include #define MIDIVIZ_VERSION_MAJOR 7 -#define MIDIVIZ_VERSION_MINOR 0 +#define MIDIVIZ_VERSION_MINOR 1 #define COLUMN_SIZE 170 #define EXPORT_COLUMN_SIZE 200 @@ -78,8 +78,10 @@ class State { ColorArray majorColor; ///< Major key pressed color. ColorArray minorColor; ///< Minor key pressed color. float size; ///< Size on screen, starting from the bottom. + float minorHeight; ///< Fraction of the keyboard height taken by the minor keys. bool highlightKeys; ///< Highlight pressed keys. bool customKeyColors; ///< Use the custom colors above instead of the color of the notes. + bool minorEdges; ///< Show edges on minor keys. }; struct PedalsState { diff --git a/src/rendering/scene/MIDIScene.cpp b/src/rendering/scene/MIDIScene.cpp index 15db587..d68bd8f 100755 --- a/src/rendering/scene/MIDIScene.cpp +++ b/src/rendering/scene/MIDIScene.cpp @@ -44,7 +44,7 @@ void MIDIScene::renderSetup(){ _flagsBufferId = 0; glGenBuffers(1, &_flagsBufferId); glBindBuffer(GL_ARRAY_BUFFER, _flagsBufferId); - glBufferData(GL_ARRAY_BUFFER, sizeof(int) * 128, NULL, GL_DYNAMIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(int) * 128, nullptr, GL_DYNAMIC_DRAW); // Programs. @@ -140,7 +140,9 @@ void MIDIScene::renderSetup(){ glUseProgram(0); // Keyboard setup. - _programKeysId = createGLProgramFromStrings(ResourcesManager::getStringForShader("keys_vert"), ResourcesManager::getStringForShader("keys_frag")); + _programKeyMajorsId = createGLProgramFromStrings(ResourcesManager::getStringForShader("majorKeys_vert"), ResourcesManager::getStringForShader("majorKeys_frag")); + _programKeyMinorsId = createGLProgramFromStrings(ResourcesManager::getStringForShader("minorKeys_vert"), ResourcesManager::getStringForShader("minorKeys_frag")); + glGenVertexArrays(1, &_vaoKeyboard); glBindVertexArray(_vaoKeyboard); // The first attribute will be the vertices positions. @@ -222,8 +224,8 @@ void MIDIScene::setScaleAndMinorWidth(const float scale, const float minorWidth) glUniform1f(speedID, scale); GLuint widthId = glGetUniformLocation(_programId, "minorsWidth"); glUniform1f(widthId, minorWidth); - glUseProgram(_programKeysId); - GLuint widthId1 = glGetUniformLocation(_programKeysId, "minorsWidth"); + glUseProgram(_programKeyMinorsId); + GLuint widthId1 = glGetUniformLocation(_programKeyMinorsId, "minorsWidth"); glUniform1f(widthId1, minorWidth); glUseProgram(0); } @@ -245,13 +247,22 @@ void MIDIScene::setKeyboardSizeAndFadeout(float keyboardHeight, float fadeOut){ glUniform1f(glGetUniformLocation(_programId, "fadeOut"), fadeOutFinal); glUseProgram(_programParticulesId); glUniform1f(glGetUniformLocation(_programParticulesId, "keyboardHeight"), keyboardHeight); - glUseProgram(_programKeysId); - glUniform1f(glGetUniformLocation(_programKeysId, "keyboardHeight"), keyboardHeight); + glUseProgram(_programKeyMinorsId); + glUniform1f(glGetUniformLocation(_programKeyMinorsId, "keyboardHeight"), keyboardHeight); + glUseProgram(_programKeyMajorsId); + glUniform1f(glGetUniformLocation(_programKeyMajorsId, "keyboardHeight"), keyboardHeight); glUseProgram(_programFlashesId); glUniform1f(glGetUniformLocation(_programFlashesId, "keyboardHeight"), keyboardHeight); glUseProgram(0); } +void MIDIScene::setMinorEdgesAndHeight(bool minorEdges, float minorHeight){ + glUseProgram(_programKeyMinorsId); + glUniform1i(glGetUniformLocation(_programKeyMinorsId, "edgeOnMinors"), int(minorEdges)); + glUniform1f(glGetUniformLocation(_programKeyMinorsId, "minorsHeight"), minorHeight); + glUseProgram(0); +} + void MIDIScene::resetParticles() { for (auto & particle : _particles) { particle.note = -1; @@ -378,26 +389,47 @@ void MIDIScene::drawFlashes(float time, const glm::vec2 & invScreenSize, const C void MIDIScene::drawKeyboard(float, const glm::vec2 & invScreenSize, const glm::vec3 & keyColor, const ColorArray & majorColors, const ColorArray & minorColors, bool highlightKeys) { - glUseProgram(_programKeysId); + { + glUseProgram(_programKeyMajorsId); + + const GLuint highId = glGetUniformLocation(_programKeyMajorsId, "highlightKeys"); + const GLuint activesId = glGetUniformLocation(_programKeyMajorsId, "actives"); + const GLuint screenId1 = glGetUniformLocation(_programKeyMajorsId, "inverseScreenSize"); + const GLuint colorId = glGetUniformLocation(_programKeyMajorsId, "edgeColor"); + const GLuint majorId = glGetUniformLocation(_programKeyMajorsId, "majorColor"); + + // Uniforms setup. + glUniform2fv(screenId1, 1, &(invScreenSize[0])); + glUniform3fv(colorId, 1, &(keyColor[0])); + glUniform3fv(majorId, GLsizei(majorColors.size()), &(majorColors[0][0])); + glUniform1i(highId, int(highlightKeys)); + glUniform1iv(activesId, GLsizei(_actives.size()), &(_actives[0])); + + // Draw the geometry. + glBindVertexArray(_vaoKeyboard); + glDrawElements(GL_TRIANGLES, int(_primitiveCount), GL_UNSIGNED_INT, (void*)0); + } - // Uniforms setup. - const GLuint screenId1 = glGetUniformLocation(_programKeysId, "inverseScreenSize"); - const GLuint colorId = glGetUniformLocation(_programKeysId, "keysColor"); - const GLuint majorId = glGetUniformLocation(_programKeysId, "majorColor"); - const GLuint minorId = glGetUniformLocation(_programKeysId, "minorColor"); - const GLuint highId = glGetUniformLocation(_programKeysId, "highlightKeys"); - const GLuint activesId = glGetUniformLocation(_programKeysId, "actives"); - glUniform2fv(screenId1, 1, &(invScreenSize[0])); - glUniform3fv(colorId, 1, &(keyColor[0])); - glUniform3fv(majorId, GLsizei(majorColors.size()), &(majorColors[0][0])); - glUniform3fv(minorId, GLsizei(minorColors.size()), &(minorColors[0][0])); - glUniform1i(highId, int(highlightKeys)); - glUniform1iv(activesId, GLsizei(_actives.size()), &(_actives[0])); + { + glUseProgram(_programKeyMinorsId); - // Draw the geometry. - glBindVertexArray(_vaoKeyboard); - glDrawElements(GL_TRIANGLES, int(_primitiveCount), GL_UNSIGNED_INT, (void*)0); + const GLuint highId = glGetUniformLocation(_programKeyMinorsId, "highlightKeys"); + const GLuint activesId = glGetUniformLocation(_programKeyMinorsId, "actives"); + const GLuint screenId1 = glGetUniformLocation(_programKeyMinorsId, "inverseScreenSize"); + const GLuint colorId = glGetUniformLocation(_programKeyMinorsId, "edgeColor"); + const GLuint minorId = glGetUniformLocation(_programKeyMinorsId, "minorColor"); + + // Uniforms setup. + glUniform2fv(screenId1, 1, &(invScreenSize[0])); + glUniform3fv(colorId, 1, &(keyColor[0])); + glUniform3fv(minorId, GLsizei(minorColors.size()), &(minorColors[0][0])); + glUniform1i(highId, int(highlightKeys)); + glUniform1iv(activesId, GLsizei(_actives.size()), &(_actives[0])); + glBindVertexArray(_vaoKeyboard); + glDrawElementsInstanced(GL_TRIANGLES, int(_primitiveCount), GL_UNSIGNED_INT, (void*)0, 53); + } + glBindVertexArray(0); glUseProgram(0); } @@ -512,9 +544,12 @@ void MIDIScene::setMinMaxKeys(int minKey, int minKeyMajor, int notesCount){ glUseProgram(_programFlashesId); glUniform1i(glGetUniformLocation(_programFlashesId, "minNote"), minKey); glUniform1f(glGetUniformLocation(_programFlashesId, "notesCount"), float(notesCount)); - glUseProgram(_programKeysId); - glUniform1i(glGetUniformLocation(_programKeysId, "minNoteMajor"), minKeyMajor); - glUniform1f(glGetUniformLocation(_programKeysId, "notesCount"), float(notesCount)); + glUseProgram(_programKeyMajorsId); + glUniform1i(glGetUniformLocation(_programKeyMajorsId, "minNoteMajor"), minKeyMajor); + glUniform1f(glGetUniformLocation(_programKeyMajorsId, "notesCount"), float(notesCount)); + glUseProgram(_programKeyMinorsId); + glUniform1i(glGetUniformLocation(_programKeyMinorsId, "minNoteMajor"), minKeyMajor); + glUniform1f(glGetUniformLocation(_programKeyMinorsId, "notesCount"), float(notesCount)); glUseProgram(_programParticulesId); glUniform1i(glGetUniformLocation(_programParticulesId, "minNote"), minKey); glUniform1f(glGetUniformLocation(_programParticulesId, "notesCount"), float(notesCount)); @@ -528,8 +563,10 @@ void MIDIScene::setOrientation(bool horizontal){ glUniform1i(glGetUniformLocation(_programId, "horizontalMode"), val); glUseProgram(_programFlashesId); glUniform1i(glGetUniformLocation(_programFlashesId, "horizontalMode"), val); - glUseProgram(_programKeysId); - glUniform1i(glGetUniformLocation(_programKeysId, "horizontalMode"), val); + glUseProgram(_programKeyMajorsId); + glUniform1i(glGetUniformLocation(_programKeyMajorsId, "horizontalMode"), val); + glUseProgram(_programKeyMinorsId); + glUniform1i(glGetUniformLocation(_programKeyMinorsId, "horizontalMode"), val); glUseProgram(_programParticulesId); glUniform1i(glGetUniformLocation(_programParticulesId, "horizontalMode"), val); glUseProgram(_programWaveId); diff --git a/src/rendering/scene/MIDIScene.h b/src/rendering/scene/MIDIScene.h index 8abf625..da7dbd0 100755 --- a/src/rendering/scene/MIDIScene.h +++ b/src/rendering/scene/MIDIScene.h @@ -35,6 +35,8 @@ class MIDIScene { void setKeyboardSizeAndFadeout(float keyboardHeight, float fadeOut); + void setMinorEdgesAndHeight(bool minorEdges, float minorHeight); + void setMinMaxKeys(int minKey, int minKeyMajor, int notesCount); void setOrientation(bool horizontal); @@ -100,7 +102,8 @@ class MIDIScene { GLuint _programId; GLuint _programFlashesId; GLuint _programParticulesId; - GLuint _programKeysId; + GLuint _programKeyMinorsId; + GLuint _programKeyMajorsId; GLuint _programPedalsId; GLuint _programWaveId; From 12220890b148bbb198bea235ce9683143146ea1b Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Mon, 31 Jul 2023 23:42:32 +0200 Subject: [PATCH 4/9] Notes: improve alignment with minor keys. --- resources/shaders/notes.vert | 16 +++++++++++++++- src/resources/shaders.cpp | 8 +++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/resources/shaders/notes.vert b/resources/shaders/notes.vert index 358dcfa..cd7498e 100644 --- a/resources/shaders/notes.vert +++ b/resources/shaders/notes.vert @@ -25,6 +25,18 @@ out INTERFACE { float channel; } Out; +#define MAJOR_COUNT 75 +const int minorIds[MAJOR_COUNT] = int[](1, 3, 0, 6, 8, 10, 0, 13, 15, 0, 18, 20, 22, 0, 25, 27, 0, 30, 32, 34, 0, 37, 39, 0, 42, 44, 46, 0, 49, 51, 0, 54, 56, 58, 0, 61, 63, 0, 66, 68, 70, 0, 73, 75, 0, 78, 80, 82, 0, 85, 87, 0, 90, 92, 94, 0, 97, 99, 0, 102, 104, 106, 0, 109, 111, 0, 114, 116, 118, 0, 121, 123, 0, 126, 0); + +float minorShift(int id){ + if(id == 1 || id == 6){ + return -0.1; + } + if(id == 3 || id == 10){ + return 0.1; + } + return 0.0; +} void main(){ @@ -47,7 +59,9 @@ void main(){ float vertLoc = 2.0 * keyboardHeight - 1.0; vertLoc += (reverseMode ? -1.0 : 1.0) * (Out.noteSize.y * 0.5 + mainSpeed * (id.y - time)); vec2 noteShift = vec2(horizLoc, vertLoc); - + + noteShift.x += id.w * minorShift(minorIds[int(id.x)] % 12) * Out.noteSize.x; + // Scale uv. Out.uv = Out.noteSize * v; Out.isMinor = id.w; diff --git a/src/resources/shaders.cpp b/src/resources/shaders.cpp index 011c7a3..28d0221 100644 --- a/src/resources/shaders.cpp +++ b/src/resources/shaders.cpp @@ -4,7 +4,7 @@ const std::unordered_map shaders = { { "background_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n uniform float time;\n uniform float secondsPerMeasure;\n uniform vec2 inverseScreenSize;\n uniform bool useDigits = true;\n uniform bool useHLines = true;\n uniform bool useVLines = true;\n uniform float minorsWidth = 1.0;\n uniform sampler2D screenTexture;\n uniform vec3 textColor = vec3(1.0);\n uniform vec3 linesColor = vec3(1.0);\n uniform bool reverseMode = false;\n uniform bool horizontalMode = false;\n vec2 flipUVIfNeeded(vec2 inUV){\n vec2 shiftUV = inUV - 0.5;\n return horizontalMode ? vec2(shiftUV.y, -shiftUV.x) + 0.5 : inUV;\n }\n #define MAJOR_COUNT 75.0\n const float octaveLinesPositions[11] = float[](0.0/75.0, 7.0/75.0, 14.0/75.0, 21.0/75.0, 28.0/75.0, 35.0/75.0, 42.0/75.0, 49.0/75.0, 56.0/75.0, 63.0/75.0, 70.0/75.0);\n \n uniform float mainSpeed;\n uniform float keyboardHeight = 0.25;\n uniform int minNoteMajor;\n uniform float notesCount;\n out vec4 fragColor;\n float printDigit(int digit, vec2 uv){\n // Clamping to avoid artifacts.\n if(uv.x < 0.01 || uv.x > 0.99 || uv.y < 0.01 || uv.y > 0.99){\n return 0.0;\n }\n \n // UV from [0,1] to local tile frame.\n vec2 localUV = flipUVIfNeeded(uv) * vec2(50.0/256.0,0.5);\n // Select the digit.\n vec2 globalUV = vec2( mod(digit,5)*50.0/256.0,digit < 5 ? 0.5 : 0.0);\n // Combine global and local shifts.\n vec2 finalUV = globalUV + localUV;\n \n // Read from font atlas. Return if above a threshold.\n float isIn = texture(screenTexture, finalUV).r;\n return isIn < 0.5 ? 0.0 : isIn ;\n \n }\n float printNumber(float num, vec2 position, vec2 uv, vec2 scale){\n if(num < -0.1){\n return 0.0f;\n }\n if(position.y > 1.0 || position.y < 0.0){\n return 0.0;\n }\n \n // We limit to the [0,999] range.\n float number = min(999.0, max(0.0,num));\n \n // Extract digits.\n int hundredDigit = int(floor( number / 100.0 ));\n int tenDigit = int(floor( number / 10.0 - hundredDigit * 10.0));\n int unitDigit = int(floor( number - hundredDigit * 100.0 - tenDigit * 10.0));\n \n // Position of the text.\n vec2 initialPos = scale*(uv-position);\n \n // Get intensity for each digit at the current fragment.\n vec2 shift = horizontalMode ? vec2(0.0, scale.y) : vec2(scale.x, 0.0);\n shift *= 0.009;\n float off = horizontalMode ? 3.0 : 0.0;\n float hundred = printDigit(hundredDigit, initialPos + off * shift);\n float ten = printDigit(tenDigit, initialPos + (off - 1.0) * shift);\n float unit = printDigit(unitDigit, initialPos + (off - 2.0) * shift);\n \n // If hundred digit == 0, hide it.\n float hundredVisibility = (1.0-step(float(hundredDigit),0.5));\n hundred *= hundredVisibility;\n // If ten digit == 0 and hundred digit == 0, hide ten.\n float tenVisibility = max(hundredVisibility,(1.0-step(float(tenDigit),0.5)));\n ten*= tenVisibility;\n \n return hundred + ten + unit;\n }\n void main(){\n \n vec4 bgColor = vec4(0.0);\n vec2 inUV = In.uv;\n float xRatio = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x;\n float yRatio = horizontalMode ? inverseScreenSize.x : inverseScreenSize.y;\n // Octaves lines.\n if(useVLines){\n // send 0 to (minNote)/MAJOR_COUNT\n // send 1 to (maxNote)/MAJOR_COUNT\n float a = (notesCount) / MAJOR_COUNT;\n float b = float(minNoteMajor) / MAJOR_COUNT;\n float refPos = a * inUV.x + b;\n for(int i = 0; i < 11; i++){\n float linePos = octaveLinesPositions[i];\n float lineIntensity = 0.7 * step(abs(refPos - linePos), xRatio / MAJOR_COUNT * notesCount);\n bgColor = mix(bgColor, vec4(linesColor, 1.0), lineIntensity);\n }\n }\n float screenRatio = inverseScreenSize.x/inverseScreenSize.y;\n vec2 scale = 1.5 * vec2(64.0, 50.0 * screenRatio);\n if(horizontalMode){\n scale = scale.yx;\n }\n // Text on the side.\n int currentMesure = int(floor(time/secondsPerMeasure));\n // How many mesures do we check.\n int count = int(ceil(0.75*(2.0/mainSpeed)))+2;\n // We check two extra measures to avoid sudden disappearance below the keyboard.\n for(int i = -2; i < count; i++){\n // Compute position of the measure currentMesure+-i.\n int mesure = currentMesure + (reverseMode ? -1 : 1) * i;\n vec2 position = vec2(0.005, keyboardHeight + (reverseMode ? -1.0 : 1.0) * (secondsPerMeasure * mesure - time)*mainSpeed*0.5);\n // Compute color for the number display, and for the horizontal line.\n float numberIntensity = useDigits ? printNumber(mesure, position, inUV, scale) : 0.0;\n bgColor = mix(bgColor, vec4(textColor, 1.0), numberIntensity);\n float lineIntensity = useHLines ? (0.25*(step(abs(inUV.y - position.y - 0.5 / scale.y), yRatio))) : 0.0;\n bgColor = mix(bgColor, vec4(linesColor, 1.0), lineIntensity);\n }\n \n fragColor = bgColor;\n }\n "}, { "flashes_vert", "#version 330\n layout(location = 0) in vec2 v;\n layout(location = 1) in int onChan;\n uniform float time;\n uniform vec2 inverseScreenSize;\n uniform float userScale = 1.0;\n uniform float keyboardHeight = 0.25;\n uniform int minNote;\n uniform float notesCount;\n uniform bool horizontalMode = false;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n const float shifts[128] = float[](\n 0,0.5,1,1.5,2,3,3.5,4,4.5,5,5.5,6,7,7.5,8,8.5,9,10,10.5,11,11.5,12,12.5,13,14,14.5,15,15.5,16,17,17.5,18,18.5,19,19.5,20,21,21.5,22,22.5,23,24,24.5,25,25.5,26,26.5,27,28,28.5,29,29.5,30,31,31.5,32,32.5,33,33.5,34,35,35.5,36,36.5,37,38,38.5,39,39.5,40,40.5,41,42,42.5,43,43.5,44,45,45.5,46,46.5,47,47.5,48,49,49.5,50,50.5,51,52,52.5,53,53.5,54,54.5,55,56,56.5,57,57.5,58,59,59.5,60,60.5,61,61.5,62,63,63.5,64,64.5,65,66,66.5,67,67.5,68,68.5,69,70,70.5,71,71.5,72,73,73.5,74\n );\n const vec2 scale = 0.9*vec2(3.5,3.0);\n out INTERFACE {\n vec2 uv;\n float onChannel;\n float id;\n } Out;\n void main(){\n \n // Scale quad, keep the square ratio.\n float screenRatio = inverseScreenSize.y/inverseScreenSize.x;\n vec2 scalingFactor = vec2(1.0, horizontalMode ? (1.0/screenRatio) : screenRatio);\n vec2 scaledPosition = v * 2.0 * scale * userScale/notesCount * scalingFactor;\n // Shift based on note/flash id.\n vec2 globalShift = vec2(-1.0 + ((shifts[gl_InstanceID] - shifts[minNote]) * 2.0 + 1.0) / notesCount, 2.0 * keyboardHeight - 1.0);\n \n gl_Position = vec4(flipIfNeeded(scaledPosition + globalShift), 0.0 , 1.0) ;\n \n // Pass infos to the fragment shader.\n Out.uv = v;\n Out.onChannel = float(onChan);\n Out.id = float(gl_InstanceID);\n \n }\n "}, { "flashes_frag", "#version 330\n #define SETS_COUNT 8\n in INTERFACE {\n vec2 uv;\n float onChannel;\n float id;\n } In;\n uniform sampler2D textureFlash;\n uniform float time;\n uniform vec3 baseColor[SETS_COUNT];\n #define numberSprites 8.0\n out vec4 fragColor;\n float rand(vec2 co){\n return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n }\n void main(){\n \n // If not on, discard flash immediatly.\n int cid = int(In.onChannel);\n if(cid < 0){\n discard;\n }\n float mask = 0.0;\n \n // If up half, read from texture atlas.\n if(In.uv.y > 0.0){\n // Select a sprite, depending on time and flash id.\n float shift = floor(mod(15.0 * time, numberSprites)) + floor(rand(In.id * vec2(time,1.0)));\n vec2 globalUV = vec2(0.5 * mod(shift, 2.0), 0.25 * floor(shift/2.0));\n \n // Scale UV to fit in one sprite from atlas.\n vec2 localUV = In.uv * 0.5 + vec2(0.25,-0.25);\n localUV.y = min(-0.05,localUV.y); //Safety clamp on the upper side (or you could set clamp_t)\n \n // Read in black and white texture do determine opacity (mask).\n vec2 finalUV = globalUV + localUV;\n mask = texture(textureFlash,finalUV).r;\n }\n \n // Colored sprite.\n vec4 spriteColor = vec4(baseColor[cid], mask);\n \n // Circular halo effect.\n float haloAlpha = 1.0 - smoothstep(0.07,0.5,length(In.uv));\n vec4 haloColor = vec4(1.0,1.0,1.0, haloAlpha * 0.92);\n \n // Mix the sprite color and the halo effect.\n fragColor = mix(spriteColor, haloColor, haloColor.a);\n \n // Boost intensity.\n fragColor *= 1.1;\n // Premultiplied alpha.\n fragColor.rgb *= fragColor.a;\n }\n "}, -{ "notes_vert", "#version 330\n layout(location = 0) in vec2 v;\n layout(location = 1) in vec4 id; //note id, start, duration, is minor\n layout(location = 2) in float channel; //note id, start, duration, is minor\n uniform float time;\n uniform float mainSpeed;\n uniform float minorsWidth = 1.0;\n uniform float keyboardHeight = 0.25;\n uniform bool reverseMode = false;\n uniform bool horizontalMode = false;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n uniform int minNoteMajor;\n uniform float notesCount;\n out INTERFACE {\n vec2 uv;\n vec2 noteSize;\n float isMinor;\n float channel;\n } Out;\n void main(){\n \n float scalingFactor = id.w != 0.0 ? minorsWidth : 1.0;\n // Size of the note : width, height based on duration and current speed.\n Out.noteSize = vec2(0.9*2.0/notesCount * scalingFactor, id.z*mainSpeed);\n \n // Compute note shift.\n // Horizontal shift based on note id, width of keyboard, and if the note is minor or not.\n // Vertical shift based on note start time, current time, speed, and height of the note quad.\n //float a = (1.0/(notesCount-1.0)) * (2.0 - 2.0/notesCount);\n //float b = -1.0 + 1.0/notesCount;\n // This should be in -1.0, 1.0.\n // input: id.x is in [0 MAJOR_COUNT]\n // we want minNote to -1+1/c, maxNote to 1-1/c\n float a = 2.0;\n float b = -notesCount + 1.0 - 2.0 * float(minNoteMajor);\n float horizLoc = (id.x * a + b + id.w) / notesCount;\n float vertLoc = 2.0 * keyboardHeight - 1.0;\n vertLoc += (reverseMode ? -1.0 : 1.0) * (Out.noteSize.y * 0.5 + mainSpeed * (id.y - time));\n vec2 noteShift = vec2(horizLoc, vertLoc);\n \n // Scale uv.\n Out.uv = Out.noteSize * v;\n Out.isMinor = id.w;\n Out.channel = channel;\n // Output position.\n gl_Position = vec4(flipIfNeeded(Out.noteSize * v + noteShift), 0.0 , 1.0) ;\n \n }\n "}, +{ "notes_vert", "#version 330\n layout(location = 0) in vec2 v;\n layout(location = 1) in vec4 id; //note id, start, duration, is minor\n layout(location = 2) in float channel; //note id, start, duration, is minor\n uniform float time;\n uniform float mainSpeed;\n uniform float minorsWidth = 1.0;\n uniform float keyboardHeight = 0.25;\n uniform bool reverseMode = false;\n uniform bool horizontalMode = false;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n uniform int minNoteMajor;\n uniform float notesCount;\n out INTERFACE {\n vec2 uv;\n vec2 noteSize;\n float isMinor;\n float channel;\n } Out;\n #define MAJOR_COUNT 75\n const int minorIds[MAJOR_COUNT] = int[](1, 3, 0, 6, 8, 10, 0, 13, 15, 0, 18, 20, 22, 0, 25, 27, 0, 30, 32, 34, 0, 37, 39, 0, 42, 44, 46, 0, 49, 51, 0, 54, 56, 58, 0, 61, 63, 0, 66, 68, 70, 0, 73, 75, 0, 78, 80, 82, 0, 85, 87, 0, 90, 92, 94, 0, 97, 99, 0, 102, 104, 106, 0, 109, 111, 0, 114, 116, 118, 0, 121, 123, 0, 126, 0);\n float minorShift(int id){\n if(id == 1 || id == 6){\n return -0.1;\n }\n if(id == 3 || id == 10){\n return 0.1;\n }\n return 0.0;\n }\n void main(){\n \n float scalingFactor = id.w != 0.0 ? 0.9*minorsWidth : 1.0;\n // Size of the note : width, height based on duration and current speed.\n Out.noteSize = vec2(0.9*2.0/notesCount * scalingFactor, id.z*mainSpeed);\n \n // Compute note shift.\n // Horizontal shift based on note id, width of keyboard, and if the note is minor or not.\n // Vertical shift based on note start time, current time, speed, and height of the note quad.\n //float a = (1.0/(notesCount-1.0)) * (2.0 - 2.0/notesCount);\n //float b = -1.0 + 1.0/notesCount;\n // This should be in -1.0, 1.0.\n // input: id.x is in [0 MAJOR_COUNT]\n // we want minNote to -1+1/c, maxNote to 1-1/c\n float a = 2.0;\n float b = -notesCount + 1.0 - 2.0 * float(minNoteMajor);\n float horizLoc = (id.x * a + b + id.w) / notesCount;\n float vertLoc = 2.0 * keyboardHeight - 1.0;\n vertLoc += (reverseMode ? -1.0 : 1.0) * (Out.noteSize.y * 0.5 + mainSpeed * (id.y - time));\n vec2 noteShift = vec2(horizLoc, vertLoc);\n noteShift.x += id.w * minorShift(minorIds[int(id.x)] % 12) * Out.noteSize.x;\n // Scale uv.\n Out.uv = Out.noteSize * v;\n Out.isMinor = id.w;\n Out.channel = channel;\n // Output position.\n gl_Position = vec4(flipIfNeeded(Out.noteSize * v + noteShift), 0.0 , 1.0) ;\n \n }\n "}, { "notes_frag", "#version 330\n #define SETS_COUNT 8\n in INTERFACE {\n vec2 uv;\n vec2 noteSize;\n float isMinor;\n float channel;\n } In;\n uniform vec3 baseColor[SETS_COUNT];\n uniform vec3 minorColor[SETS_COUNT];\n uniform vec2 inverseScreenSize;\n uniform float colorScale;\n uniform float keyboardHeight = 0.25;\n uniform float fadeOut = 0.0;\n uniform bool horizontalMode = false;\n #define cornerRadius 0.01\n out vec4 fragColor;\n void main(){\n \n // If lower area of the screen, discard fragment as it should be hidden behind the keyboard.\n vec2 normalizedCoord = vec2(gl_FragCoord.xy) * inverseScreenSize;\n if((horizontalMode ? normalizedCoord.x : normalizedCoord.y) < keyboardHeight){\n discard;\n }\n \n // Rounded corner (super-ellipse equation).\n float radiusPosition = pow(abs(In.uv.x/(0.5*In.noteSize.x)), In.noteSize.x/cornerRadius) + pow(abs(In.uv.y/(0.5*In.noteSize.y)), In.noteSize.y/cornerRadius);\n \n if( radiusPosition > 1.0){\n discard;\n }\n \n // Fragment color.\n int cid = int(In.channel);\n fragColor.rgb = colorScale * mix(baseColor[cid], minorColor[cid], In.isMinor);\n \n if( radiusPosition > 0.8){\n fragColor.rgb *= 1.05;\n }\n float distFromBottom = horizontalMode ? normalizedCoord.x : normalizedCoord.y;\n float fadeOutFinal = min(fadeOut, 0.9999);\n distFromBottom = max(distFromBottom - fadeOutFinal, 0.0) / (1.0 - fadeOutFinal);\n float alpha = 1.0 - distFromBottom;\n fragColor.a = alpha;\n }\n "}, { "particles_vert", "#version 330\n #define SETS_COUNT 8\n layout(location = 0) in vec2 v;\n uniform float time;\n uniform float scale;\n uniform vec3 baseColor[SETS_COUNT];\n uniform vec2 inverseScreenSize;\n uniform sampler2D textureParticles;\n uniform vec2 inverseTextureSize;\n uniform int globalId;\n uniform float duration;\n uniform int channel;\n uniform int texCount;\n uniform float colorScale;\n uniform float expansionFactor = 1.0;\n uniform float speedScaling = 0.2;\n uniform float keyboardHeight = 0.25;\n uniform int minNote;\n uniform float notesCount;\n uniform bool horizontalMode = false;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n const float shifts[128] = float[](\n 0,0.5,1,1.5,2,3,3.5,4,4.5,5,5.5,6,7,7.5,8,8.5,9,10,10.5,11,11.5,12,12.5,13,14,14.5,15,15.5,16,17,17.5,18,18.5,19,19.5,20,21,21.5,22,22.5,23,24,24.5,25,25.5,26,26.5,27,28,28.5,29,29.5,30,31,31.5,32,32.5,33,33.5,34,35,35.5,36,36.5,37,38,38.5,39,39.5,40,40.5,41,42,42.5,43,43.5,44,45,45.5,46,46.5,47,47.5,48,49,49.5,50,50.5,51,52,52.5,53,53.5,54,54.5,55,56,56.5,57,57.5,58,59,59.5,60,60.5,61,61.5,62,63,63.5,64,64.5,65,66,66.5,67,67.5,68,68.5,69,70,70.5,71,71.5,72,73,73.5,74\n );\n out INTERFACE {\n vec4 color;\n vec2 uv;\n float id;\n } Out;\n float rand(vec2 co){\n return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n }\n void main(){\n Out.id = float(gl_InstanceID % texCount);\n Out.uv = v + 0.5;\n // Fade color based on time.\n Out.color = vec4(colorScale * baseColor[channel], 1.0-time*time);\n \n float localTime = speedScaling * time * duration;\n float particlesCount = 1.0/inverseTextureSize.y;\n \n // Pick particle id at random.\n float particleId = float(gl_InstanceID) + floor(particlesCount * 10.0 * rand(vec2(globalId,globalId)));\n float textureId = mod(particleId,particlesCount);\n float particleShift = floor(particleId/particlesCount);\n \n // Particle uv, in pixels.\n vec2 particleUV = vec2(localTime / inverseTextureSize.x + 10.0 * particleShift, textureId);\n // UV in [0,1]\n particleUV = (particleUV+0.5)*vec2(1.0,-1.0)*inverseTextureSize;\n // Avoid wrapping.\n particleUV.x = clamp(particleUV.x,0.0,1.0);\n // We want to skip reading from the very beginning of the trajectories because they are identical.\n // particleUV.x = 0.95 * particleUV.x + 0.05;\n // Read corresponding trajectory to get particle current position.\n vec3 position = texture(textureParticles, particleUV).xyz;\n // Center position (from [0,1] to [-0.5,0.5] on x axis.\n position.x -= 0.5;\n \n // Compute shift, randomly disturb it.\n vec2 shift = 0.5*position.xy;\n float random = rand(vec2(particleId + float(globalId),time*0.000002+100.0*float(globalId)));\n shift += vec2(0.0,0.1*random);\n \n // Scale shift with time (expansion effect).\n shift = shift*time*expansionFactor;\n // and with altitude of the particle (ditto).\n shift.x *= max(0.5, pow(shift.y,0.3));\n \n // Horizontal shift is based on the note ID.\n float xshift = -1.0 + ((shifts[globalId] - shifts[int(minNote)]) * 2.0 + 1.0) / notesCount;\n // Combine global shift (due to note id) and local shift (based on read position).\n vec2 globalShift = vec2(xshift, (2.0 * keyboardHeight - 1.0)-0.02);\n vec2 localShift = 0.003 * scale * v + shift * duration * vec2(1.0,0.5);\n float screenRatio = inverseScreenSize.y/inverseScreenSize.x;\n vec2 screenScaling = vec2(1.0, horizontalMode ? (1.0/screenRatio) : screenRatio);\n vec2 finalPos = globalShift + screenScaling * localShift;\n \n // Discard particles that reached the end of their trajectories by putting them off-screen.\n finalPos = mix(vec2(-200.0),finalPos, position.z);\n // Output final particle position.\n gl_Position = vec4(flipIfNeeded(finalPos), 0.0, 1.0);\n \n \n }\n "}, { "particles_frag", "#version 330\n in INTERFACE {\n vec4 color;\n vec2 uv;\n float id;\n } In;\n uniform sampler2DArray lookParticles;\n out vec4 fragColor;\n void main(){\n float alpha = texture(lookParticles, vec3(In.uv, In.id)).r;\n fragColor = In.color;\n fragColor.a *= alpha;\n }\n "}, @@ -12,8 +12,10 @@ const std::unordered_map shaders = { { "particlesblur_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n uniform sampler2D screenTexture;\n uniform vec2 inverseScreenSize;\n uniform vec3 backgroundColor = vec3(0.0);\n uniform float attenuationFactor = 0.99;\n uniform float time;\n out vec4 fragColor;\n vec4 blur(vec2 uv, bool vert){\n vec4 color = 0.2270270270 * texture(screenTexture, uv);\n vec2 pixelOffset = vert ? vec2(0.0, inverseScreenSize.y) : vec2(inverseScreenSize.x, 0.0);\n vec2 texCoordOffset0 = 1.3846153846 * pixelOffset;\n vec4 col0 = texture(screenTexture, uv + texCoordOffset0) + texture(screenTexture, uv - texCoordOffset0);\n color += 0.3162162162 * col0;\n vec2 texCoordOffset1 = 3.2307692308 * pixelOffset;\n vec4 col1 = texture(screenTexture, uv + texCoordOffset1) + texture(screenTexture, uv - texCoordOffset1);\n color += 0.0702702703 * col1;\n return color;\n }\n void main(){\n \n // Gaussian blur separated in two 1D convolutions, relying on bilinear interpolation to\n // sample multiple pixels at once with the proper weights.\n vec4 color = blur(In.uv, time > 0.5);\n // Include decay for fade out.\n fragColor = mix(vec4(backgroundColor, 0.0), color, attenuationFactor);\n \n }\n "}, { "screenquad_vert", "#version 330\n layout(location = 0) in vec3 v;\n out INTERFACE {\n vec2 uv;\n } Out ;\n void main(){\n \n // We directly output the position.\n gl_Position = vec4(v, 1.0);\n // Output the UV coordinates computed from the positions.\n Out.uv = v.xy * 0.5 + 0.5;\n \n }\n "}, { "screenquad_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n uniform sampler2D screenTexture;\n uniform vec2 inverseScreenSize;\n out vec4 fragColor;\n void main(){\n \n fragColor = texture(screenTexture,In.uv);\n \n }\n "}, -{ "keys_vert", "#version 330\n layout(location = 0) in vec2 v;\n out INTERFACE {\n vec2 uv;\n } Out ;\n uniform float keyboardHeight = 0.25;\n uniform bool horizontalMode = false;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n void main(){\n // Input are in -0.5,0.5\n // We directly output the position.\n // [-0.5, 0.5] to [-1, 2.0*keyboardHeight-1.0]\n float yShift = keyboardHeight * (2.0 * v.y + 1.0) - 1.0;\n vec2 pos2D = vec2(v.x*2.0, yShift);\n gl_Position.xy = flipIfNeeded(pos2D);\n gl_Position.zw = vec2(0.0, 1.0);\n // Output the UV coordinates computed from the positions.\n Out.uv = v.xy + 0.5;\n \n }\n "}, -{ "keys_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n #define SETS_COUNT 8\n #define MAJOR_COUNT 75\n uniform vec2 inverseScreenSize;\n uniform float minorsWidth = 1.0;\n uniform vec3 keysColor = vec3(0.0);\n uniform vec3 minorColor[SETS_COUNT];\n uniform vec3 majorColor[SETS_COUNT];\n uniform bool highlightKeys;\n uniform bool horizontalMode = false;\n uniform int actives[128];\n uniform int minNoteMajor;\n uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1)\n const bool isMinor[MAJOR_COUNT] = bool[](true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, true, true, false, true, true, false, true, false);\n const int majorIds[MAJOR_COUNT] = int[](0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24, 26, 28, 29, 31, 33, 35, 36, 38, 40, 41, 43, 45, 47, 48, 50, 52, 53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83, 84, 86, 88, 89, 91, 93, 95, 96, 98, 100, 101, 103, 105, 107, 108, 110, 112, 113, 115, 117, 119, 120, 122, 124, 125, 127);\n const int minorIds[MAJOR_COUNT] = int[](1, 3, 0, 6, 8, 10, 0, 13, 15, 0, 18, 20, 22, 0, 25, 27, 0, 30, 32, 34, 0, 37, 39, 0, 42, 44, 46, 0, 49, 51, 0, 54, 56, 58, 0, 61, 63, 0, 66, 68, 70, 0, 73, 75, 0, 78, 80, 82, 0, 85, 87, 0, 90, 92, 94, 0, 97, 99, 0, 102, 104, 106, 0, 109, 111, 0, 114, 116, 118, 0, 121, 123, 0, 126, 0);\n vec2 minorShift(int id){\n if(id == 1 || id == 6){\n return vec2(0.0, 0.2);\n }\n if(id == 3 || id == 10){\n return vec2(0.2, 0.0);\n }\n return vec2(0.1,0.1);\n }\n out vec4 fragColor;\n void main(){\n // White keys: white\n // Black keys: keyColor\n // Lines between keys: keyColor\n // Active key: activeColor\n // White keys, and separators.\n float widthScaling = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x;\n float intensity = int(abs(fract(In.uv.x * notesCount)) >= 2.0 * notesCount * widthScaling);\n \n // If the current major key is active, the majorColor is specific.\n int majorId = majorIds[clamp(int(In.uv.x * notesCount) + minNoteMajor, 0, 74)];\n int cidMajor = actives[majorId];\n vec3 backColor = (highlightKeys && cidMajor >= 0) ? majorColor[cidMajor] : vec3(1.0);\n vec3 frontColor = keysColor;\n // Upper keyboard.\n if(In.uv.y > 0.4){\n int minorLocalId = min(int(floor(In.uv.x * notesCount + 0.5) + minNoteMajor) - 1, 74);\n // Handle black keys.\n // Hide keys that are on the edges.\n if(minorLocalId >= 0 && isMinor[minorLocalId] && In.uv.x > 0.5/notesCount && In.uv.x < 1.0 - 0.5/notesCount){\n int minorId = minorIds[minorLocalId];\n // Get the shift for non-centered minor keys.\n vec2 shifts = minorsWidth * minorShift(minorId % 12);\n // Compensate total width.\n float marginSize = minorsWidth * 1.2;\n // Rescale UV to take shift into account.\n float localUv = fract(In.uv.x * notesCount + 0.5);\n localUv = abs( (localUv - shifts.x) / (1.0 - shifts.x - shifts.y) * 2.0 - 1.0);\n // Detect edges.\n intensity = step(marginSize, localUv);\n //float roundEdge = (1.0 - exp(50.0 * (-In.uv.y + 0.4)))*1.1;\n //intensity += smoothstep(roundEdge - 0.1, roundEdge + 0.1, localUv);\n //intensity = clamp(intensity, 0.0, 1.0);\n int cidMinor = actives[minorId];\n if(highlightKeys && cidMinor >= 0){\n frontColor = minorColor[cidMinor];\n }\n }\n }\n \n fragColor.rgb = mix(frontColor, backColor, intensity);\n fragColor.a = 1.0;\n }\n "}, +{ "majorKeys_vert", "#version 330\n layout(location = 0) in vec2 v;\n out INTERFACE {\n vec2 uv;\n } Out ;\n uniform bool horizontalMode = false;\n uniform float keyboardHeight = 0.25;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n void main(){\n // Input are in -0.5,0.5\n // We directly output the position.\n // [-0.5, 0.5] to [-1, -1+2.0*keyboardHeight]\n float yShift = keyboardHeight * (2.0 * v.y + 1.0) - 1.0;\n vec2 pos2D = vec2(v.x * 2.0, yShift);\n gl_Position.xy = flipIfNeeded(pos2D);\n gl_Position.zw = vec2(0.0, 1.0);\n // Output the UV coordinates computed from the positions.\n Out.uv = v.xy + 0.5;\n \n }\n "}, +{ "majorKeys_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n #define SETS_COUNT 8\n #define MAJOR_COUNT 75\n uniform bool highlightKeys;\n uniform bool horizontalMode = false;\n uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1)\n uniform int actives[128];\n uniform int minNoteMajor;\n uniform vec2 inverseScreenSize;\n uniform vec3 edgeColor = vec3(0.0);\n uniform vec3 majorColor[SETS_COUNT];\n const int majorIds[MAJOR_COUNT] = int[](0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24, 26, 28, 29, 31, 33, 35, 36, 38, 40, 41, 43, 45, 47, 48, 50, 52, 53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83, 84, 86, 88, 89, 91, 93, 95, 96, 98, 100, 101, 103, 105, 107, 108, 110, 112, 113, 115, 117, 119, 120, 122, 124, 125, 127);\n out vec4 fragColor;\n void main(){\n // White keys, and separators.\n float widthScaling = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x;\n float majorUvX = fract(In.uv.x * notesCount);\n // Edges on the sides\n float centerIntensity = 1.0 - step( 1.0 - 2.0 * notesCount * widthScaling, abs(majorUvX * 2.0 - 1.0));\n // If the current major key is active, the majorColor is specific.\n int majorId = majorIds[clamp(int(In.uv.x * notesCount) + minNoteMajor, 0, 74)];\n int cidMajor = actives[majorId];\n vec3 frontColor = (highlightKeys && cidMajor >= 0) ? majorColor[cidMajor] : vec3(1.0);\n // Mix.\n fragColor.rgb = mix(edgeColor, frontColor, centerIntensity);\n fragColor.a = 1.0;\n }\n "}, +{ "minorKeys_vert", "#version 330\n layout(location = 0) in vec2 v;\n out INTERFACE {\n vec2 uv;\n float id;\n } Out ;\n uniform bool horizontalMode = false;\n uniform float keyboardHeight = 0.25;\n uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1)\n uniform int actives[128];\n uniform int minNoteMajor;\n uniform float minorsHeight = 0.6;\n uniform float minorsWidth = 1.0;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n #define MINOR_COUNT 53\n const int minorIds[MINOR_COUNT] = int[](1, 3, 6, 8, 10, 13, 15, 18, 20, 22, 25, 27, 30, 32, 34, 37, 39, 42, 44, 46, 49, 51, 54, 56, 58, 61, 63, 66, 68, 70, 73, 75, 78, 80, 82, 85, 87, 90, 92, 94, 97, 99, 102, 104, 106, 109, 111, 114, 116, 118, 121, 123, 126);\n const int noteDelta[12] = int[](0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6);\n float minorShift(int id){\n if(id == 1 || id == 6){\n return -0.1;\n }\n if(id == 3 || id == 10){\n return 0.1;\n }\n return 0.0;\n }\n void main(){\n float noteWidth = 2.0 / notesCount;\n // Size of the note : width, height based on duration and current speed.\n vec2 noteSize = vec2(0.9 * noteWidth * minorsWidth, 2.0 * minorsHeight * keyboardHeight);\n // Compute note shift.\n // Horizontal shift based on note id, width of keyboard, and if the note is minor or not.\n //float a = (1.0/(notesCount-1.0)) * (2.0 - 2.0/notesCount);\n //float b = -1.0 + 1.0/notesCount;\n // This should be in -1.0, 1.0.\n // input: noteId is in [0 MAJOR_COUNT]\n // we want minNote to -1+1/c, maxNote to 1-1/c\n float a = 2.0;\n float b = -notesCount + 1.0 - 2.0 * float(minNoteMajor);\n int minorId = minorIds[gl_InstanceID];\n int noteId = (minorId/12) * 7 + noteDelta[minorId % 12];\n float horizLoc = (float(noteId) * a + b + 1.0) / notesCount;\n float vertLoc = 2.0 * (1.0 - minorsHeight) * keyboardHeight - 1.0 + noteSize.y * 0.5;\n vec2 noteShift = vec2(horizLoc, vertLoc);\n noteShift.x += minorShift(minorId % 12) * noteSize.x;\n // Output position.\n gl_Position = vec4(flipIfNeeded(noteSize * v + noteShift), 0.0 , 1.0) ;\n // Discard keys that are too close to the screen edges.\n if(abs(noteShift.x) >= 1.0 - 0.5 * noteWidth){\n gl_Position = vec4(-40000.0);\n }\n \n // Output the UV coordinates computed from the positions.\n Out.uv = v.xy + 0.5;\n // And the active channel if present.\n Out.id = float(actives[minorId]);\n \n }\n "}, +{ "minorKeys_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n float id;\n } In ;\n #define SETS_COUNT 8\n uniform bool highlightKeys;\n uniform bool horizontalMode = false;\n uniform float keyboardHeight = 0.25;\n uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1)\n uniform int minNoteMajor;\n uniform vec2 inverseScreenSize;\n uniform vec3 edgeColor = vec3(0.0);\n uniform vec3 minorColor[SETS_COUNT];\n uniform bool edgeOnMinors;\n uniform float minorsHeight = 0.6;\n uniform float minorsWidth = 1.0;\n vec2 minorShift(int id){\n if(id == 1 || id == 6){\n return vec2(0.0, 0.3);\n }\n if(id == 3 || id == 10){\n return vec2(0.3, 0.0);\n }\n return vec2(0.1,0.1);\n }\n out vec4 fragColor;\n void main(){\n // Center uvs\n vec2 ndc = 2.0 * In.uv - 1.0;\n float widthScaling = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x;\n float heightScaling = horizontalMode ? inverseScreenSize.x : inverseScreenSize.y;\n float uvWidth = minorsWidth / float(notesCount) * 0.5;\n float uvHeight = minorsHeight * keyboardHeight * 0.5;\n // Edges on the sides and bottom.\n float xStep = step(1.0 - 2.0 * widthScaling / uvWidth, abs(ndc.x));\n float yStep = step(1.0 - 2.0 * heightScaling / uvHeight, -ndc.y);\n float centerIntensity = edgeOnMinors ? ((1.0 - xStep) * (1.0 - yStep)) : 1.0;\n // Key color changes when active.\n int cidMinor = int(In.id);\n vec3 frontColor = (highlightKeys && cidMinor >= 0) ? minorColor[cidMinor] : edgeColor;\n // Mix\n fragColor.rgb = mix(edgeColor, frontColor, centerIntensity);\n fragColor.a = 1.0;\n }\n "}, { "backgroundtexture_vert", "#version 330\n layout(location = 0) in vec2 v;\n out INTERFACE {\n vec2 uv;\n } Out ;\n uniform bool behindKeyboard;\n uniform float keyboardHeight = 0.25;\n void main(){\n vec2 pos = v;\n if(!behindKeyboard){\n pos.y = (1.0-keyboardHeight) * pos.y + keyboardHeight;\n }\n // We directly output the position.\n gl_Position = vec4(pos, 0.0, 1.0);\n // Output the UV coordinates computed from the positions.\n Out.uv = v.xy * 0.5 + 0.5;\n \n }\n "}, { "backgroundtexture_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n uniform sampler2D screenTexture;\n uniform float textureAlpha;\n uniform bool behindKeyboard;\n out vec4 fragColor;\n void main(){\n fragColor = texture(screenTexture, In.uv);\n fragColor.a *= textureAlpha;\n }\n "}, { "pedal_vert", "#version 330\n layout(location = 0) in vec2 v;\n uniform vec2 shift;\n uniform vec2 scale;\n out INTERFACE {\n float id;\n } Out ;\n #define SOSTENUTO 33\n #define DAMPER 65\n #define SOFT 97\n #define EXPRESSION -1 damper, soft, expression\n void main(){\n // Translate to put on top of the keyboard.\n gl_Position = vec4(v.xy * scale + shift, 0.5, 1.0);\n // Detect which pedal this vertex belong to.\n Out.id = gl_VertexID < SOSTENUTO ? 0.0 :\n (gl_VertexID < DAMPER ? 1.0 :\n (gl_VertexID < SOFT ? 2.0 :\n 3.0\n ));\n \n }\n "}, From 34e7fe7fcf07b3c6625a7a5928aa953939e8b6a4 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Mon, 31 Jul 2023 23:50:25 +0200 Subject: [PATCH 5/9] Merge PR #130 --- resources/shaders/flashes.frag | 2 +- resources/shaders/majorKeys.frag | 2 +- resources/shaders/minorKeys.frag | 2 +- resources/shaders/notes.frag | 2 +- resources/shaders/particles.vert | 2 +- src/rendering/SetOptions.cpp | 2 +- src/rendering/SetOptions.h | 2 +- src/resources/shaders.cpp | 12 ++++++------ 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/resources/shaders/flashes.frag b/resources/shaders/flashes.frag index c22543b..6587520 100644 --- a/resources/shaders/flashes.frag +++ b/resources/shaders/flashes.frag @@ -1,5 +1,5 @@ #version 330 -#define SETS_COUNT 8 +#define SETS_COUNT 12 in INTERFACE { diff --git a/resources/shaders/majorKeys.frag b/resources/shaders/majorKeys.frag index c617eaf..0037ada 100644 --- a/resources/shaders/majorKeys.frag +++ b/resources/shaders/majorKeys.frag @@ -4,7 +4,7 @@ in INTERFACE { vec2 uv; } In ; -#define SETS_COUNT 8 +#define SETS_COUNT 12 #define MAJOR_COUNT 75 uniform bool highlightKeys; diff --git a/resources/shaders/minorKeys.frag b/resources/shaders/minorKeys.frag index 1e8cdca..97c1ce9 100644 --- a/resources/shaders/minorKeys.frag +++ b/resources/shaders/minorKeys.frag @@ -5,7 +5,7 @@ in INTERFACE { float id; } In ; -#define SETS_COUNT 8 +#define SETS_COUNT 12 uniform bool highlightKeys; uniform bool horizontalMode = false; diff --git a/resources/shaders/notes.frag b/resources/shaders/notes.frag index fd9b8b2..731e126 100644 --- a/resources/shaders/notes.frag +++ b/resources/shaders/notes.frag @@ -1,5 +1,5 @@ #version 330 -#define SETS_COUNT 8 +#define SETS_COUNT 12 in INTERFACE { vec2 uv; diff --git a/resources/shaders/particles.vert b/resources/shaders/particles.vert index 0faf451..7e84b2c 100644 --- a/resources/shaders/particles.vert +++ b/resources/shaders/particles.vert @@ -1,5 +1,5 @@ #version 330 -#define SETS_COUNT 8 +#define SETS_COUNT 12 layout(location = 0) in vec2 v; diff --git a/src/rendering/SetOptions.cpp b/src/rendering/SetOptions.cpp index a42588f..85e16b8 100755 --- a/src/rendering/SetOptions.cpp +++ b/src/rendering/SetOptions.cpp @@ -40,7 +40,7 @@ int SetOptions::apply(int note, int channel, int track, double start) const { case SetMode::SPLIT: return note < key ? 0 : 1; case SetMode::KEY: - return noteShift[note % 12] % SETS_COUNT; + return (note % 12) % SETS_COUNT; case SetMode::LIST: { // For each channel, find the corresponding key, and test diff --git a/src/rendering/SetOptions.h b/src/rendering/SetOptions.h index bc317bf..0edf738 100755 --- a/src/rendering/SetOptions.h +++ b/src/rendering/SetOptions.h @@ -5,7 +5,7 @@ #include #include -#define SETS_COUNT 8 +#define SETS_COUNT 12 enum class SetMode : int { CHANNEL = 0, diff --git a/src/resources/shaders.cpp b/src/resources/shaders.cpp index 28d0221..5b89da3 100644 --- a/src/resources/shaders.cpp +++ b/src/resources/shaders.cpp @@ -3,19 +3,19 @@ const std::unordered_map shaders = { { "background_vert", "#version 330\n layout(location = 0) in vec3 v;\n uniform bool horizontalMode = false;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n out INTERFACE {\n vec2 uv;\n } Out ;\n void main(){\n \n // We directly output the position.\n gl_Position = vec4(flipIfNeeded(v.xy), v.z, 1.0);\n // Output the UV coordinates computed from the positions.\n Out.uv = (v.xy) * 0.5 + 0.5;\n \n }\n "}, { "background_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n uniform float time;\n uniform float secondsPerMeasure;\n uniform vec2 inverseScreenSize;\n uniform bool useDigits = true;\n uniform bool useHLines = true;\n uniform bool useVLines = true;\n uniform float minorsWidth = 1.0;\n uniform sampler2D screenTexture;\n uniform vec3 textColor = vec3(1.0);\n uniform vec3 linesColor = vec3(1.0);\n uniform bool reverseMode = false;\n uniform bool horizontalMode = false;\n vec2 flipUVIfNeeded(vec2 inUV){\n vec2 shiftUV = inUV - 0.5;\n return horizontalMode ? vec2(shiftUV.y, -shiftUV.x) + 0.5 : inUV;\n }\n #define MAJOR_COUNT 75.0\n const float octaveLinesPositions[11] = float[](0.0/75.0, 7.0/75.0, 14.0/75.0, 21.0/75.0, 28.0/75.0, 35.0/75.0, 42.0/75.0, 49.0/75.0, 56.0/75.0, 63.0/75.0, 70.0/75.0);\n \n uniform float mainSpeed;\n uniform float keyboardHeight = 0.25;\n uniform int minNoteMajor;\n uniform float notesCount;\n out vec4 fragColor;\n float printDigit(int digit, vec2 uv){\n // Clamping to avoid artifacts.\n if(uv.x < 0.01 || uv.x > 0.99 || uv.y < 0.01 || uv.y > 0.99){\n return 0.0;\n }\n \n // UV from [0,1] to local tile frame.\n vec2 localUV = flipUVIfNeeded(uv) * vec2(50.0/256.0,0.5);\n // Select the digit.\n vec2 globalUV = vec2( mod(digit,5)*50.0/256.0,digit < 5 ? 0.5 : 0.0);\n // Combine global and local shifts.\n vec2 finalUV = globalUV + localUV;\n \n // Read from font atlas. Return if above a threshold.\n float isIn = texture(screenTexture, finalUV).r;\n return isIn < 0.5 ? 0.0 : isIn ;\n \n }\n float printNumber(float num, vec2 position, vec2 uv, vec2 scale){\n if(num < -0.1){\n return 0.0f;\n }\n if(position.y > 1.0 || position.y < 0.0){\n return 0.0;\n }\n \n // We limit to the [0,999] range.\n float number = min(999.0, max(0.0,num));\n \n // Extract digits.\n int hundredDigit = int(floor( number / 100.0 ));\n int tenDigit = int(floor( number / 10.0 - hundredDigit * 10.0));\n int unitDigit = int(floor( number - hundredDigit * 100.0 - tenDigit * 10.0));\n \n // Position of the text.\n vec2 initialPos = scale*(uv-position);\n \n // Get intensity for each digit at the current fragment.\n vec2 shift = horizontalMode ? vec2(0.0, scale.y) : vec2(scale.x, 0.0);\n shift *= 0.009;\n float off = horizontalMode ? 3.0 : 0.0;\n float hundred = printDigit(hundredDigit, initialPos + off * shift);\n float ten = printDigit(tenDigit, initialPos + (off - 1.0) * shift);\n float unit = printDigit(unitDigit, initialPos + (off - 2.0) * shift);\n \n // If hundred digit == 0, hide it.\n float hundredVisibility = (1.0-step(float(hundredDigit),0.5));\n hundred *= hundredVisibility;\n // If ten digit == 0 and hundred digit == 0, hide ten.\n float tenVisibility = max(hundredVisibility,(1.0-step(float(tenDigit),0.5)));\n ten*= tenVisibility;\n \n return hundred + ten + unit;\n }\n void main(){\n \n vec4 bgColor = vec4(0.0);\n vec2 inUV = In.uv;\n float xRatio = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x;\n float yRatio = horizontalMode ? inverseScreenSize.x : inverseScreenSize.y;\n // Octaves lines.\n if(useVLines){\n // send 0 to (minNote)/MAJOR_COUNT\n // send 1 to (maxNote)/MAJOR_COUNT\n float a = (notesCount) / MAJOR_COUNT;\n float b = float(minNoteMajor) / MAJOR_COUNT;\n float refPos = a * inUV.x + b;\n for(int i = 0; i < 11; i++){\n float linePos = octaveLinesPositions[i];\n float lineIntensity = 0.7 * step(abs(refPos - linePos), xRatio / MAJOR_COUNT * notesCount);\n bgColor = mix(bgColor, vec4(linesColor, 1.0), lineIntensity);\n }\n }\n float screenRatio = inverseScreenSize.x/inverseScreenSize.y;\n vec2 scale = 1.5 * vec2(64.0, 50.0 * screenRatio);\n if(horizontalMode){\n scale = scale.yx;\n }\n // Text on the side.\n int currentMesure = int(floor(time/secondsPerMeasure));\n // How many mesures do we check.\n int count = int(ceil(0.75*(2.0/mainSpeed)))+2;\n // We check two extra measures to avoid sudden disappearance below the keyboard.\n for(int i = -2; i < count; i++){\n // Compute position of the measure currentMesure+-i.\n int mesure = currentMesure + (reverseMode ? -1 : 1) * i;\n vec2 position = vec2(0.005, keyboardHeight + (reverseMode ? -1.0 : 1.0) * (secondsPerMeasure * mesure - time)*mainSpeed*0.5);\n // Compute color for the number display, and for the horizontal line.\n float numberIntensity = useDigits ? printNumber(mesure, position, inUV, scale) : 0.0;\n bgColor = mix(bgColor, vec4(textColor, 1.0), numberIntensity);\n float lineIntensity = useHLines ? (0.25*(step(abs(inUV.y - position.y - 0.5 / scale.y), yRatio))) : 0.0;\n bgColor = mix(bgColor, vec4(linesColor, 1.0), lineIntensity);\n }\n \n fragColor = bgColor;\n }\n "}, { "flashes_vert", "#version 330\n layout(location = 0) in vec2 v;\n layout(location = 1) in int onChan;\n uniform float time;\n uniform vec2 inverseScreenSize;\n uniform float userScale = 1.0;\n uniform float keyboardHeight = 0.25;\n uniform int minNote;\n uniform float notesCount;\n uniform bool horizontalMode = false;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n const float shifts[128] = float[](\n 0,0.5,1,1.5,2,3,3.5,4,4.5,5,5.5,6,7,7.5,8,8.5,9,10,10.5,11,11.5,12,12.5,13,14,14.5,15,15.5,16,17,17.5,18,18.5,19,19.5,20,21,21.5,22,22.5,23,24,24.5,25,25.5,26,26.5,27,28,28.5,29,29.5,30,31,31.5,32,32.5,33,33.5,34,35,35.5,36,36.5,37,38,38.5,39,39.5,40,40.5,41,42,42.5,43,43.5,44,45,45.5,46,46.5,47,47.5,48,49,49.5,50,50.5,51,52,52.5,53,53.5,54,54.5,55,56,56.5,57,57.5,58,59,59.5,60,60.5,61,61.5,62,63,63.5,64,64.5,65,66,66.5,67,67.5,68,68.5,69,70,70.5,71,71.5,72,73,73.5,74\n );\n const vec2 scale = 0.9*vec2(3.5,3.0);\n out INTERFACE {\n vec2 uv;\n float onChannel;\n float id;\n } Out;\n void main(){\n \n // Scale quad, keep the square ratio.\n float screenRatio = inverseScreenSize.y/inverseScreenSize.x;\n vec2 scalingFactor = vec2(1.0, horizontalMode ? (1.0/screenRatio) : screenRatio);\n vec2 scaledPosition = v * 2.0 * scale * userScale/notesCount * scalingFactor;\n // Shift based on note/flash id.\n vec2 globalShift = vec2(-1.0 + ((shifts[gl_InstanceID] - shifts[minNote]) * 2.0 + 1.0) / notesCount, 2.0 * keyboardHeight - 1.0);\n \n gl_Position = vec4(flipIfNeeded(scaledPosition + globalShift), 0.0 , 1.0) ;\n \n // Pass infos to the fragment shader.\n Out.uv = v;\n Out.onChannel = float(onChan);\n Out.id = float(gl_InstanceID);\n \n }\n "}, -{ "flashes_frag", "#version 330\n #define SETS_COUNT 8\n in INTERFACE {\n vec2 uv;\n float onChannel;\n float id;\n } In;\n uniform sampler2D textureFlash;\n uniform float time;\n uniform vec3 baseColor[SETS_COUNT];\n #define numberSprites 8.0\n out vec4 fragColor;\n float rand(vec2 co){\n return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n }\n void main(){\n \n // If not on, discard flash immediatly.\n int cid = int(In.onChannel);\n if(cid < 0){\n discard;\n }\n float mask = 0.0;\n \n // If up half, read from texture atlas.\n if(In.uv.y > 0.0){\n // Select a sprite, depending on time and flash id.\n float shift = floor(mod(15.0 * time, numberSprites)) + floor(rand(In.id * vec2(time,1.0)));\n vec2 globalUV = vec2(0.5 * mod(shift, 2.0), 0.25 * floor(shift/2.0));\n \n // Scale UV to fit in one sprite from atlas.\n vec2 localUV = In.uv * 0.5 + vec2(0.25,-0.25);\n localUV.y = min(-0.05,localUV.y); //Safety clamp on the upper side (or you could set clamp_t)\n \n // Read in black and white texture do determine opacity (mask).\n vec2 finalUV = globalUV + localUV;\n mask = texture(textureFlash,finalUV).r;\n }\n \n // Colored sprite.\n vec4 spriteColor = vec4(baseColor[cid], mask);\n \n // Circular halo effect.\n float haloAlpha = 1.0 - smoothstep(0.07,0.5,length(In.uv));\n vec4 haloColor = vec4(1.0,1.0,1.0, haloAlpha * 0.92);\n \n // Mix the sprite color and the halo effect.\n fragColor = mix(spriteColor, haloColor, haloColor.a);\n \n // Boost intensity.\n fragColor *= 1.1;\n // Premultiplied alpha.\n fragColor.rgb *= fragColor.a;\n }\n "}, -{ "notes_vert", "#version 330\n layout(location = 0) in vec2 v;\n layout(location = 1) in vec4 id; //note id, start, duration, is minor\n layout(location = 2) in float channel; //note id, start, duration, is minor\n uniform float time;\n uniform float mainSpeed;\n uniform float minorsWidth = 1.0;\n uniform float keyboardHeight = 0.25;\n uniform bool reverseMode = false;\n uniform bool horizontalMode = false;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n uniform int minNoteMajor;\n uniform float notesCount;\n out INTERFACE {\n vec2 uv;\n vec2 noteSize;\n float isMinor;\n float channel;\n } Out;\n #define MAJOR_COUNT 75\n const int minorIds[MAJOR_COUNT] = int[](1, 3, 0, 6, 8, 10, 0, 13, 15, 0, 18, 20, 22, 0, 25, 27, 0, 30, 32, 34, 0, 37, 39, 0, 42, 44, 46, 0, 49, 51, 0, 54, 56, 58, 0, 61, 63, 0, 66, 68, 70, 0, 73, 75, 0, 78, 80, 82, 0, 85, 87, 0, 90, 92, 94, 0, 97, 99, 0, 102, 104, 106, 0, 109, 111, 0, 114, 116, 118, 0, 121, 123, 0, 126, 0);\n float minorShift(int id){\n if(id == 1 || id == 6){\n return -0.1;\n }\n if(id == 3 || id == 10){\n return 0.1;\n }\n return 0.0;\n }\n void main(){\n \n float scalingFactor = id.w != 0.0 ? 0.9*minorsWidth : 1.0;\n // Size of the note : width, height based on duration and current speed.\n Out.noteSize = vec2(0.9*2.0/notesCount * scalingFactor, id.z*mainSpeed);\n \n // Compute note shift.\n // Horizontal shift based on note id, width of keyboard, and if the note is minor or not.\n // Vertical shift based on note start time, current time, speed, and height of the note quad.\n //float a = (1.0/(notesCount-1.0)) * (2.0 - 2.0/notesCount);\n //float b = -1.0 + 1.0/notesCount;\n // This should be in -1.0, 1.0.\n // input: id.x is in [0 MAJOR_COUNT]\n // we want minNote to -1+1/c, maxNote to 1-1/c\n float a = 2.0;\n float b = -notesCount + 1.0 - 2.0 * float(minNoteMajor);\n float horizLoc = (id.x * a + b + id.w) / notesCount;\n float vertLoc = 2.0 * keyboardHeight - 1.0;\n vertLoc += (reverseMode ? -1.0 : 1.0) * (Out.noteSize.y * 0.5 + mainSpeed * (id.y - time));\n vec2 noteShift = vec2(horizLoc, vertLoc);\n noteShift.x += id.w * minorShift(minorIds[int(id.x)] % 12) * Out.noteSize.x;\n // Scale uv.\n Out.uv = Out.noteSize * v;\n Out.isMinor = id.w;\n Out.channel = channel;\n // Output position.\n gl_Position = vec4(flipIfNeeded(Out.noteSize * v + noteShift), 0.0 , 1.0) ;\n \n }\n "}, -{ "notes_frag", "#version 330\n #define SETS_COUNT 8\n in INTERFACE {\n vec2 uv;\n vec2 noteSize;\n float isMinor;\n float channel;\n } In;\n uniform vec3 baseColor[SETS_COUNT];\n uniform vec3 minorColor[SETS_COUNT];\n uniform vec2 inverseScreenSize;\n uniform float colorScale;\n uniform float keyboardHeight = 0.25;\n uniform float fadeOut = 0.0;\n uniform bool horizontalMode = false;\n #define cornerRadius 0.01\n out vec4 fragColor;\n void main(){\n \n // If lower area of the screen, discard fragment as it should be hidden behind the keyboard.\n vec2 normalizedCoord = vec2(gl_FragCoord.xy) * inverseScreenSize;\n if((horizontalMode ? normalizedCoord.x : normalizedCoord.y) < keyboardHeight){\n discard;\n }\n \n // Rounded corner (super-ellipse equation).\n float radiusPosition = pow(abs(In.uv.x/(0.5*In.noteSize.x)), In.noteSize.x/cornerRadius) + pow(abs(In.uv.y/(0.5*In.noteSize.y)), In.noteSize.y/cornerRadius);\n \n if( radiusPosition > 1.0){\n discard;\n }\n \n // Fragment color.\n int cid = int(In.channel);\n fragColor.rgb = colorScale * mix(baseColor[cid], minorColor[cid], In.isMinor);\n \n if( radiusPosition > 0.8){\n fragColor.rgb *= 1.05;\n }\n float distFromBottom = horizontalMode ? normalizedCoord.x : normalizedCoord.y;\n float fadeOutFinal = min(fadeOut, 0.9999);\n distFromBottom = max(distFromBottom - fadeOutFinal, 0.0) / (1.0 - fadeOutFinal);\n float alpha = 1.0 - distFromBottom;\n fragColor.a = alpha;\n }\n "}, -{ "particles_vert", "#version 330\n #define SETS_COUNT 8\n layout(location = 0) in vec2 v;\n uniform float time;\n uniform float scale;\n uniform vec3 baseColor[SETS_COUNT];\n uniform vec2 inverseScreenSize;\n uniform sampler2D textureParticles;\n uniform vec2 inverseTextureSize;\n uniform int globalId;\n uniform float duration;\n uniform int channel;\n uniform int texCount;\n uniform float colorScale;\n uniform float expansionFactor = 1.0;\n uniform float speedScaling = 0.2;\n uniform float keyboardHeight = 0.25;\n uniform int minNote;\n uniform float notesCount;\n uniform bool horizontalMode = false;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n const float shifts[128] = float[](\n 0,0.5,1,1.5,2,3,3.5,4,4.5,5,5.5,6,7,7.5,8,8.5,9,10,10.5,11,11.5,12,12.5,13,14,14.5,15,15.5,16,17,17.5,18,18.5,19,19.5,20,21,21.5,22,22.5,23,24,24.5,25,25.5,26,26.5,27,28,28.5,29,29.5,30,31,31.5,32,32.5,33,33.5,34,35,35.5,36,36.5,37,38,38.5,39,39.5,40,40.5,41,42,42.5,43,43.5,44,45,45.5,46,46.5,47,47.5,48,49,49.5,50,50.5,51,52,52.5,53,53.5,54,54.5,55,56,56.5,57,57.5,58,59,59.5,60,60.5,61,61.5,62,63,63.5,64,64.5,65,66,66.5,67,67.5,68,68.5,69,70,70.5,71,71.5,72,73,73.5,74\n );\n out INTERFACE {\n vec4 color;\n vec2 uv;\n float id;\n } Out;\n float rand(vec2 co){\n return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n }\n void main(){\n Out.id = float(gl_InstanceID % texCount);\n Out.uv = v + 0.5;\n // Fade color based on time.\n Out.color = vec4(colorScale * baseColor[channel], 1.0-time*time);\n \n float localTime = speedScaling * time * duration;\n float particlesCount = 1.0/inverseTextureSize.y;\n \n // Pick particle id at random.\n float particleId = float(gl_InstanceID) + floor(particlesCount * 10.0 * rand(vec2(globalId,globalId)));\n float textureId = mod(particleId,particlesCount);\n float particleShift = floor(particleId/particlesCount);\n \n // Particle uv, in pixels.\n vec2 particleUV = vec2(localTime / inverseTextureSize.x + 10.0 * particleShift, textureId);\n // UV in [0,1]\n particleUV = (particleUV+0.5)*vec2(1.0,-1.0)*inverseTextureSize;\n // Avoid wrapping.\n particleUV.x = clamp(particleUV.x,0.0,1.0);\n // We want to skip reading from the very beginning of the trajectories because they are identical.\n // particleUV.x = 0.95 * particleUV.x + 0.05;\n // Read corresponding trajectory to get particle current position.\n vec3 position = texture(textureParticles, particleUV).xyz;\n // Center position (from [0,1] to [-0.5,0.5] on x axis.\n position.x -= 0.5;\n \n // Compute shift, randomly disturb it.\n vec2 shift = 0.5*position.xy;\n float random = rand(vec2(particleId + float(globalId),time*0.000002+100.0*float(globalId)));\n shift += vec2(0.0,0.1*random);\n \n // Scale shift with time (expansion effect).\n shift = shift*time*expansionFactor;\n // and with altitude of the particle (ditto).\n shift.x *= max(0.5, pow(shift.y,0.3));\n \n // Horizontal shift is based on the note ID.\n float xshift = -1.0 + ((shifts[globalId] - shifts[int(minNote)]) * 2.0 + 1.0) / notesCount;\n // Combine global shift (due to note id) and local shift (based on read position).\n vec2 globalShift = vec2(xshift, (2.0 * keyboardHeight - 1.0)-0.02);\n vec2 localShift = 0.003 * scale * v + shift * duration * vec2(1.0,0.5);\n float screenRatio = inverseScreenSize.y/inverseScreenSize.x;\n vec2 screenScaling = vec2(1.0, horizontalMode ? (1.0/screenRatio) : screenRatio);\n vec2 finalPos = globalShift + screenScaling * localShift;\n \n // Discard particles that reached the end of their trajectories by putting them off-screen.\n finalPos = mix(vec2(-200.0),finalPos, position.z);\n // Output final particle position.\n gl_Position = vec4(flipIfNeeded(finalPos), 0.0, 1.0);\n \n \n }\n "}, +{ "flashes_frag", "#version 330\n #define SETS_COUNT 12\n in INTERFACE {\n vec2 uv;\n float onChannel;\n float id;\n } In;\n uniform sampler2D textureFlash;\n uniform float time;\n uniform vec3 baseColor[SETS_COUNT];\n #define numberSprites 8.0\n out vec4 fragColor;\n float rand(vec2 co){\n return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n }\n void main(){\n \n // If not on, discard flash immediatly.\n int cid = int(In.onChannel);\n if(cid < 0){\n discard;\n }\n float mask = 0.0;\n \n // If up half, read from texture atlas.\n if(In.uv.y > 0.0){\n // Select a sprite, depending on time and flash id.\n float shift = floor(mod(15.0 * time, numberSprites)) + floor(rand(In.id * vec2(time,1.0)));\n vec2 globalUV = vec2(0.5 * mod(shift, 2.0), 0.25 * floor(shift/2.0));\n \n // Scale UV to fit in one sprite from atlas.\n vec2 localUV = In.uv * 0.5 + vec2(0.25,-0.25);\n localUV.y = min(-0.05,localUV.y); //Safety clamp on the upper side (or you could set clamp_t)\n \n // Read in black and white texture do determine opacity (mask).\n vec2 finalUV = globalUV + localUV;\n mask = texture(textureFlash,finalUV).r;\n }\n \n // Colored sprite.\n vec4 spriteColor = vec4(baseColor[cid], mask);\n \n // Circular halo effect.\n float haloAlpha = 1.0 - smoothstep(0.07,0.5,length(In.uv));\n vec4 haloColor = vec4(1.0,1.0,1.0, haloAlpha * 0.92);\n \n // Mix the sprite color and the halo effect.\n fragColor = mix(spriteColor, haloColor, haloColor.a);\n \n // Boost intensity.\n fragColor *= 1.1;\n // Premultiplied alpha.\n fragColor.rgb *= fragColor.a;\n }\n "}, +{ "notes_vert", "#version 330\n layout(location = 0) in vec2 v;\n layout(location = 1) in vec4 id; //note id, start, duration, is minor\n layout(location = 2) in float channel; //note id, start, duration, is minor\n uniform float time;\n uniform float mainSpeed;\n uniform float minorsWidth = 1.0;\n uniform float keyboardHeight = 0.25;\n uniform bool reverseMode = false;\n uniform bool horizontalMode = false;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n uniform int minNoteMajor;\n uniform float notesCount;\n out INTERFACE {\n vec2 uv;\n vec2 noteSize;\n float isMinor;\n float channel;\n } Out;\n #define MAJOR_COUNT 75\n const int minorIds[MAJOR_COUNT] = int[](1, 3, 0, 6, 8, 10, 0, 13, 15, 0, 18, 20, 22, 0, 25, 27, 0, 30, 32, 34, 0, 37, 39, 0, 42, 44, 46, 0, 49, 51, 0, 54, 56, 58, 0, 61, 63, 0, 66, 68, 70, 0, 73, 75, 0, 78, 80, 82, 0, 85, 87, 0, 90, 92, 94, 0, 97, 99, 0, 102, 104, 106, 0, 109, 111, 0, 114, 116, 118, 0, 121, 123, 0, 126, 0);\n float minorShift(int id){\n if(id == 1 || id == 6){\n return -0.1;\n }\n if(id == 3 || id == 10){\n return 0.1;\n }\n return 0.0;\n }\n void main(){\n \n float scalingFactor = id.w != 0.0 ? minorsWidth : 1.0;\n // Size of the note : width, height based on duration and current speed.\n Out.noteSize = vec2(0.9*2.0/notesCount * scalingFactor, id.z*mainSpeed);\n \n // Compute note shift.\n // Horizontal shift based on note id, width of keyboard, and if the note is minor or not.\n // Vertical shift based on note start time, current time, speed, and height of the note quad.\n //float a = (1.0/(notesCount-1.0)) * (2.0 - 2.0/notesCount);\n //float b = -1.0 + 1.0/notesCount;\n // This should be in -1.0, 1.0.\n // input: id.x is in [0 MAJOR_COUNT]\n // we want minNote to -1+1/c, maxNote to 1-1/c\n float a = 2.0;\n float b = -notesCount + 1.0 - 2.0 * float(minNoteMajor);\n float horizLoc = (id.x * a + b + id.w) / notesCount;\n float vertLoc = 2.0 * keyboardHeight - 1.0;\n vertLoc += (reverseMode ? -1.0 : 1.0) * (Out.noteSize.y * 0.5 + mainSpeed * (id.y - time));\n vec2 noteShift = vec2(horizLoc, vertLoc);\n noteShift.x += id.w * minorShift(minorIds[int(id.x)] % 12) * Out.noteSize.x;\n // Scale uv.\n Out.uv = Out.noteSize * v;\n Out.isMinor = id.w;\n Out.channel = channel;\n // Output position.\n gl_Position = vec4(flipIfNeeded(Out.noteSize * v + noteShift), 0.0 , 1.0) ;\n \n }\n "}, +{ "notes_frag", "#version 330\n #define SETS_COUNT 12\n in INTERFACE {\n vec2 uv;\n vec2 noteSize;\n float isMinor;\n float channel;\n } In;\n uniform vec3 baseColor[SETS_COUNT];\n uniform vec3 minorColor[SETS_COUNT];\n uniform vec2 inverseScreenSize;\n uniform float colorScale;\n uniform float keyboardHeight = 0.25;\n uniform float fadeOut = 0.0;\n uniform bool horizontalMode = false;\n #define cornerRadius 0.01\n out vec4 fragColor;\n void main(){\n \n // If lower area of the screen, discard fragment as it should be hidden behind the keyboard.\n vec2 normalizedCoord = vec2(gl_FragCoord.xy) * inverseScreenSize;\n if((horizontalMode ? normalizedCoord.x : normalizedCoord.y) < keyboardHeight){\n discard;\n }\n \n // Rounded corner (super-ellipse equation).\n float radiusPosition = pow(abs(In.uv.x/(0.5*In.noteSize.x)), In.noteSize.x/cornerRadius) + pow(abs(In.uv.y/(0.5*In.noteSize.y)), In.noteSize.y/cornerRadius);\n \n if( radiusPosition > 1.0){\n discard;\n }\n \n // Fragment color.\n int cid = int(In.channel);\n fragColor.rgb = colorScale * mix(baseColor[cid], minorColor[cid], In.isMinor);\n \n if( radiusPosition > 0.8){\n fragColor.rgb *= 1.05;\n }\n float distFromBottom = horizontalMode ? normalizedCoord.x : normalizedCoord.y;\n float fadeOutFinal = min(fadeOut, 0.9999);\n distFromBottom = max(distFromBottom - fadeOutFinal, 0.0) / (1.0 - fadeOutFinal);\n float alpha = 1.0 - distFromBottom;\n fragColor.a = alpha;\n }\n "}, +{ "particles_vert", "#version 330\n #define SETS_COUNT 12\n layout(location = 0) in vec2 v;\n uniform float time;\n uniform float scale;\n uniform vec3 baseColor[SETS_COUNT];\n uniform vec2 inverseScreenSize;\n uniform sampler2D textureParticles;\n uniform vec2 inverseTextureSize;\n uniform int globalId;\n uniform float duration;\n uniform int channel;\n uniform int texCount;\n uniform float colorScale;\n uniform float expansionFactor = 1.0;\n uniform float speedScaling = 0.2;\n uniform float keyboardHeight = 0.25;\n uniform int minNote;\n uniform float notesCount;\n uniform bool horizontalMode = false;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n const float shifts[128] = float[](\n 0,0.5,1,1.5,2,3,3.5,4,4.5,5,5.5,6,7,7.5,8,8.5,9,10,10.5,11,11.5,12,12.5,13,14,14.5,15,15.5,16,17,17.5,18,18.5,19,19.5,20,21,21.5,22,22.5,23,24,24.5,25,25.5,26,26.5,27,28,28.5,29,29.5,30,31,31.5,32,32.5,33,33.5,34,35,35.5,36,36.5,37,38,38.5,39,39.5,40,40.5,41,42,42.5,43,43.5,44,45,45.5,46,46.5,47,47.5,48,49,49.5,50,50.5,51,52,52.5,53,53.5,54,54.5,55,56,56.5,57,57.5,58,59,59.5,60,60.5,61,61.5,62,63,63.5,64,64.5,65,66,66.5,67,67.5,68,68.5,69,70,70.5,71,71.5,72,73,73.5,74\n );\n out INTERFACE {\n vec4 color;\n vec2 uv;\n float id;\n } Out;\n float rand(vec2 co){\n return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n }\n void main(){\n Out.id = float(gl_InstanceID % texCount);\n Out.uv = v + 0.5;\n // Fade color based on time.\n Out.color = vec4(colorScale * baseColor[channel], 1.0-time*time);\n \n float localTime = speedScaling * time * duration;\n float particlesCount = 1.0/inverseTextureSize.y;\n \n // Pick particle id at random.\n float particleId = float(gl_InstanceID) + floor(particlesCount * 10.0 * rand(vec2(globalId,globalId)));\n float textureId = mod(particleId,particlesCount);\n float particleShift = floor(particleId/particlesCount);\n \n // Particle uv, in pixels.\n vec2 particleUV = vec2(localTime / inverseTextureSize.x + 10.0 * particleShift, textureId);\n // UV in [0,1]\n particleUV = (particleUV+0.5)*vec2(1.0,-1.0)*inverseTextureSize;\n // Avoid wrapping.\n particleUV.x = clamp(particleUV.x,0.0,1.0);\n // We want to skip reading from the very beginning of the trajectories because they are identical.\n // particleUV.x = 0.95 * particleUV.x + 0.05;\n // Read corresponding trajectory to get particle current position.\n vec3 position = texture(textureParticles, particleUV).xyz;\n // Center position (from [0,1] to [-0.5,0.5] on x axis.\n position.x -= 0.5;\n \n // Compute shift, randomly disturb it.\n vec2 shift = 0.5*position.xy;\n float random = rand(vec2(particleId + float(globalId),time*0.000002+100.0*float(globalId)));\n shift += vec2(0.0,0.1*random);\n \n // Scale shift with time (expansion effect).\n shift = shift*time*expansionFactor;\n // and with altitude of the particle (ditto).\n shift.x *= max(0.5, pow(shift.y,0.3));\n \n // Horizontal shift is based on the note ID.\n float xshift = -1.0 + ((shifts[globalId] - shifts[int(minNote)]) * 2.0 + 1.0) / notesCount;\n // Combine global shift (due to note id) and local shift (based on read position).\n vec2 globalShift = vec2(xshift, (2.0 * keyboardHeight - 1.0)-0.02);\n vec2 localShift = 0.003 * scale * v + shift * duration * vec2(1.0,0.5);\n float screenRatio = inverseScreenSize.y/inverseScreenSize.x;\n vec2 screenScaling = vec2(1.0, horizontalMode ? (1.0/screenRatio) : screenRatio);\n vec2 finalPos = globalShift + screenScaling * localShift;\n \n // Discard particles that reached the end of their trajectories by putting them off-screen.\n finalPos = mix(vec2(-200.0),finalPos, position.z);\n // Output final particle position.\n gl_Position = vec4(flipIfNeeded(finalPos), 0.0, 1.0);\n \n \n }\n "}, { "particles_frag", "#version 330\n in INTERFACE {\n vec4 color;\n vec2 uv;\n float id;\n } In;\n uniform sampler2DArray lookParticles;\n out vec4 fragColor;\n void main(){\n float alpha = texture(lookParticles, vec3(In.uv, In.id)).r;\n fragColor = In.color;\n fragColor.a *= alpha;\n }\n "}, { "particlesblur_vert", "#version 330\n layout(location = 0) in vec3 v;\n out INTERFACE {\n vec2 uv;\n } Out ;\n void main(){\n \n // We directly output the position.\n gl_Position = vec4(v, 1.0);\n // Output the UV coordinates computed from the positions.\n Out.uv = v.xy * 0.5 + 0.5;\n \n }\n "}, { "particlesblur_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n uniform sampler2D screenTexture;\n uniform vec2 inverseScreenSize;\n uniform vec3 backgroundColor = vec3(0.0);\n uniform float attenuationFactor = 0.99;\n uniform float time;\n out vec4 fragColor;\n vec4 blur(vec2 uv, bool vert){\n vec4 color = 0.2270270270 * texture(screenTexture, uv);\n vec2 pixelOffset = vert ? vec2(0.0, inverseScreenSize.y) : vec2(inverseScreenSize.x, 0.0);\n vec2 texCoordOffset0 = 1.3846153846 * pixelOffset;\n vec4 col0 = texture(screenTexture, uv + texCoordOffset0) + texture(screenTexture, uv - texCoordOffset0);\n color += 0.3162162162 * col0;\n vec2 texCoordOffset1 = 3.2307692308 * pixelOffset;\n vec4 col1 = texture(screenTexture, uv + texCoordOffset1) + texture(screenTexture, uv - texCoordOffset1);\n color += 0.0702702703 * col1;\n return color;\n }\n void main(){\n \n // Gaussian blur separated in two 1D convolutions, relying on bilinear interpolation to\n // sample multiple pixels at once with the proper weights.\n vec4 color = blur(In.uv, time > 0.5);\n // Include decay for fade out.\n fragColor = mix(vec4(backgroundColor, 0.0), color, attenuationFactor);\n \n }\n "}, { "screenquad_vert", "#version 330\n layout(location = 0) in vec3 v;\n out INTERFACE {\n vec2 uv;\n } Out ;\n void main(){\n \n // We directly output the position.\n gl_Position = vec4(v, 1.0);\n // Output the UV coordinates computed from the positions.\n Out.uv = v.xy * 0.5 + 0.5;\n \n }\n "}, { "screenquad_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n uniform sampler2D screenTexture;\n uniform vec2 inverseScreenSize;\n out vec4 fragColor;\n void main(){\n \n fragColor = texture(screenTexture,In.uv);\n \n }\n "}, { "majorKeys_vert", "#version 330\n layout(location = 0) in vec2 v;\n out INTERFACE {\n vec2 uv;\n } Out ;\n uniform bool horizontalMode = false;\n uniform float keyboardHeight = 0.25;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n void main(){\n // Input are in -0.5,0.5\n // We directly output the position.\n // [-0.5, 0.5] to [-1, -1+2.0*keyboardHeight]\n float yShift = keyboardHeight * (2.0 * v.y + 1.0) - 1.0;\n vec2 pos2D = vec2(v.x * 2.0, yShift);\n gl_Position.xy = flipIfNeeded(pos2D);\n gl_Position.zw = vec2(0.0, 1.0);\n // Output the UV coordinates computed from the positions.\n Out.uv = v.xy + 0.5;\n \n }\n "}, -{ "majorKeys_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n #define SETS_COUNT 8\n #define MAJOR_COUNT 75\n uniform bool highlightKeys;\n uniform bool horizontalMode = false;\n uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1)\n uniform int actives[128];\n uniform int minNoteMajor;\n uniform vec2 inverseScreenSize;\n uniform vec3 edgeColor = vec3(0.0);\n uniform vec3 majorColor[SETS_COUNT];\n const int majorIds[MAJOR_COUNT] = int[](0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24, 26, 28, 29, 31, 33, 35, 36, 38, 40, 41, 43, 45, 47, 48, 50, 52, 53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83, 84, 86, 88, 89, 91, 93, 95, 96, 98, 100, 101, 103, 105, 107, 108, 110, 112, 113, 115, 117, 119, 120, 122, 124, 125, 127);\n out vec4 fragColor;\n void main(){\n // White keys, and separators.\n float widthScaling = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x;\n float majorUvX = fract(In.uv.x * notesCount);\n // Edges on the sides\n float centerIntensity = 1.0 - step( 1.0 - 2.0 * notesCount * widthScaling, abs(majorUvX * 2.0 - 1.0));\n // If the current major key is active, the majorColor is specific.\n int majorId = majorIds[clamp(int(In.uv.x * notesCount) + minNoteMajor, 0, 74)];\n int cidMajor = actives[majorId];\n vec3 frontColor = (highlightKeys && cidMajor >= 0) ? majorColor[cidMajor] : vec3(1.0);\n // Mix.\n fragColor.rgb = mix(edgeColor, frontColor, centerIntensity);\n fragColor.a = 1.0;\n }\n "}, +{ "majorKeys_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n #define SETS_COUNT 12\n #define MAJOR_COUNT 75\n uniform bool highlightKeys;\n uniform bool horizontalMode = false;\n uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1)\n uniform int actives[128];\n uniform int minNoteMajor;\n uniform vec2 inverseScreenSize;\n uniform vec3 edgeColor = vec3(0.0);\n uniform vec3 majorColor[SETS_COUNT];\n const int majorIds[MAJOR_COUNT] = int[](0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24, 26, 28, 29, 31, 33, 35, 36, 38, 40, 41, 43, 45, 47, 48, 50, 52, 53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83, 84, 86, 88, 89, 91, 93, 95, 96, 98, 100, 101, 103, 105, 107, 108, 110, 112, 113, 115, 117, 119, 120, 122, 124, 125, 127);\n out vec4 fragColor;\n void main(){\n // White keys, and separators.\n float widthScaling = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x;\n float majorUvX = fract(In.uv.x * notesCount);\n // Edges on the sides\n float centerIntensity = 1.0 - step( 1.0 - 2.0 * notesCount * widthScaling, abs(majorUvX * 2.0 - 1.0));\n // If the current major key is active, the majorColor is specific.\n int majorId = majorIds[clamp(int(In.uv.x * notesCount) + minNoteMajor, 0, 74)];\n int cidMajor = actives[majorId];\n vec3 frontColor = (highlightKeys && cidMajor >= 0) ? majorColor[cidMajor] : vec3(1.0);\n // Mix.\n fragColor.rgb = mix(edgeColor, frontColor, centerIntensity);\n fragColor.a = 1.0;\n }\n "}, { "minorKeys_vert", "#version 330\n layout(location = 0) in vec2 v;\n out INTERFACE {\n vec2 uv;\n float id;\n } Out ;\n uniform bool horizontalMode = false;\n uniform float keyboardHeight = 0.25;\n uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1)\n uniform int actives[128];\n uniform int minNoteMajor;\n uniform float minorsHeight = 0.6;\n uniform float minorsWidth = 1.0;\n vec2 flipIfNeeded(vec2 inPos){\n return horizontalMode ? vec2(inPos.y, -inPos.x) : inPos;\n }\n #define MINOR_COUNT 53\n const int minorIds[MINOR_COUNT] = int[](1, 3, 6, 8, 10, 13, 15, 18, 20, 22, 25, 27, 30, 32, 34, 37, 39, 42, 44, 46, 49, 51, 54, 56, 58, 61, 63, 66, 68, 70, 73, 75, 78, 80, 82, 85, 87, 90, 92, 94, 97, 99, 102, 104, 106, 109, 111, 114, 116, 118, 121, 123, 126);\n const int noteDelta[12] = int[](0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6);\n float minorShift(int id){\n if(id == 1 || id == 6){\n return -0.1;\n }\n if(id == 3 || id == 10){\n return 0.1;\n }\n return 0.0;\n }\n void main(){\n float noteWidth = 2.0 / notesCount;\n // Size of the note : width, height based on duration and current speed.\n vec2 noteSize = vec2(0.9 * noteWidth * minorsWidth, 2.0 * minorsHeight * keyboardHeight);\n // Compute note shift.\n // Horizontal shift based on note id, width of keyboard, and if the note is minor or not.\n //float a = (1.0/(notesCount-1.0)) * (2.0 - 2.0/notesCount);\n //float b = -1.0 + 1.0/notesCount;\n // This should be in -1.0, 1.0.\n // input: noteId is in [0 MAJOR_COUNT]\n // we want minNote to -1+1/c, maxNote to 1-1/c\n float a = 2.0;\n float b = -notesCount + 1.0 - 2.0 * float(minNoteMajor);\n int minorId = minorIds[gl_InstanceID];\n int noteId = (minorId/12) * 7 + noteDelta[minorId % 12];\n float horizLoc = (float(noteId) * a + b + 1.0) / notesCount;\n float vertLoc = 2.0 * (1.0 - minorsHeight) * keyboardHeight - 1.0 + noteSize.y * 0.5;\n vec2 noteShift = vec2(horizLoc, vertLoc);\n noteShift.x += minorShift(minorId % 12) * noteSize.x;\n // Output position.\n gl_Position = vec4(flipIfNeeded(noteSize * v + noteShift), 0.0 , 1.0) ;\n // Discard keys that are too close to the screen edges.\n if(abs(noteShift.x) >= 1.0 - 0.5 * noteWidth){\n gl_Position = vec4(-40000.0);\n }\n \n // Output the UV coordinates computed from the positions.\n Out.uv = v.xy + 0.5;\n // And the active channel if present.\n Out.id = float(actives[minorId]);\n \n }\n "}, -{ "minorKeys_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n float id;\n } In ;\n #define SETS_COUNT 8\n uniform bool highlightKeys;\n uniform bool horizontalMode = false;\n uniform float keyboardHeight = 0.25;\n uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1)\n uniform int minNoteMajor;\n uniform vec2 inverseScreenSize;\n uniform vec3 edgeColor = vec3(0.0);\n uniform vec3 minorColor[SETS_COUNT];\n uniform bool edgeOnMinors;\n uniform float minorsHeight = 0.6;\n uniform float minorsWidth = 1.0;\n vec2 minorShift(int id){\n if(id == 1 || id == 6){\n return vec2(0.0, 0.3);\n }\n if(id == 3 || id == 10){\n return vec2(0.3, 0.0);\n }\n return vec2(0.1,0.1);\n }\n out vec4 fragColor;\n void main(){\n // Center uvs\n vec2 ndc = 2.0 * In.uv - 1.0;\n float widthScaling = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x;\n float heightScaling = horizontalMode ? inverseScreenSize.x : inverseScreenSize.y;\n float uvWidth = minorsWidth / float(notesCount) * 0.5;\n float uvHeight = minorsHeight * keyboardHeight * 0.5;\n // Edges on the sides and bottom.\n float xStep = step(1.0 - 2.0 * widthScaling / uvWidth, abs(ndc.x));\n float yStep = step(1.0 - 2.0 * heightScaling / uvHeight, -ndc.y);\n float centerIntensity = edgeOnMinors ? ((1.0 - xStep) * (1.0 - yStep)) : 1.0;\n // Key color changes when active.\n int cidMinor = int(In.id);\n vec3 frontColor = (highlightKeys && cidMinor >= 0) ? minorColor[cidMinor] : edgeColor;\n // Mix\n fragColor.rgb = mix(edgeColor, frontColor, centerIntensity);\n fragColor.a = 1.0;\n }\n "}, +{ "minorKeys_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n float id;\n } In ;\n #define SETS_COUNT 12\n uniform bool highlightKeys;\n uniform bool horizontalMode = false;\n uniform float keyboardHeight = 0.25;\n uniform float notesCount; // (maxNoteMajor - minNoteMajor + 1)\n uniform int minNoteMajor;\n uniform vec2 inverseScreenSize;\n uniform vec3 edgeColor = vec3(0.0);\n uniform vec3 minorColor[SETS_COUNT];\n uniform bool edgeOnMinors;\n uniform float minorsHeight = 0.6;\n uniform float minorsWidth = 1.0;\n vec2 minorShift(int id){\n if(id == 1 || id == 6){\n return vec2(0.0, 0.3);\n }\n if(id == 3 || id == 10){\n return vec2(0.3, 0.0);\n }\n return vec2(0.1,0.1);\n }\n out vec4 fragColor;\n void main(){\n // Center uvs\n vec2 ndc = 2.0 * In.uv - 1.0;\n float widthScaling = horizontalMode ? inverseScreenSize.y : inverseScreenSize.x;\n float heightScaling = horizontalMode ? inverseScreenSize.x : inverseScreenSize.y;\n float uvWidth = minorsWidth / float(notesCount) * 0.5;\n float uvHeight = minorsHeight * keyboardHeight * 0.5;\n // Edges on the sides and bottom.\n float xStep = step(1.0 - 2.0 * widthScaling / uvWidth, abs(ndc.x));\n float yStep = step(1.0 - 2.0 * heightScaling / uvHeight, -ndc.y);\n float centerIntensity = edgeOnMinors ? ((1.0 - xStep) * (1.0 - yStep)) : 1.0;\n // Key color changes when active.\n int cidMinor = int(In.id);\n vec3 frontColor = (highlightKeys && cidMinor >= 0) ? minorColor[cidMinor] : edgeColor;\n // Mix\n fragColor.rgb = mix(edgeColor, frontColor, centerIntensity);\n fragColor.a = 1.0;\n }\n "}, { "backgroundtexture_vert", "#version 330\n layout(location = 0) in vec2 v;\n out INTERFACE {\n vec2 uv;\n } Out ;\n uniform bool behindKeyboard;\n uniform float keyboardHeight = 0.25;\n void main(){\n vec2 pos = v;\n if(!behindKeyboard){\n pos.y = (1.0-keyboardHeight) * pos.y + keyboardHeight;\n }\n // We directly output the position.\n gl_Position = vec4(pos, 0.0, 1.0);\n // Output the UV coordinates computed from the positions.\n Out.uv = v.xy * 0.5 + 0.5;\n \n }\n "}, { "backgroundtexture_frag", "#version 330\n in INTERFACE {\n vec2 uv;\n } In ;\n uniform sampler2D screenTexture;\n uniform float textureAlpha;\n uniform bool behindKeyboard;\n out vec4 fragColor;\n void main(){\n fragColor = texture(screenTexture, In.uv);\n fragColor.a *= textureAlpha;\n }\n "}, { "pedal_vert", "#version 330\n layout(location = 0) in vec2 v;\n uniform vec2 shift;\n uniform vec2 scale;\n out INTERFACE {\n float id;\n } Out ;\n #define SOSTENUTO 33\n #define DAMPER 65\n #define SOFT 97\n #define EXPRESSION -1 damper, soft, expression\n void main(){\n // Translate to put on top of the keyboard.\n gl_Position = vec4(v.xy * scale + shift, 0.5, 1.0);\n // Detect which pedal this vertex belong to.\n Out.id = gl_VertexID < SOSTENUTO ? 0.0 :\n (gl_VertexID < DAMPER ? 1.0 :\n (gl_VertexID < SOFT ? 2.0 :\n 3.0\n ));\n \n }\n "}, From 73ccc4db3001fa5326d3641185bdfa1929a55d50 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 1 Aug 2023 00:45:02 +0200 Subject: [PATCH 6/9] Sets: add chromatic coloring mode, separate for major key mode. --- src/rendering/Renderer.cpp | 16 ++++++++++------ src/rendering/SetOptions.cpp | 2 ++ src/rendering/SetOptions.h | 1 + src/rendering/State.cpp | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/rendering/Renderer.cpp b/src/rendering/Renderer.cpp index 172c042..18cf7d1 100755 --- a/src/rendering/Renderer.cpp +++ b/src/rendering/Renderer.cpp @@ -1149,6 +1149,8 @@ void Renderer::showSets(){ shouldUpdate = ImGui::RadioButton("Track", (int*)(&_state.setOptions.mode), int(SetMode::TRACK)) || shouldUpdate; ImGuiSameLine(2*90); shouldUpdate = ImGui::RadioButton("Key", (int*)(&_state.setOptions.mode), int(SetMode::KEY)) || shouldUpdate; + ImGuiSameLine(3*90); + shouldUpdate = ImGui::RadioButton("Chromatic", (int*)(&_state.setOptions.mode), int(SetMode::CHROMATIC)) || shouldUpdate; shouldUpdate = ImGui::RadioButton("Split", (int*)(&_state.setOptions.mode), int(SetMode::SPLIT)) || shouldUpdate; ImGuiSameLine(); @@ -1174,6 +1176,8 @@ void Renderer::showSets(){ } +static constexpr char const* kSetsComboString = " 0\0 1\0 2\0 3\0 4\0 5\0 6\0 7\0 8\0 9\0 10\0 11\0\0"; + void Renderer::showSetEditor(){ const unsigned int colWidth = 80; @@ -1270,7 +1274,7 @@ void Renderer::showSetEditor(){ ImGui::TableNextColumn(); ImGuiPushItemWidth(colWidth); // It is simpler to use a combo here (no weird focus issues when sorting rows). - if(ImGui::Combo("##Set", &key.set, " 0\0 1\0 2\0 3\0 4\0 5\0 6\0 7\0\0")){ + if(ImGui::Combo("##Set", &key.set, kSetsComboString)){ refreshSetOptions = true; } ImGui::PopItemWidth(); @@ -1306,7 +1310,7 @@ void Renderer::showSetEditor(){ ImGuiSameLine(2 * colWidth + 3 * offset); ImGuiPushItemWidth(colWidth); - ImGui::Combo("##Set", &newKey.set, " 0\0 1\0 2\0 3\0 4\0 5\0 6\0 7\0\0"); + ImGui::Combo("##Set", &newKey.set, kSetsComboString); ImGui::PopItemWidth(); ImGuiSameLine(3 * colWidth + 4 * offset); @@ -1674,14 +1678,14 @@ bool Renderer::channelColorEdit(const char * name, const char * displayName, Col ImGuiSameLine(); ImGui::Text("%s", displayName); if(ImGui::BeginPopup(name)){ - // Do 2x4 color sinks. + // Do 3 columns of color sinks. bool edit = false; - ImGuiPushItemWidth(25); + ImGuiPushItemWidth(35); for(size_t cid = 0; cid < colors.size(); ++cid){ const std::string nameC = "Set " + std::to_string(cid); edit = ImGui::ColorEdit3(nameC.c_str(), &colors[cid][0], ImGuiColorEditFlags_NoInputs) || edit; - if(cid % 2 == 0 && cid != colors.size()-1){ - ImGuiSameLine(); + if(cid % 3 != 2 && cid != colors.size()-1){ + ImGuiSameLine(75 * (cid%3+1)); } } ImGui::PopItemWidth(); diff --git a/src/rendering/SetOptions.cpp b/src/rendering/SetOptions.cpp index 85e16b8..43ecb31 100755 --- a/src/rendering/SetOptions.cpp +++ b/src/rendering/SetOptions.cpp @@ -40,6 +40,8 @@ int SetOptions::apply(int note, int channel, int track, double start) const { case SetMode::SPLIT: return note < key ? 0 : 1; case SetMode::KEY: + return noteShift[note % 12] % SETS_COUNT; + case SetMode::CHROMATIC: return (note % 12) % SETS_COUNT; case SetMode::LIST: { diff --git a/src/rendering/SetOptions.h b/src/rendering/SetOptions.h index 0edf738..b35fbe7 100755 --- a/src/rendering/SetOptions.h +++ b/src/rendering/SetOptions.h @@ -13,6 +13,7 @@ enum class SetMode : int { SPLIT = 2, KEY = 3, LIST = 4, + CHROMATIC = 5, }; struct SetOptions { diff --git a/src/rendering/State.cpp b/src/rendering/State.cpp index db3b6f8..44819b2 100755 --- a/src/rendering/State.cpp +++ b/src/rendering/State.cpp @@ -153,7 +153,7 @@ void State::defineOptions(){ _sharedInfos["colors-per-set"].category = OptionInfos::Category::SETS; _sharedInfos["sets-mode"] = {"How should notes be grouped into sets", OptionInfos::Type::OTHER}; - _sharedInfos["sets-mode"].values = "per-channel: 0, per-track: 1, split based on a key separator: 2, per-key: 3"; + _sharedInfos["sets-mode"].values = "per-channel: 0, per-track: 1, split based on a key separator: 2, per-key: 3, split based on list: 4, chromatic: 5"; _sharedInfos["sets-mode"].category = OptionInfos::Category::SETS; _sharedInfos["sets-separator-key"] = {"If notes are grouped in two sets, defines the key where the split should happen", OptionInfos::Type::KEY, {0.0f, 127.0f}}; From 6c7ad49aa06afa32bbdabdb81f919de491da3a92 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 1 Aug 2023 00:47:01 +0200 Subject: [PATCH 7/9] State: sort help items in alphabetical order. --- src/rendering/State.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/rendering/State.cpp b/src/rendering/State.cpp index 44819b2..eb0912e 100755 --- a/src/rendering/State.cpp +++ b/src/rendering/State.cpp @@ -200,21 +200,32 @@ size_t State::helpText(std::string & configOpts, std::string & setsOpts){ } size_t alignSize = maxLength + 4; - std::stringstream msgDefault; - std::stringstream msgSets; + std::vector msgDefault; + std::vector msgSets; for(const auto & param : _sharedInfos){ const std::string padString(alignSize - param.first.size(), ' '); const std::string line = "--" + param.first + padString + param.second.description + " (" + param.second.values + ")\n"; if(param.second.category == OptionInfos::Category::SETS){ - msgSets << line; + msgSets.push_back(line); } else { - msgDefault << line; + msgDefault.push_back(line); } } + std::sort(msgDefault.begin(), msgDefault.end()); + std::sort(msgSets.begin(), msgSets.end()); - configOpts = msgDefault.str(); - setsOpts = msgSets.str(); + std::stringstream msgDefaultStr; + std::stringstream msgSetsStr; + + for(const auto& str : msgDefault){ + msgDefaultStr << str; + } + for(const auto& str : msgSets){ + msgSetsStr << str; + } + configOpts = msgDefaultStr.str(); + setsOpts = msgSetsStr.str(); return alignSize; } From 09b4bf9e67d9ceffbc07d4a3fec9823bee7d70a4 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 1 Aug 2023 13:59:57 +0200 Subject: [PATCH 8/9] CI: Update macOS target. --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8748232..7b6878c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -14,7 +14,7 @@ jobs: cmake-opts: - os: windows-2019 cmake-opts: -A x64 - - os: macos-10.15 + - os: macos-11 cmake-opts: runs-on: ${{ matrix.os }} From a4f705e1e5e29c64b9b6b71eaa927ca63866640c Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 1 Aug 2023 14:00:19 +0200 Subject: [PATCH 9/9] CI: Update macOS target. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 185b9ff..9c5e1d0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: artifact_name: build/Release/MIDIVisualizer.exe asset_name: MIDIVisualizer-windows-64bits.zip ffmpeg-archive: ffmpeg-windows-64-static-lgpl.zip - - os: macos-10.15 + - os: macos-11 cmake-opts: artifact_name: build/MIDIVisualizer.app asset_name: MIDIVisualizer-macos.zip