#include "synth_engine.h" #include // A simple sine lookup table for the sine oscillator const int SINE_TABLE_SIZE = 256; static int16_t sine_table[SINE_TABLE_SIZE]; static bool sine_table_filled = false; /** * @brief Fills the global sine table. Called once on startup. */ void fill_sine_table() { if (sine_table_filled) return; for (int i = 0; i < SINE_TABLE_SIZE; ++i) { // M_PI is not standard C++, but it's common. If it fails, use 3.1415926535... sine_table[i] = static_cast(sin(2.0 * M_PI * i / SINE_TABLE_SIZE) * 32767.0); } sine_table_filled = true; } SynthEngine::SynthEngine(uint32_t sampleRate) : _sampleRate(sampleRate), _phase(0), _increment(0), _volume(0.5f), _waveform(SAWTOOTH), _isGateOpen(false), _envState(ENV_IDLE), _envLevel(0.0f), _attackInc(0.0f), _decayDec(0.0f), _sustainLevel(1.0f), _releaseDec(0.0f), _lpAlpha(1.0f), _hpAlpha(0.0f), _lpVal(0.0f), _hpVal(0.0f) { fill_sine_table(); // Initialize with a default frequency setFrequency(440.0f); setADSR(0.05f, 0.1f, 0.7f, 0.2f); // Default envelope } void SynthEngine::setFrequency(float freq) { // Calculate the phase increment for a given frequency. // The phase accumulator is a 32-bit unsigned integer (0 to 2^32-1). // One full cycle of the accumulator represents one cycle of the waveform. // increment = (frequency * 2^32) / sampleRate // The original calculation was incorrect for float frequencies. _increment = static_cast((double)freq * (4294967296.0 / (double)_sampleRate)); } void SynthEngine::setVolume(float vol) { if (vol < 0.0f) vol = 0.0f; if (vol > 1.0f) vol = 1.0f; _volume = vol; } void SynthEngine::setWaveform(Waveform form) { _waveform = form; } void SynthEngine::setGate(bool isOpen) { _isGateOpen = isOpen; if (isOpen) { _envState = ENV_ATTACK; } else { _envState = ENV_RELEASE; } } void SynthEngine::setADSR(float attack, float decay, float sustain, float release) { // Calculate increments per sample based on time in seconds // Avoid division by zero _attackInc = (attack > 0.001f) ? (1.0f / (attack * _sampleRate)) : 1.0f; _decayDec = (decay > 0.001f) ? (1.0f / (decay * _sampleRate)) : 1.0f; _sustainLevel = sustain; _releaseDec = (release > 0.001f) ? (1.0f / (release * _sampleRate)) : 1.0f; } void SynthEngine::setFilter(float lpCutoff, float hpCutoff) { // Simple one-pole filter coefficient calculation: alpha = 2*PI*fc/fs _lpAlpha = 2.0f * M_PI * lpCutoff / _sampleRate; if (_lpAlpha > 1.0f) _lpAlpha = 1.0f; if (_lpAlpha < 0.0f) _lpAlpha = 0.0f; _hpAlpha = 2.0f * M_PI * hpCutoff / _sampleRate; if (_hpAlpha > 1.0f) _hpAlpha = 1.0f; if (_hpAlpha < 0.0f) _hpAlpha = 0.0f; } float SynthEngine::getFrequency() const { return (float)((double)_increment * (double)_sampleRate / 4294967296.0); } void SynthEngine::process(int16_t* buffer, uint32_t numFrames) { for (uint32_t i = 0; i < numFrames; ++i) { _phase += _increment; int16_t sample = 0; // Oscillator Generation switch (_waveform) { case SAWTOOTH: sample = static_cast(_phase >> 16); break; case SQUARE: sample = (_phase < 0x80000000) ? 32767 : -32768; break; case SINE: sample = sine_table[(_phase >> 24) & 0xFF]; break; } float sampleF = static_cast(sample); // Apply Filters (One-pole) // Low Pass _lpVal += _lpAlpha * (sampleF - _lpVal); sampleF = _lpVal; // High Pass (implemented as Input - LowPass(hp_cutoff)) _hpVal += _hpAlpha * (sampleF - _hpVal); sampleF = sampleF - _hpVal; // Apply ADSR Envelope switch (_envState) { case ENV_ATTACK: _envLevel += _attackInc; if (_envLevel >= 1.0f) { _envLevel = 1.0f; _envState = ENV_DECAY; } break; case ENV_DECAY: _envLevel -= _decayDec; if (_envLevel <= _sustainLevel) { _envLevel = _sustainLevel; _envState = ENV_SUSTAIN; } break; case ENV_SUSTAIN: _envLevel = _sustainLevel; break; case ENV_RELEASE: _envLevel -= _releaseDec; if (_envLevel <= 0.0f) { _envLevel = 0.0f; _envState = ENV_IDLE; } break; case ENV_IDLE: _envLevel = 0.0f; break; } sampleF *= _envLevel; // Apply Master Volume and write to buffer buffer[i] = static_cast(sampleF * _volume); } }