// Jamcorder Shadertoy shim // Simulates the mtSong* API with helper functions instead of real textures. // For Shadertoy previews, route note lookups through: // mtSongVelocity(int timeSlice, int midiPitch) // mtSongChannelMask(int timeSlice, int midiPitch) // mtSongActive(int timeSlice, int midiPitch) float mtTime = iTime; float mtTimeDelta = iTimeDelta; float mtSongSliceRate = 30.0; ivec2 mtSongTextureSize = ivec2(1024, 256); float mtSecondsVisible = 8.0; const int MT_SONG_PITCH_COUNT = 128; const float MT_SHIM_LOOP_SECONDS = 16.0; const float MT_SHIM_STEP_SECONDS = 0.25; int mtSongSliceCapacity() { return max(1, mtSongTextureSize.y * max(1, mtSongTextureSize.x / MT_SONG_PITCH_COUNT)); } float mtShimLoopTime(int timeSlice) { float seconds = float(max(timeSlice, 0)) / mtSongSliceRate; return mod(seconds, MT_SHIM_LOOP_SECONDS); } int mtShimStepIndex(int timeSlice) { return int(floor(mtShimLoopTime(timeSlice) / MT_SHIM_STEP_SECONDS)); } float mtShimStepPhase(int timeSlice) { return fract(mtShimLoopTime(timeSlice) / MT_SHIM_STEP_SECONDS); } int mtShimBarIndex(int timeSlice) { return mtShimStepIndex(timeSlice) / 16; } int mtShimPitchClass(int midiPitch) { return int(mod(float(midiPitch), 12.0)); } bool mtShimPitchInChord(int midiPitch, int root, int quality) { int pitchClass = mtShimPitchClass(midiPitch); int third = quality == 0 ? 4 : 3; return pitchClass == root || pitchClass == (root + third) % 12 || pitchClass == (root + 7) % 12; } float mtShimVelocityAccent(int stepInBar) { if (stepInBar == 0 || stepInBar == 8) return 1.0; if (stepInBar == 4 || stepInBar == 12) return 0.82; if (stepInBar % 2 == 0) return 0.72; return 0.58; } float mtSongVelocity(int timeSlice, int midiPitch) { if (timeSlice < 0 || timeSlice >= mtSongSliceCapacity()) return 0.0; int step = mtShimStepIndex(timeSlice); int stepInBar = step % 16; int bar = mtShimBarIndex(timeSlice) % 4; float phase = mtShimStepPhase(timeSlice); int root = int[4](0, 5, 9, 7)[bar]; int quality = int[4](0, 1, 1, 0)[bar]; float velocity = 0.0; if (phase < 0.92 && midiPitch >= 33 && midiPitch <= 84 && mtShimPitchInChord(midiPitch, root, quality)) { velocity = max(velocity, 84.0 * mtShimVelocityAccent(stepInBar)); } if (phase < 0.62) { int bassPitch = int[4](36, 41, 45, 43)[bar]; if (midiPitch == bassPitch && (stepInBar == 0 || stepInBar == 8)) { velocity = max(velocity, 118.0); } } if (phase < 0.48) { int melody = int[8](72, 74, 76, 79, 81, 79, 76, 74)[step % 8] + int[4](0, -2, 2, 0)[bar]; if (midiPitch == melody && stepInBar % 2 == 0) { velocity = max(velocity, 112.0); } } if (phase < 0.35) { int sparkle = 84 + (step % 4); if (midiPitch == sparkle && (stepInBar == 7 || stepInBar == 15)) { velocity = max(velocity, 96.0); } } return velocity; } uint mtSongChannelMask(int timeSlice, int midiPitch) { float velocity = mtSongVelocity(timeSlice, midiPitch); if (velocity <= 0.0) return 0u; int stepInBar = mtShimStepIndex(timeSlice) % 16; uint mask = 0u; if (midiPitch <= 48) { mask |= 1u << 1; } else { mask |= 1u << 0; } if (stepInBar == 7 || stepInBar == 15) { mask |= 1u << 4; } return mask; } bool mtSongActive(int timeSlice, int midiPitch) { return mtSongChannelMask(timeSlice, midiPitch) != 0u; }