#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) { fill_sine_table(); // Initialize with a default frequency setFrequency(440.0f); } 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::process(int16_t* buffer, uint32_t numFrames) { for (uint32_t i = 0; i < numFrames; ++i) { _phase += _increment; int16_t sample = 0; switch (_waveform) { case SAWTOOTH: sample = static_cast(_phase >> 16); break; case SQUARE: sample = (_phase < 0x80000000) ? 32767 : -32768; break; case SINE: // Use top 8 bits of phase as index into sine table sample = sine_table[(_phase >> 24) & 0xFF]; break; } // Apply volume and write to buffer buffer[i] = static_cast(sample * _volume); } }