#ifndef SYNTH_ENGINE_H #define SYNTH_ENGINE_H #include #include #include #if defined(ARDUINO_ARCH_RP2040) #include class SynthMutex { public: SynthMutex() { mutex_init(&mtx); } void lock() { mutex_enter_blocking(&mtx); } void unlock() { mutex_exit(&mtx); } private: mutex_t mtx; }; template class SynthLockGuard { public: explicit SynthLockGuard(Mutex& m) : m_mutex(m) { m_mutex.lock(); } ~SynthLockGuard() { m_mutex.unlock(); } SynthLockGuard(const SynthLockGuard&) = delete; SynthLockGuard& operator=(const SynthLockGuard&) = delete; private: Mutex& m_mutex; }; #else #include using SynthMutex = std::mutex; template using SynthLockGuard = std::lock_guard; #endif /** * @class SynthEngine * @brief A portable, platform-agnostic synthesizer engine. * * This class contains the core digital signal processing (DSP) logic. * It has no dependencies on any specific hardware, OS, or audio API. * It works by filling a provided buffer with 16-bit signed audio samples. * * The oscillator uses a 32-bit unsigned integer as a phase accumulator, * which is highly efficient and avoids floating-point math in the audio loop, * making it ideal for the RP2040. */ class SynthEngine { public: enum Waveform { SAWTOOTH, SQUARE, SINE }; /** * @brief Constructs the synthesizer engine. * @param sampleRate The audio sample rate in Hz (e.g., 44100). */ ~SynthEngine(); SynthEngine(uint32_t sampleRate); /** * @brief Fills a buffer with audio samples. This is the main audio callback. * @param buffer Pointer to the output buffer to be filled. * @param numFrames The number of audio frames (samples) to generate. */ void process(int16_t* buffer, uint32_t numFrames); /** * @brief Sets the frequency of the oscillator. * @param freq The frequency in Hz. */ void setFrequency(float freq); /** * @brief Sets the output volume. * @param vol Volume from 0.0 (silent) to 1.0 (full). */ void setVolume(float vol); /** * @brief Sets the oscillator's waveform. * @param form The waveform to use. */ void setWaveform(Waveform form); /** * @brief Opens or closes the sound gate. * @param isOpen True to produce sound, false for silence. */ void setGate(bool isOpen); /** * @brief Gets the current frequency of the oscillator. * @return The frequency in Hz. */ float getFrequency() const; // --- Grid Synth --- struct GridCell { enum Type { EMPTY, FIXED_OSCILLATOR, INPUT_OSCILLATOR, WAVETABLE, NOISE, LFO, GATE_INPUT, ADSR_ATTACK, ADSR_DECAY, ADSR_SUSTAIN, ADSR_RELEASE, FORK, WIRE, LPF, HPF, VCA, BITCRUSHER, DISTORTION, RECTIFIER, PITCH_SHIFTER, GLITCH, OPERATOR, DELAY, REVERB, SINK }; enum Op { OP_ADD, OP_MUL, OP_SUB, OP_DIV, OP_MIN, OP_MAX }; Type type = EMPTY; float param = 0.5f; // 0.0 to 1.0 int rotation = 0; // 0:N, 1:E, 2:S, 3:W (Output direction) float value = 0.0f; // Current output sample float next_value = 0.0f; // For double-buffering in processGridStep float phase = 0.0f; // For Oscillator, Noise state }; static const int GRID_W = 12; static const int GRID_H = 12; static const size_t SERIALIZED_GRID_SIZE = GRID_W * GRID_H * 3; void exportGrid(uint8_t* buffer); void importGrid(const uint8_t* buffer); void loadPreset(int preset); void rebuildProcessingOrder(); void clearGrid(); GridCell grid[GRID_W][GRID_H]; SynthMutex gridMutex; // Helper to process one sample step of the grid float processGridStep(); private: uint32_t _sampleRate; uint32_t _phase; // Phase accumulator for the oscillator. uint32_t _increment; // Phase increment per sample, determines frequency. float _volume; Waveform _waveform; bool _isGateOpen; uint32_t _rngState; std::vector> _processing_order; void rebuildProcessingOrder_locked(); // Internal random number generator float _random(); }; #endif // SYNTH_ENGINE_H