562 lines
20 KiB
C
562 lines
20 KiB
C
/*
|
||
// 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);
|
||
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
|
||
}
|
||
|
||
} |