|
TR123e - EMCT Computing Final Project Version 1.0
Research Project Translation from gen~ to embedded Moog synth
|
Real-time monophonic synthesizer implementation for Bela platform. More...
#include <Bela.h>#include <libraries/Midi/Midi.h>#include <cmath>#include "ADSR.h"#include "KeyFollow.h"#include "MidiHandler.h"#include "MoogFilterEnvelope.h"#include "PortamentoFilter.h"#include "PortamentoPlayer.h"#include "ResonanceRamp.h"#include "VelocityParser.h"#include "zdf_moogladder_v2.h"Functions | |
| bool | setup (BelaContext *context, void *userData) |
| System initialization and configuration. | |
| void | render (BelaContext *context, void *userData) |
| Real-time audio processing callback. | |
| void | cleanup (BelaContext *context, void *userData) |
| System cleanup and resource deallocation. | |
Variables | |
| float | oscillatorPhase = 0.0f |
| Primary oscillator phase accumulator [0, 2π]. | |
| float | twoPi = 2.0f * M_PI |
| Precomputed 2π constant for computational efficiency. | |
| Midi | midi |
| MIDI interface controller. | |
| MidiHandler | midiHandler (44100.0f, 1.0f) |
| High-level MIDI message processor and timing controller. | |
| VelocityParser | velocityParser (64) |
| Velocity-sensitive note triggering processor. | |
| PortamentoFilter | portamentoFilter |
| Portamento behavior analysis module. | |
| PortamentoPlayer | portamentoPlayer (44100.0f, 100.0f) |
| Pitch interpolation and portamento synthesis engine. | |
| ADSR | envelope |
| Primary amplitude envelope generator. | |
| MoogFilterEnvelope | filterEnv (44100.0f) |
| Filter cutoff frequency envelope generator. | |
| KeyFollow | keyFollow (0.01f) |
| Keyboard tracking / key follow processor. | |
| ResonanceRamp | resonanceRamp (44100.0f, 50.0f) |
| Resonance parameter smoothing filter. | |
| ZDFMoogLadderFilter | zdfFilter (44100.0f) |
| Zero-delay feedback Moog ladder filter implementation. | |
| float * | inputBuffer = nullptr |
| Input audio buffer for oscillator output. | |
| float * | outputBuffer = nullptr |
| Output audio buffer for filtered audio. | |
| int | bufferSize = 0 |
| Current audio buffer size in samples. | |
| float | baseCutoffFrequency = 5000.0f |
| Base filter cutoff frequency in Hz. | |
| int | gAudioFramesPerAnalogFrame = 0 |
| Audio-to-analog frame ratio for control rate processing. | |
Real-time monophonic synthesizer implementation for Bela platform.
This file implements a complete monophonic synthesizer using the Bela real-time audio platform. The synthesizer features a sine wave oscillator with portamento, dual ADSR envelopes (amplitude and filter), a zero-delay feedback Moog ladder filter, and comprehensive MIDI control with analog input integration.
@architecture The system employs a modular signal flow architecture: MIDI Input → Note Processing → Oscillator → Filter → Amplification → Audio Output ↓ ↓ ↓ Portamento Amp Envelope Filter Envelope
@performance_characteristics
@dependencies
| void cleanup | ( | BelaContext * | context, |
| void * | userData ) |
System cleanup and resource deallocation.
@function cleanup
Releases dynamically allocated memory and performs orderly shutdown of audio processing system. Called once when audio processing terminates.
| context | Bela audio context (unused in cleanup) |
| userData | User-defined data pointer (unused) |
@complexity O(1) - Constant time cleanup @realtime_safety Non-real-time safe (performs memory deallocation)
Deallocate input buffer memory Prevents memory leaks on system shutdown
Deallocate output buffer memory
Completes memory management for audio buffers
| void render | ( | BelaContext * | context, |
| void * | userData ) |
Real-time audio processing callback.
@function render
Core real-time audio processing function called at regular intervals (typically every 64-512 samples). Implements complete signal chain from MIDI input through oscillator, envelope, and filter processing to audio output.
| context | Bela audio context with I/O buffers and timing information |
| userData | User-defined data pointer (unused) |
@complexity O(n) where n = context->audioFrames @realtime_safety Real-time safe (no dynamic allocation or blocking operations) @latency Ultra-low latency (< 5ms typical processing delay)
Calculate current time in milliseconds for MIDI timing correlation Provides high-precision timestamp for event scheduling and delay compensation Formula: (samples_elapsed / sample_rate) * 1000 = time_in_ms
Process all available MIDI messages in current buffer period Non-blocking iteration ensures real-time safety while handling multiple simultaneous MIDI events with sample-accurate timing
Process Note On/Off messages for synthesis control Both message types handled by unified processor for consistent timing
Process Control Change messages for real-time parameter modification Implements standard MIDI CC protocol for synthesizer control
CC 14: Filter Cutoff Frequency Control Maps MIDI CC value to exponential frequency range [20Hz - 30kHz] Formula: f = 20 * (1500^(cc/127)) provides musical frequency scaling
CC 15: Filter Resonance Control Linear mapping from MIDI CC to resonance parameter [0.0-1.0] Higher values increase filter Q and self-oscillation tendency
Update MIDI handler timing state for accurate event scheduling Maintains internal timing reference for sample-accurate MIDI processing
Process all sample-accurate delayed MIDI messages Handles messages that require precise timing alignment with audio samples for glitch-free note transitions and envelope triggering
Parse velocity to determine note-on vs note-off state Implements MIDI standard where velocity 0 = note-off
Analyze portamento requirements based on playing technique Determines whether smooth pitch transition is appropriate based on note overlap timing and musical context
Convert MIDI velocity to normalized amplitude scaling [0.0-1.0] Provides velocity-sensitive filter envelope response
Process note-on events: trigger all synthesis modules
Process note-off events: release all synthesis modules
Set resonance emphasis during note events Creates characteristic filter "pop" during note articulation typical of classic analog synthesizers
Static variable for output gain to maintain state between function calls Note: Double semicolon appears to be a typo in original code
Process each audio sample in the current buffer Implements complete synthesis chain: oscillator → envelope → filter → output
Calculate analog input index based on audio/analog frame ratio Analog inputs sampled at lower rate than audio (typically 8:1) This indexing ensures proper correlation between control and audio timing
Generate amplitude envelope value [0.0-1.0] Provides time-varying amplitude control for musical articulation
Generate current oscillator frequency from portamento processor Returns frequency in Hz with smooth transitions between notes
Calculate keyboard tracking contribution to filter frequency Higher notes slightly open filter for consistent timbre across range
Generate filter cutoff frequency from envelope and key tracking Combines base frequency, envelope modulation, and keyboard tracking
Read analog control potentiometers [0.0-1.0] Hardware control integration for real-time parameter adjustment
Read filter mode selection from analog input Maps continuous [0.0-1.0] to discrete mode values [0-2] Modes: 0=LP24, 1=LP12, 2=HP12, 3=BP12 (depending on filter implementation)
Read additional control parameters from analog inputs
Update filter resonance with smooth ramping Prevents audio artifacts from abrupt parameter changes
Update filter parameters for current sample Combines envelope-generated cutoff with manual control
Update envelope parameters in real-time Allows dynamic response characteristics during performance
Generate oscillator output using Direct Digital Synthesis (DDS) Implements conditional processing for CPU efficiency
Only generate oscillator output when envelope is active Saves CPU cycles during silent periods (envelope in idle state)
Generate sine wave using phase accumulator method sin(φ) where φ = accumulated phase [0, 2π]
Update phase accumulator for next sample Phase increment = 2π * frequency / sample_rate This implements the fundamental DDS frequency equation
Phase wrapping to prevent floating-point precision loss Maintains phase within [0, 2π] range for optimal accuracy
Apply amplitude envelope to oscillator output Creates time-varying amplitude response for musical articulation
Reset oscillator phase during silent periods Ensures clean restart for next note-on event
Apply output scaling to prevent clipping 50% scaling provides headroom for filter resonance peaks
Store oscillator output in input buffer for filter processing Separation allows for potential future multi-stage processing
Apply filter processing to entire buffer Separate loop allows for potential SIMD optimization and maintains clean separation between synthesis and filtering stages
Process each sample through ZDF Moog ladder filter Apply output gain control for final level adjustment
Write processed audio to both output channels (stereo) Monophonic synthesizer output duplicated to both channels for standard stereo compatibility
| bool setup | ( | BelaContext * | context, |
| void * | userData ) |
System initialization and configuration.
@function setup
Initializes all audio processing modules, allocates dynamic memory, configures MIDI interface, and establishes initial parameter states. Called once at system startup before real-time processing begins.
| context | Bela audio context containing system configuration |
| userData | User-defined data pointer (unused in this implementation) |
@complexity O(1) - Constant time initialization @realtime_safety Non-real-time safe (performs memory allocation)
Configure MIDI interface for hardware device "hw:0,0" This corresponds to the first MIDI interface on the system
Enable built-in MIDI message parsing for automatic protocol handling Parser provides structured access to MIDI channel messages, system exclusive data, and real-time messages with proper timing information
Cache sample rate for computational efficiency Eliminates repeated context dereferencing in audio callback
Calculate and cache audio/analog frame ratio Bela processes analog inputs at lower rates than audio samples Typical ratio: 8 audio frames per analog frame (44.1kHz/5.5kHz)
Initialize ZDF Moog ladder filter with current sample rate Reconfiguration ensures proper coefficient calculation for actual system sample rate (may differ from default 44.1kHz)
Reset filter internal state to prevent initialization artifacts Clears delay lines and feedback paths for clean startup
Configure initial filter parameters
Cache buffer size for memory allocation Size determined by Bela configuration, typically 64-512 samples
Allocate input buffer for oscillator output staging Enables separation of synthesis and filtering for modular processing
Allocate output buffer for final processed audio Provides clean separation between processing stages
Configure filter envelope parameters
Set filter envelope depth to 48 semitones (4 octaves) Provides musically significant frequency modulation range while preventing excessive filter opening that could cause aliasing
Initialize resonance ramping to moderate value 0.5 provides good filter character without excessive self-oscillation
Reset amplitude envelope to idle state Ensures clean initialization without residual envelope activity
Configure amplitude envelope timing (values in samples)
Set sustain level to 65% of peak amplitude Balances musical sustain with dynamic range preservation
Configure envelope curvature parameters
These parameters control the mathematical curvature of envelope segments, affecting the perceptual character of amplitude changes
| float baseCutoffFrequency = 5000.0f |
Base filter cutoff frequency in Hz.
Fundamental filter frequency before envelope and key tracking modulation. Set to 5kHz as musically useful default providing bright, open character while allowing sufficient modulation range.
| int bufferSize = 0 |
Current audio buffer size in samples.
Dynamically determined from Bela context, typically 64-512 samples depending on system configuration and latency requirements.
| ADSR envelope |
Primary amplitude envelope generator.
Four-stage ADSR (Attack, Decay, Sustain, Release) envelope with exponential segment shaping and configurable curvature parameters. Implements state machine for proper envelope stage transitions.
| MoogFilterEnvelope filterEnv(44100.0f) | ( | 44100. | 0f | ) |
Filter cutoff frequency envelope generator.
| sampleRate | 44100.0f Hz - Audio processing rate |
Specialized envelope generator for filter frequency modulation with velocity sensitivity and exponential frequency mapping. Provides musical filter sweeps with proper logarithmic frequency scaling.
| int gAudioFramesPerAnalogFrame = 0 |
Audio-to-analog frame ratio for control rate processing.
Bela systems process analog inputs at lower rates than audio (typically 8:1 ratio). This variable caches the ratio for efficient analog input indexing without repeated division operations in the audio callback.
| float* inputBuffer = nullptr |
Input audio buffer for oscillator output.
Dynamically allocated buffer for storing raw oscillator output before filter processing. Separation of generation and filtering stages allows for potential future multi-stage processing implementations.
| KeyFollow keyFollow(0.01f) | ( | 0. | 01f | ) |
Keyboard tracking / key follow processor.
| intensity | 0.01f - Key tracking coefficient [0.0-1.0] |
Implements keyboard tracking for filter cutoff frequency, allowing higher notes to open the filter proportionally. Uses MIDI note number to frequency mapping with configurable tracking intensity.
| Midi midi |
MIDI interface controller.
Handles low-level MIDI protocol parsing and message buffering. Configured for hardware MIDI interface "hw:0,0" (first MIDI device).
| MidiHandler midiHandler(44100.0f, 1.0f) | ( | 44100. | 0f, |
| 1. | 0f ) |
High-level MIDI message processor and timing controller.
| sampleRate | 44100.0f Hz - Standard audio sample rate |
| jitterReduction | 1.0f - Temporal quantization factor for timing stability |
Implements sophisticated MIDI timing algorithms to handle note scheduling, velocity-sensitive triggering, and temporal message buffering for frame-accurate event processing.
| float oscillatorPhase = 0.0f |
Primary oscillator phase accumulator [0, 2π].
Maintains phase continuity for sine wave generation using direct digital synthesis (DDS) method. Phase wrapping prevents floating-point precision degradation over extended operation periods.
| float* outputBuffer = nullptr |
Output audio buffer for filtered audio.
Stores final processed audio after filter stage, ready for DAC output. Double-buffering architecture prevents audio artifacts during processing.
| PortamentoFilter portamentoFilter |
Portamento behavior analysis module.
Analyzes incoming MIDI note patterns to determine appropriate portamento behavior based on legato playing technique detection and note overlap timing.
| PortamentoPlayer portamentoPlayer(44100.0f, 100.0f) | ( | 44100. | 0f, |
| 100. | 0f ) |
Pitch interpolation and portamento synthesis engine.
| sampleRate | 44100.0f Hz - Audio processing rate |
| glideTime | 100.0f ms - Default portamento transition duration |
Implements exponential pitch interpolation algorithms for smooth frequency transitions between notes. Uses logarithmic frequency scaling to maintain perceptually linear pitch movement across the entire musical range.
| ResonanceRamp resonanceRamp(44100.0f, 50.0f) | ( | 44100. | 0f, |
| 50. | 0f ) |
Resonance parameter smoothing filter.
| sampleRate | 44100.0f Hz - Audio processing rate |
| rampTime | 50.0f ms - Parameter transition smoothing time |
Implements first-order low-pass filtering for resonance parameter changes to prevent audio artifacts from abrupt control changes, particularly important for high-Q filter settings where parameter discontinuities can cause instability.
| float twoPi = 2.0f * M_PI |
Precomputed 2π constant for computational efficiency.
Cached value eliminates repeated trigonometric calculations in the audio processing loop, reducing CPU overhead in real-time context.
| VelocityParser velocityParser(64) | ( | 64 | ) |
Velocity-sensitive note triggering processor.
| threshold | 64 - MIDI velocity threshold for note-on discrimination |
Implements hysteresis-based velocity parsing to distinguish between note-on and note-off events, preventing false triggering from low velocity note-on messages (common in electronic controllers).
| ZDFMoogLadderFilter zdfFilter(44100.0f) | ( | 44100. | 0f | ) |
Zero-delay feedback Moog ladder filter implementation.
| sampleRate | 44100.0f Hz - Audio processing rate |
Advanced digital implementation of the classic Moog transistor ladder filter using zero-delay feedback (ZDF) topology. Provides analog-accurate frequency response and self-oscillation behavior with numerical stability across all parameter ranges.