/* // Created by bruno on 16.2.2025. */ #include "audio.h" AudioData audioData; MidiEvent midiEvents[MIDI_TRACK_MAX][MAX_MIDI_EVENTS]; int midiEventCount[MIDI_TRACK_MAX]; int nextMidiEvent[MIDI_TRACK_MAX]; uint16_t getAvailableChannel() { for (uint16_t i = 0; i < NUM_SYNTH_VOICES - MIDI_VOICES; i++) { if (audioData.synthVoices[i].volume == 0) { return i; } } return -1; } // Helper: compute left/right gains from a pan value in [–1..+1] // pan = –1.0 → full left (L=1, R=0) // pan = +1.0 → full right (L=0, R=1) // pan = 0.0 → center (L=R=1/sqrt(2) or just 0.707 to avoid clipping) static void compute_stereo_gains(float pan, float *outL, float *outR) { // Simple linear panning (no constant‐power law). // If you prefer constant‐power, you could do: // float angle = (pan + 1.0f) * (M_PI / 4.0f); // *outL = cosf(angle); // *outR = sinf(angle); // // Here we’ll just do linear: pan = fmaxf(-1.0f, fminf(+1.0f, pan)); if (pan <= 0.0f) { *outL = 1.0f; *outR = 1.0f + pan; // pan is negative, so R < 1 } else { *outL = 1.0f - pan; // pan is positive, so L < 1 *outR = 1.0f; } // Optionally, scale down both so we never exceed 1.0f / sqrt(2) // e.g. *outL *= 0.7071f; *outR *= 0.7071f; } // Improved audio ; with anti-clipping and smooth fade-out void audio_callback(void *userdata, Uint8 *stream, int len) { AudioData *audio = (AudioData *) userdata; int frames = len / (2 * sizeof(float)); // Stereo frame count float elapsedSec = ((float) audio->totalSamples) / SAMPLE_RATE; audio->totalSamples += frames; for (uint8_t midiChannel = 0; midiChannel < MIDI_TRACK_MAX; midiChannel++) { while (nextMidiEvent[midiChannel] < midiEventCount[midiChannel] && midiEvents[midiChannel][nextMidiEvent[midiChannel]].timeSec <= elapsedSec) { MidiEvent *ev = &midiEvents[midiChannel][nextMidiEvent[midiChannel]]; // printf("Event at %f, %s, note: %d, velocity: %d\n", ev->timeSec, ev->type == MIDI_NOTE_ON ? "ON" : "OFF", // ev->note, ev->velocity); uint16_t freq = (uint16_t)(440.0f * powf(2.0f, (ev->note - 69) / 12.0f)); uint8_t midiVoiceIndex = NUM_SYNTH_VOICES - midiChannel - 1; SynthVoice *v = &audio->synthVoices[midiVoiceIndex]; if (ev->type == MIDI_NOTE_ON && ev->velocity > 0) { // Note On v->frequency = freq; v->volume = ev->velocity * 2; v->smoothedAmp = 0; // printf("Playing voice %d at freq %d hz, volume %d\n", midiVoiceIndex, v->frequency, // v->volume); } else if (ev->type == MIDI_NOTE_OFF || ev->velocity == 0) { // Note Off v->volume = 0; // printf("Stopping voice %d at freq %d hz, volume %d\n", midiVoiceIndex, v->frequency, v->volume); } else if ((ev->type & 0xF0) == MIDI_PROGRAM_CHANGE) { if (ev->note == 0) { v->waveform = resolvePatch(ev->note); } } nextMidiEvent[midiChannel]++; } } float *outBuf = (float *) stream; for (int i = 0; i < 2 * frames; ++i) { outBuf[i] = 0.0f; } float listenerCx = audio->playerRect->x + audio->playerRect->w * 0.5f; int *voiceCounts = calloc(frames, sizeof(int)); for (int v = 0; v < NUM_SYNTH_VOICES; v++) { SynthVoice *voice = &audio->synthVoices[v]; if ((voice->volume == 0 && voice->smoothedAmp < 0.001f) || voice->frequency == 0) continue; float sourceCx = voice->sourceRect.x + TILE_SIZE * 0.5f; float dx = sourceCx - listenerCx; float pan = fmaxf(-1.0f, fminf(+1.0f, dx / audio->maxPanDistance)); float gainL = 1; float gainR = 1; float targetAmp = (voice->volume / 255.0f) / 2; if (v < NUM_SYNTH_VOICES - MIDI_VOICES) { float distanceAtten = 1.0f - fminf(fabsf(dx) / audio->maxPanDistance, 1.0f); targetAmp *= distanceAtten; compute_stereo_gains(pan, &gainL, &gainR); gainL *= 0.7071f; gainR *= 0.7071f; } double phaseInc = ((double) voice->frequency * 256.0) / (double) SAMPLE_RATE; for (int i = 0; i < frames; i++) { voice->smoothedAmp += (targetAmp - voice->smoothedAmp) * SMOOTHING_FACTOR; float amp = voice->smoothedAmp; double norm = voice->phase / 256.0; double t = norm * 2.0 - 1.0; float sample; switch (voice->waveform) { case WAVE_SINE: sample = (float) sin(norm * 2.0 * M_PI); break; case WAVE_SQUARE: sample = (t >= 0.0) ? 1.0f : -1.0f; break; case WAVE_SAWTOOTH: sample = (float) t; break; case WAVE_TRIANGLE: sample = (float) (1.0 - 4.0 * fabs(t - floor(t + 0.5))); break; case WAVE_NOISE: sample = ((float) rand() / (float) RAND_MAX) * 2.0f - 1.0f; break; case WAVE_HALF_SINE: // one cycle of sine clipped to [0, 1] sample = (float) (0.5 * sin(norm * 2.0 * M_PI) + 0.5); break; case WAVE_PULSE25: sample = (norm < 0.25) ? 1.0f : -1.0f; break; case WAVE_PULSE10: sample = (norm < 0.10) ? 1.0f : -1.0f; break; case WAVE_CLIPPED_SINE: sample = fmaxf(-0.7f, fminf(0.7f, (float) sin(norm * 2.0 * M_PI))); break; case WAVE_EXP: sample = copysignf(powf(fabsf(t), 0.5f), t); // softer exponential curve break; case WAVE_RAMP: sample = 2.0f * fmodf(norm, 1.0f) - 1.0f; // ascending ramp (like saw) break; case WAVE_REVERSE_SAW: sample = 1.0f - 2.0f * fmodf(norm, 1.0f); // descending ramp break; case WAVE_STAIRCASE: sample = floorf(norm * 8.0f) / 4.0f - 1.0f; // stepped waveform break; default: sample = (float) sin(norm * 2.0 * M_PI); // fallback to sine break; } voice->phase += phaseInc; if (voice->phase >= 256.0) voice->phase -= 256.0; else if (voice->phase < 0.0) voice->phase += 256.0; int idxL = 2 * i; int idxR = 2 * i + 1; outBuf[idxL] += sample * amp * gainL; outBuf[idxR] += sample * amp * gainR; voiceCounts[i]++; } } for (int i = 0; i < frames; ++i) { int count = voiceCounts[i]; if (count > 0) { outBuf[2 * i + 0] /= count; outBuf[2 * i + 1] /= count; } } free(voiceCounts); } static uint32_t read_be_uint32(const uint8_t *data) { return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; } static uint16_t read_be_uint16(const uint8_t *data) { return (data[0] << 8) | data[1]; } static uint32_t read_vlq(const uint8_t **ptr) { uint32_t value = 0; const uint8_t *p = *ptr; while (*p & 0x80) { value = (value << 7) | (*p++ & 0x7F); } value = (value << 7) | (*p++ & 0x7F); *ptr = p; return value; } void load_midi_file(const char *path) { FILE *f = fopen(path, "rb"); if (!f) return; fseek(f, 0, SEEK_END); long size = ftell(f); rewind(f); uint8_t *data = malloc(size); fread(data, 1, size, f); fclose(f); const uint8_t *ptr = data; if (memcmp(ptr, "MThd", 4) != 0) return; ptr += 8; // skip header length uint16_t format = read_be_uint16(ptr); ptr += 2; uint16_t nTracks = read_be_uint16(ptr); ptr += 2; uint16_t ppqn = read_be_uint16(ptr); ptr += 2; if (format > 1) { printf("ERROR: Only MIDI format 0 or 1 supported (found %d)\n", format); free(data); return; } if (nTracks == 0 || nTracks > MIDI_TRACK_MAX) { printf("ERROR: Number of tracks %d out of range\n", nTracks); free(data); return; } for (int trackIndex = 0; trackIndex < nTracks; trackIndex++) { if (memcmp(ptr, "MTrk", 4) != 0) return; uint32_t trackLen = read_be_uint32(ptr + 4); ptr += 8; const uint8_t *trackEnd = ptr + trackLen; float curTime = 0.0f; uint32_t tempo = 500000; // default: 120 BPM uint8_t lastStatus = 0; while (ptr < trackEnd && midiEventCount[trackIndex] < MAX_MIDI_EVENTS) { uint32_t delta = read_vlq(&ptr); curTime += (delta * (tempo / 1000000.0f)) / ppqn; uint8_t status = *ptr; if (status < 0x80) status = lastStatus; else ptr++; lastStatus = status; if (status == 0xFF) { uint8_t metaType = *ptr++; uint32_t len = read_vlq(&ptr); if (metaType == 0x03) { // This is a track name — skip it ptr += len; continue; } if (metaType == 0x51 && len == 3) { tempo = (ptr[0] << 16 | ptr[1] << 8 | ptr[2]); } ptr += len; } else if ((status & 0xF0) == 0x90 || (status & 0xF0) == 0x80) { uint8_t note = *ptr++; uint8_t vel = *ptr++; midiEvents[trackIndex][midiEventCount[trackIndex]++] = (MidiEvent) { .timeSec = curTime, .type = status, .note = note, .velocity = vel }; } else { ptr += 2; // skip unknown } } } free(data); } Waveform resolvePatch(uint8_t patchNum) { switch (patchNum) { case 0: return WAVE_CLIPPED_SINE; // Acoustic Grand Piano (harmonic, percussive) case 1: return WAVE_CLIPPED_SINE; // Bright Acoustic Piano case 2: return WAVE_CLIPPED_SINE; // Electric Grand Piano case 3: return WAVE_CLIPPED_SINE; // Honky-Tonk Piano case 4: return WAVE_SINE; // Electric Piano 1 (Rhodes) - smooth bell-like case 5: return WAVE_SINE; // Electric Piano 2 (Wurlitzer) case 6: return WAVE_CLIPPED_SINE; // Harpsichord (plucked, bright) case 7: return WAVE_SQUARE; // Clavinet (sharp, funky) case 8: return WAVE_SINE; // Celesta (bell-like, soft) case 9: return WAVE_SINE; // Glockenspiel (bright bell) case 10: return WAVE_SINE; // Music Box (delicate bell) case 11: return WAVE_SINE; // Vibraphone (vibrato sine-like) case 12: return WAVE_SINE; // Marimba (warm sine) case 13: return WAVE_SINE; // Xylophone (sharp sine) case 14: return WAVE_SINE; // Tubular Bells (bell) case 15: return WAVE_SINE; // Dulcimer (plucked sine) case 16: return WAVE_SAWTOOTH; // Drawbar Organ (rich harmonic) case 17: return WAVE_SAWTOOTH; // Percussive Organ case 18: return WAVE_SAWTOOTH; // Rock Organ case 19: return WAVE_SAWTOOTH; // Church Organ case 20: return WAVE_SAWTOOTH; // Reed Organ case 21: return WAVE_SQUARE; // Accordion (reedy square) case 22: return WAVE_SQUARE; // Harmonica (square-ish) case 23: return WAVE_SQUARE; // Tango Accordion case 24: return WAVE_PULSE25; // Nylon Guitar (plucked, soft pulse) case 25: return WAVE_PULSE25; // Steel Guitar case 26: return WAVE_PULSE25; // Jazz Guitar case 27: return WAVE_PULSE25; // Clean Electric Guitar case 28: return WAVE_PULSE25; // Muted Electric Guitar case 29: return WAVE_PULSE10; // Overdriven Guitar (more distorted) case 30: return WAVE_PULSE10; // Distortion Guitar case 31: return WAVE_PULSE25; // Guitar Harmonics (plucked) case 32: return WAVE_SINE; // Acoustic Bass case 33: return WAVE_SINE; // Electric Bass (finger) case 34: return WAVE_SINE; // Electric Bass (pick) case 35: return WAVE_SINE; // Fretless Bass case 36: return WAVE_TRIANGLE; // Slap Bass 1 (percussive triangle) case 37: return WAVE_TRIANGLE; // Slap Bass 2 case 38: return WAVE_SAWTOOTH; // Synth Bass 1 case 39: return WAVE_SAWTOOTH; // Synth Bass 2 case 40: return WAVE_SINE; // Violin case 41: return WAVE_SINE; // Viola case 42: return WAVE_SINE; // Cello case 43: return WAVE_SINE; // Contrabass case 44: return WAVE_SAWTOOTH; // Tremolo Strings (rich) case 45: return WAVE_SINE; // Pizzicato Strings (plucked) case 46: return WAVE_SINE; // Orchestral Harp (plucked sine) case 47: return WAVE_CLIPPED_SINE; // Timpani (percussive sine) case 48: return WAVE_SAWTOOTH; // String Ensemble 1 (Slow Strings) case 49: return WAVE_SAWTOOTH; // String Ensemble 2 (Fast Strings) case 50: return WAVE_SAWTOOTH; // SynthStrings 1 case 51: return WAVE_SAWTOOTH; // SynthStrings 2 case 52: return WAVE_SINE; // Choir Aahs case 53: return WAVE_SINE; // Voice Oohs case 54: return WAVE_SQUARE; // Synth Voice case 55: return WAVE_SAWTOOTH; // Orchestra Hit (harsh) case 56: return WAVE_SQUARE; // Trumpet case 57: return WAVE_SQUARE; // Trombone case 58: return WAVE_SQUARE; // Tuba case 59: return WAVE_PULSE25; // Muted Trumpet case 60: return WAVE_SQUARE; // French Horn case 61: return WAVE_SAWTOOTH; // Brass Section case 62: return WAVE_SAWTOOTH; // SynthBrass 1 case 63: return WAVE_SAWTOOTH; // SynthBrass 2 case 64: return WAVE_SQUARE; // Soprano Sax case 65: return WAVE_SQUARE; // Alto Sax case 66: return WAVE_SQUARE; // Tenor Sax case 67: return WAVE_SQUARE; // Baritone Sax case 68: return WAVE_SINE; // Oboe case 69: return WAVE_SINE; // English Horn case 70: return WAVE_SINE; // Bassoon case 71: return WAVE_SINE; // Clarinet case 72: return WAVE_SINE; // Piccolo case 73: return WAVE_SINE; // Flute case 74: return WAVE_SINE; // Recorder case 75: return WAVE_SINE; // Pan Flute case 76: return WAVE_SINE; // Blown Bottle case 77: return WAVE_SINE; // Shakuhachi case 78: return WAVE_SINE; // Whistle case 79: return WAVE_SINE; // Ocarina case 80: return WAVE_SQUARE; // Lead 1 (Square) case 81: return WAVE_SAWTOOTH; // Lead 2 (Sawtooth) case 82: return WAVE_SAWTOOTH; // Lead 3 (Calliope) case 83: return WAVE_SINE; // Lead 4 (Chiff) case 84: return WAVE_SQUARE; // Lead 5 (Charang) case 85: return WAVE_SQUARE; // Lead 6 (Voice) case 86: return WAVE_SAWTOOTH; // Lead 7 (Fifths) case 87: return WAVE_SQUARE; // Lead 8 (Bass + Lead) case 88: return WAVE_SAWTOOTH; // Pad 1 (New Age) case 89: return WAVE_SAWTOOTH; // Pad 2 (Warm) case 90: return WAVE_SAWTOOTH; // Pad 3 (Polysynth) case 91: return WAVE_SINE; // Pad 4 (Choir) case 92: return WAVE_SINE; // Pad 5 (Bowed) case 93: return WAVE_SAWTOOTH; // Pad 6 (Metallic) case 94: return WAVE_SINE; // Pad 7 (Halo) case 95: return WAVE_SINE; // Pad 8 (Sweep) case 96: return WAVE_NOISE; // FX 1 (Rain) case 97: return WAVE_NOISE; // FX 2 (Soundtrack) case 98: return WAVE_NOISE; // FX 3 (Crystal) case 99: return WAVE_NOISE; // FX 4 (Atmosphere) case 100: return WAVE_NOISE; // FX 5 (Brightness) case 101: return WAVE_NOISE; // FX 6 (Goblins) case 102: return WAVE_NOISE; // FX 7 (Echoes) case 103: return WAVE_NOISE; // FX 8 (Sci-Fi) case 104: return WAVE_SAWTOOTH; // Sitar case 105: return WAVE_SQUARE; // Banjo case 106: return WAVE_PULSE25; // Shamisen case 107: return WAVE_SINE; // Koto case 108: return WAVE_SINE; // Kalimba case 109: return WAVE_SINE; // Bagpipe case 110: return WAVE_SINE; // Fiddle case 111: return WAVE_SINE; // Shanai case 112: return WAVE_SAWTOOTH; // Tinkle Bell case 113: return WAVE_NOISE; // Agogo case 114: return WAVE_NOISE; // Steel Drums case 115: return WAVE_NOISE; // Woodblock case 116: return WAVE_NOISE; // Taiko Drum case 117: return WAVE_NOISE; // Melodic Tom case 118: return WAVE_NOISE; // Synth Drum case 119: return WAVE_NOISE; // Reverse Cymbal case 120: return WAVE_NOISE; // Guitar Fret Noise case 121: return WAVE_NOISE; // Breath Noise case 122: return WAVE_NOISE; // Seashore case 123: return WAVE_NOISE; // Bird Tweet case 124: return WAVE_NOISE; // Telephone Ring case 125: return WAVE_NOISE; // Helicopter case 126: return WAVE_NOISE; // Applause case 127: return WAVE_NOISE; // Gunshot default: return WAVE_SINE; // Default fallback } }