TR123e - EMCT Computing Final Project Version 1.0
Research Project Translation from gen~ to embedded Moog synth
Loading...
Searching...
No Matches
PortamentoPlayer Class Reference

High-precision frequency interpolation engine for musical portamento. More...

#include <PortamentoPlayer.h>

Public Member Functions

 PortamentoPlayer (float sampleRate, float defaultPortamentoTimeMs=100.0f)
 Construct PortamentoPlayer with audio system parameters.
void setPortamentoTime (float timeMs)
 Update portamento timing parameter in real-time.
void noteOn (int midiNote, bool portamentoOn)
 Trigger new note with optional portamento control.
void noteOff ()
 Signal note release for envelope coordination.
int getCurrentNote ()
 Query current MIDI note number.
float getCurrentFreq ()
 Query current instantaneous frequency.
float process ()
 Generate next frequency sample with interpolation processing.

Private Member Functions

float midiToFreq (int midiNote)
 Convert MIDI note number to frequency using equal temperament.
float interpolateFrequency ()
 Perform linear frequency interpolation for smooth transitions.

Private Attributes

int currentNote
 Current MIDI note number being processed.
float sampleRate
 Audio system sample rate in Hz.
float currentFreq
 Current instantaneous frequency in Hz.
float targetFreq
 Target frequency for interpolation in Hz.
float incrementPerSample
 Frequency increment per audio sample.
float portamentoTimeMs
 Portamento duration in milliseconds.
bool noteIsOn
 Note activity state flag.

Detailed Description

High-precision frequency interpolation engine for musical portamento.

The PortamentoPlayer class provides professional-grade pitch sliding capabilities with sample-accurate timing and configurable interpolation parameters. It handles MIDI note-to-frequency conversion, interpolation timing calculations, and real-time frequency generation suitable for direct oscillator control.

@design_principles

  • Sample Accuracy: All timing calculations performed at audio sample rate
  • Musical Precision: MIDI note conversion maintains standard tuning accuracy
  • Performance Oriented: Optimized for real-time audio processing constraints
  • State Consistency: Maintains frequency continuity across note transitions

@interpolation_characteristics

  • Linear Interpolation: Computationally efficient with acceptable musical results
  • Configurable Timing: User-controlled portamento duration (1ms-10s range)
  • Immediate Response: Optional instant frequency jumps for staccato playing
  • Sustain Capability: Maintains frequency during note release phases

@musical_applications

  • Monophonic lead synthesizers with expressive pitch control
  • Bass instruments requiring smooth frequency transitions
  • Vocal synthesis with natural pitch slides
  • String instrument emulation with portamento
  • Theremin-style continuous pitch instruments

@usage_example

PortamentoPlayer player(44100.0f, 150.0f); // 150ms default glide
// Note-on with portamento:
player.noteOn(60, true); // C4 with smooth transition
// Audio processing loop:
for (int i = 0; i < bufferSize; ++i) {
float frequency = player.process();
float sample = oscillator.process(frequency);
outputBuffer[i] = sample;
}
PortamentoPlayer(float sampleRate, float defaultPortamentoTimeMs=100.0f)
Construct PortamentoPlayer with audio system parameters.
Definition PortamentoPlayer.cpp:24
int bufferSize
Definition render_with_MOOGFILTER.cpp:116
float * outputBuffer
Definition render_with_MOOGFILTER.cpp:115
float frequency
Definition zdf_render.cpp:67

Constructor & Destructor Documentation

◆ PortamentoPlayer()

PortamentoPlayer::PortamentoPlayer ( float sampleRate,
float defaultPortamentoTimeMs = 100.0f )

Construct PortamentoPlayer with audio system parameters.

Initialize PortamentoPlayer with audio system parameters.

Parameters
sampleRateAudio processing sample rate in Hz Used for timing calculations and interpolation precision Typical values: 44100, 48000, 96000 Hz
defaultPortamentoTimeMsDefault portamento duration in milliseconds Configurable timing for pitch transitions Default: 100ms (musically appropriate for most contexts) Range: [1.0-10000.0]ms practical limits

@complexity O(1) - Constant time initialization @memory ~32 bytes object size (platform dependent) @thread_safety Safe for concurrent construction

