#include "synth.h" #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif void synthInit(Synth *s, float sampleRate) { s->sampleRate = sampleRate; s->attackSec = 0.005f; s->releaseSec = 0.050f; s->dutyCycle = 0.5f; s->waveform = WAVE_SINE; s->lastStolenVoice = 0; for (int i = 0; i < VOICE_COUNT; i++) { s->voices[i].phase = 0.0f; s->voices[i].freqHz = 440.0f; s->voices[i].amp = 0.0f; s->voices[i].targetAmp = 0.0f; s->voices[i].active = 0; s->voices[i].midiNote = -1; s->pitchBend = 0.0f; s->pitchBendRange = 2.0f; } } float waveformSample(Waveform w, float phase, float dutyCycle) { switch (w) { case WAVE_SINE: return sinf(2.0f * (float)M_PI * phase); case WAVE_TRIANGLE: return (phase < 0.5f) ? ( 4.0f * phase - 1.0f) : (-4.0f * phase + 3.0f); case WAVE_SAW: return 2.0f * phase - 1.0f; case WAVE_RAMP: return 1.0f - 2.0f * phase; case WAVE_PULSE: return (phase < dutyCycle) ? 1.0f : -1.0f; case WAVE_NOISE: return ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f; default: return 0.0f; } } const char *waveformName(Waveform w) { switch (w) { case WAVE_SINE: return "Sine"; case WAVE_TRIANGLE: return "Triangle"; case WAVE_SAW: return "Saw"; case WAVE_RAMP: return "Ramp"; case WAVE_PULSE: return "Pulse"; case WAVE_NOISE: return "Noise"; default: return "???"; } } void synthNoteOn(Synth *s, int midiNote) { float hz = 440.0f * powf(2.0f, (midiNote - 69) / 12.0f); for (int i = 0; i < VOICE_COUNT; i++) { if (!s->voices[i].active) { s->voices[i].freqHz = hz; s->voices[i].midiNote = midiNote; s->voices[i].targetAmp = 1.0f; s->voices[i].active = 1; return; } } // No free voice — steal round-robin int i = s->lastStolenVoice % VOICE_COUNT; s->lastStolenVoice++; s->voices[i].freqHz = hz; s->voices[i].midiNote = midiNote; s->voices[i].targetAmp = 1.0f; s->voices[i].phase = 0.0f; s->voices[i].active = 1; } void synthNoteOff(Synth *s, int midiNote) { for (int i = 0; i < VOICE_COUNT; i++) { if (s->voices[i].active && s->voices[i].midiNote == midiNote) s->voices[i].targetAmp = 0.0f; } } void synthFillBuffer(Synth *s, int16_t *out, int frames) { const float sr = s->sampleRate; const float attackInc = (s->attackSec <= 0.0f) ? 1.0f : (1.0f / (s->attackSec * sr)); const float releaseInc = (s->releaseSec <= 0.0f) ? 1.0f : (1.0f / (s->releaseSec * sr)); for (int i = 0; i < frames; i++) { float mix = 0.0f; for (int v = 0; v < VOICE_COUNT; v++) { Voice *vv = &s->voices[v]; if (!vv->active) continue; if (vv->targetAmp > vv->amp) { vv->amp += attackInc; if (vv->amp > vv->targetAmp) vv->amp = vv->targetAmp; } else if (vv->targetAmp < vv->amp) { vv->amp -= releaseInc; if (vv->amp < vv->targetAmp) vv->amp = vv->targetAmp; } if (vv->amp <= 0.0f && vv->targetAmp == 0.0f) { vv->active = 0; continue; } //vv->phase += vv->freqHz / sr; float bendMultiplier = powf(2.0f, (s->pitchBend * s->pitchBendRange) / 12.0f); vv->phase += (vv->freqHz * bendMultiplier) / sr; if (vv->phase >= 1.0f) vv->phase -= 1.0f; mix += waveformSample(s->waveform, vv->phase, s->dutyCycle) * vv->amp; } mix *= (0.2f / VOICE_COUNT) * 4.0f; int32_t sample = (int32_t)lrintf(mix * 32767.0f); if (sample > 32767) sample = 32767; if (sample < -32768) sample = -32768; out[i] = (int16_t)sample; } }