UI controls in emulator

This commit is contained in:
Dejvino
2026-02-27 20:20:06 +01:00
parent 2b6e1d2c6b
commit 5240eb990b
3 changed files with 204 additions and 18 deletions
+50 -12
View File
@@ -1,35 +1,73 @@
#include "synth_engine.h"
#include <math.h>
// 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<int16_t>(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)
_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).
// 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
// We use a 64-bit intermediate calculation to prevent overflow.
_increment = static_cast<uint32_t>((static_cast<uint64_t>(freq) << 32) / _sampleRate);
// The original calculation was incorrect for float frequencies.
_increment = static_cast<uint32_t>((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) {
// 1. Advance the phase. Integer overflow automatically wraps it,
// which is exactly what we want for a continuous oscillator.
_phase += _increment;
// 2. Generate the sample. For a sawtooth wave, the sample value is
// directly proportional to the phase. We take the top 16 bits
// of the 32-bit phase accumulator to get a signed 16-bit sample.
int16_t sample = static_cast<int16_t>(_phase >> 16);
int16_t sample = 0;
switch (_waveform) {
case SAWTOOTH:
sample = static_cast<int16_t>(_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;
}
// 3. Write the sample to the buffer.
buffer[i] = sample;
// Apply volume and write to buffer
buffer[i] = static_cast<int16_t>(sample * _volume);
}
}