@implementation_notes Constructor initializes all frequency state to 0.0f, ensuring clean startup behavior without frequency artifacts on first note event.

Parameters
sampleRateAudio processing rate for timing calculations
defaultPortamentoTimeMsInitial portamento duration setting

Initializes all frequency state to zero for clean startup behavior. No frequency output occurs until first noteOn() call establishes valid frequency values.

Member Function Documentation

◆ getCurrentFreq()

float PortamentoPlayer::getCurrentFreq ( )

Query current instantaneous frequency.

Returns
Current frequency in Hz (may be interpolating) Represents the exact frequency at current processing moment

@complexity O(1) - Direct member variable access @precision Full floating-point precision @range [0.0, ~13289.75]Hz (MIDI note 0-127 frequency range)

@applications

  • Frequency display/monitoring
  • Secondary oscillator synchronization
  • Frequency-dependent processing (filters, effects)
  • Analysis and debugging
Returns
Current frequency in Hz (potentially interpolating)

Direct frequency accessor for monitoring and secondary processing applications. Provides real-time frequency value without affecting interpolation state.

◆ getCurrentNote()

int PortamentoPlayer::getCurrentNote ( )

Query current MIDI note number.

Returns
Current MIDI note number being processed Returns last note passed to noteOn() regardless of interpolation state

@complexity O(1) - Direct member variable access @thread_safety Safe for concurrent read access

@use_cases

  • Keyboard tracking calculations
  • Filter frequency modulation
  • Display/UI updates
  • MIDI echo/monitoring
Returns
Most recent MIDI note passed to noteOn()

Simple accessor method for external state queries and keyboard tracking applications. Returns stored value regardless of current interpolation state.

◆ interpolateFrequency()

float PortamentoPlayer::interpolateFrequency ( )
private

Perform linear frequency interpolation for smooth transitions.

Execute linear frequency interpolation with completion detection.

Returns
Current interpolated frequency value

Core interpolation algorithm implementing sample-accurate linear frequency transitions. Handles completion detection and state cleanup when target frequency is reached within numerical precision limits.

Returns
Current interpolated frequency value

Core interpolation algorithm implementing sample-accurate linear frequency transitions with automatic completion detection and numerical stability.

@algorithm_analysis The method implements a three-stage decision process:

  1. Static Check: Return immediately if no interpolation active
  2. Completion Test: Detect when target frequency reached within tolerance
  3. Increment Application: Apply linear interpolation step

@numerical_stability Uses absolute value comparison with increment tolerance to prevent oscillation around target frequency due to floating-point precision limits. This ensures clean completion detection regardless of interpolation direction.

@convergence_analysis Convergence condition: |targetFreq - currentFreq| ≤ |incrementPerSample|

This guarantees completion within one additional sample period, preventing infinite interpolation due to numerical precision limitations.

Early return for static frequency (no interpolation active) Optimizes common case where frequency has reached target

Completion detection with numerical tolerance

Checks if remaining frequency difference is smaller than the interpolation increment, indicating completion within one sample period. Uses absolute values to handle both positive and negative frequency transitions correctly.

Interpolation complete: snap to exact target frequency and disable further interpolation processing

Continue linear interpolation: apply calculated increment for smooth frequency progression toward target

◆ midiToFreq()

float PortamentoPlayer::midiToFreq ( int midiNote)
private

Convert MIDI note number to frequency using equal temperament.

Convert MIDI note to frequency using equal temperament.

Parameters
midiNoteMIDI note number [0-127]
Returns
Corresponding frequency in Hz

Implements standard equal temperament conversion formula with 440 Hz reference for A4 (MIDI note 69). Provides standard musical tuning compatible with conventional instruments and software.

Parameters
midiNoteMIDI note number input
Returns
Corresponding frequency in Hz

@mathematical_implementation Uses standard equal temperament formula: f = 440 * 2^((n-69)/12)

Reference: A4 (MIDI note 69) = 440.0 Hz Semitone ratio: 2^(1/12) ≈ 1.059463 (12th root of 2)

@precision_analysis

  • IEEE 754 single precision: ~7 decimal digits accuracy
  • Frequency resolution: ~0.01 Hz at middle frequencies
  • Musical accuracy: Better than 0.1 cent across full MIDI range
  • Suitable for professional audio applications

@performance_characteristics

  • Single pow() function call: ~10-20 CPU cycles on modern processors
  • FPU pipeline friendly: no conditional branching
  • Cache efficient: no memory access required

◆ noteOff()

void PortamentoPlayer::noteOff ( )

Signal note release for envelope coordination.

Signal note release while maintaining frequency output.

Marks note as released while maintaining current frequency for envelope release phase. Frequency continues processing to support amplitude envelope decay without pitch artifacts.

@complexity O(1) - Simple state flag update @realtime_safety Real-time safe (no blocking operations)

@behavioral_notes

  • Does not immediately stop frequency output
  • Allows continued frequency generation during envelope release
  • Frequency freezes when interpolation completes
  • Enables natural amplitude decay without pitch drift

Simple state update allowing continued frequency generation during amplitude envelope release phase. Frequency interpolation continues if active, providing smooth pitch behavior during note decay.

◆ noteOn()

void PortamentoPlayer::noteOn ( int midiNote,
bool portamentoOn )

Trigger new note with optional portamento control.

Process note-on event with portamento control.

Initiates frequency transition to new MIDI note with configurable portamento behavior. Calculates target frequency and interpolation parameters for smooth real-time processing.

Parameters
midiNoteTarget MIDI note number [0-127] Converted to frequency using equal temperament formula Note 69 (A4) = 440.0 Hz reference
portamentoOnEnable/disable portamento for this transition true: Smooth interpolation from current frequency false: Immediate frequency jump (staccato)

@complexity O(1) - Constant time note processing @precision IEEE 754 floating-point arithmetic accuracy @realtime_safety Real-time safe (deterministic execution time)

@algorithm_behavior

  • If portamentoOn == false OR currentFreq == 0.0: Immediate frequency jump
  • If portamentoOn == true AND currentFreq != 0.0: Calculate interpolation
  • Interpolation uses linear frequency domain for computational efficiency
  • Sample-accurate timing based on portamentoTimeMs and sample rate

@state_updates Updates internal state variables:

  • targetFreq: Set to new note frequency
  • currentNote: Updated for external state queries
  • noteIsOn: Set to true for processing control
  • incrementPerSample: Calculated for linear interpolation
Parameters
midiNoteTarget MIDI note number
portamentoOnEnable smooth frequency transition

@algorithm_implementation The method implements conditional behavior based on portamento settings and current frequency state:

  1. Target Frequency Calculation: Always performed using equal temperament
  2. Interpolation Decision: Based on portamentoOn flag and current state
  3. Timing Calculation: Sample-accurate interpolation step computation

@mathematical_precision Interpolation timing: samples = (timeMs / 1000.0) * sampleRate Step size: increment = (targetFreq - currentFreq) / samples

This provides linear frequency interpolation with exact timing control independent of audio buffer sizes or system scheduling variations.

Calculate target frequency using equal temperament conversion Always performed to ensure proper frequency reference regardless of portamento settings

Determine interpolation behavior based on portamento settings and current frequency state

Immediate frequency jump for:

  • Explicit portamento disable (staccato playing)
  • First note after silence (no source frequency for interpolation)

Calculate smooth interpolation parameters for legato transition

Convert portamento time to sample count for precise timing: portamentoSamples = (timeMs / 1000.0) * sampleRate

Calculate linear interpolation increment: incrementPerSample = frequency_difference / time_in_samples

Update state variables for processing and external queries

◆ process()

float PortamentoPlayer::process ( )

Generate next frequency sample with interpolation processing.

Main processing method for real-time frequency generation.

Core processing method called once per audio sample to generate current frequency value with smooth interpolation. Handles all interpolation logic, completion detection, and state management.

Returns
Current frequency in Hz for oscillator control Smoothly interpolated value between source and target frequencies

@complexity O(1) - Constant time per sample processing @determinism Bounded execution time regardless of interpolation state @realtime_safety Real-time safe (no allocation or blocking)

@processing_logic

  1. Check if interpolation is active (noteIsOn OR frequency != target)
  2. If active: Call interpolateFrequency() for smooth progression
  3. If inactive: Return current frequency (sustain for envelope release)

@state_management

  • Automatically detects interpolation completion
  • Maintains frequency during note release phase
  • Handles edge cases (zero frequency, target reached)
  • Provides continuous output for audio processing chain

@call_pattern Must be called exactly once per audio sample for proper timing:

for (int sample = 0; sample < bufferSize; ++sample) {
float freq = player.process();
// Use frequency for oscillator control...
}
Returns
Current frequency for oscillator control

@processing_strategy Implements conditional processing based on note state and interpolation status:

  1. Active Processing: Note is on OR frequency interpolation incomplete
  2. Sustain Mode: Note is off but maintain frequency for envelope release

This approach ensures continuous frequency output during amplitude envelope release phases while allowing interpolation to complete naturally.

@real_time_considerations

  • Deterministic execution time: No loops or recursive calls
  • Minimal branching: CPU branch predictor friendly
  • Single function call: interpolateFrequency() when active
  • Cache efficient: All data in single object instance

@musical_behavior The processing continues frequency generation after note-off to support natural amplitude envelope decay without pitch artifacts. This matches the behavior of acoustic instruments and traditional analog synthesizers.

Determine if active frequency processing is required

Conditions for active processing:

  • noteIsOn: Note is currently active (before note-off)
  • currentFreq != targetFreq: Interpolation still in progress

This logic ensures frequency continues updating during:

  • Active note playing with interpolation
  • Note release phase with ongoing interpolation
  • Sustain phase maintaining constant frequency

Sustain current frequency for envelope release

When note is off and target frequency reached, maintain current frequency to support amplitude envelope decay without pitch drift or artifacts

◆ setPortamentoTime()

void PortamentoPlayer::setPortamentoTime ( float timeMs)

Update portamento timing parameter in real-time.

Update portamento timing parameter.

Parameters
timeMsNew portamento duration in milliseconds Controls the time required for complete pitch transitions Affects only future note events (current glides unchanged)

@range [0.1-10000.0]ms practical limits for musical applications @precision Full floating-point precision maintained @realtime_safety Real-time safe (no allocation or blocking)

@musical_considerations

  • Short times (1-50ms): Rapid pitch slides, good for fast passages
  • Medium times (50-200ms): Classic portamento feel, most musical
  • Long times (200ms+): Dramatic pitch sweeps, special effects
Note
Changes take effect immediately but only apply to subsequent noteOn() calls. Active portamento continues with original timing.
Parameters
timeMsNew portamento duration in milliseconds

Simple parameter update affecting future note transitions. No validation performed for maximum real-time performance.

Member Data Documentation

◆ currentFreq

float PortamentoPlayer::currentFreq
private

Current instantaneous frequency in Hz.

Real-time frequency value updated each sample during interpolation. Represents the exact frequency being output for oscillator control.

◆ currentNote

int PortamentoPlayer::currentNote
private

Current MIDI note number being processed.

Stores the most recent MIDI note number passed to noteOn() for external state queries and keyboard tracking applications.

◆ incrementPerSample

float PortamentoPlayer::incrementPerSample
private

Frequency increment per audio sample.

Calculated interpolation step size for linear frequency progression. Value determined by (targetFreq - currentFreq) / portamentoSamples providing precise timing control over frequency transitions.

◆ noteIsOn

bool PortamentoPlayer::noteIsOn
private

Note activity state flag.

Boolean indicator of current note status for processing control. Used to determine when frequency generation should continue during envelope release phases after note-off events.

◆ portamentoTimeMs

float PortamentoPlayer::portamentoTimeMs
private

Portamento duration in milliseconds.

User-configurable timing parameter controlling the duration of frequency transitions. Converted to sample count for precise interpolation timing calculations.

◆ sampleRate

float PortamentoPlayer::sampleRate
private

Audio system sample rate in Hz.

Cached sample rate used for timing calculations and interpolation precision. Enables sample-accurate portamento timing independent of audio system configuration.

◆ targetFreq

float PortamentoPlayer::targetFreq
private

Target frequency for interpolation in Hz.

Destination frequency calculated from MIDI note number using equal temperament conversion. Interpolation progresses toward this value over the configured portamento duration.


The documentation for this class was generated from the following files: