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

Unified foundation architecture for scalar and SIMD Moog ladder filter implementations. More...

#include <arm_neon.h>
#include <algorithm>
#include <cmath>

Go to the source code of this file.

Classes

class  MoogLadderFilterScalar
 High-fidelity scalar implementation of Huovilainen Moog ladder filter. More...
class  MoogLadderFilterSIMD
 High-performance SIMD implementation using ARM NEON vectorization. More...

Functions

float clamp (float x, float a, float b)
 High-performance parameter clamping for audio applications.
float fixdenorm (float val)
 Denormal number protection for sustained audio processing performance.
float32x4_t clamp_f32x4 (float32x4_t x, float min_val, float max_val)
 Vectorized parameter clamping for SIMD audio processing.
float32x4_t vdivq_f32 (float32x4_t a, float32x4_t b)
 Efficient vector division using Newton-Raphson approximation.

Detailed Description

Unified foundation architecture for scalar and SIMD Moog ladder filter implementations.

This header file establishes the architectural foundation for a comprehensive family of Moog ladder filter implementations, providing shared utility functions, common interface definitions, and unified design patterns that ensure consistency across scalar and vectorized implementations while maintaining optimal performance characteristics.

@architectural_philosophy The base architecture employs several key design principles:

Unified Interface Design: Common method signatures and parameter conventions across all implementation variants, enabling interchangeable usage and systematic performance comparison without requiring application code modifications.

Performance-Oriented Utilities: Shared utility functions optimized for both scalar and vector operations, including specialized denormal protection, parameter clamping, and SIMD-specific mathematical operations.

Implementation Flexibility: Support for both ARM NEON vectorized processing and traditional scalar operations within a cohesive framework that maximizes code reuse while enabling implementation-specific optimizations.

Research Validation Framework: Consistent interfaces that facilitate systematic algorithm comparison, performance analysis, and validation across different optimization approaches and computational paradigms.

@design_pattern_analysis The architecture implements several established software design patterns:

Template Method Pattern: Common interface structure with implementation-specific optimizations, allowing algorithmic variations while maintaining behavioral consistency.

Strategy Pattern: Interchangeable scalar and vector implementations that can be selected based on performance requirements and hardware capabilities.

Facade Pattern: Simplified interface that abstracts complex internal optimizations and provides clean, intuitive access to advanced filter functionality.

Utility Pattern: Shared helper functions that provide common functionality across multiple implementations without code duplication or performance overhead.

@performance_engineering_foundations The base architecture incorporates several performance engineering principles:

Computational Efficiency: Inline utility functions eliminate function call overhead while providing compiler optimization opportunities across all implementations.

Memory Optimization: Aligned data structures and cache-friendly access patterns optimized for modern processor architectures and memory hierarchies.

SIMD Readiness: Native support for ARM NEON vector operations with fallback mechanisms for platforms without vector processing capabilities.

Denormal Protection: Comprehensive safeguards against floating-point denormal numbers that can cause severe performance degradation in recursive algorithms.

@license_information GAIALIVE License - MIT-style permissive licensing

Created by Timothy Paul Read on 2025/5/25 Gaia Live DEV: gaialive.com Copyright (c) 2025 Timothy Paul Read

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Author
Timothy Paul Read
Date
2025/5/25 @organization Gaia Live DEV (gaialive.com)
Version
Final (updated with corrected setParams and process semantics)

Function Documentation

◆ clamp()

float clamp ( float x,
float a,
float b )
inline

High-performance parameter clamping for audio applications.

Parameters
xInput value to be constrained
aMinimum allowed value (inclusive)
bMaximum allowed value (inclusive)
Returns
Clamped value within specified range [a, b]

Provides efficient parameter range validation essential for preventing numerical instability and maintaining filter stability across all parameter ranges. The implementation uses STL min/max functions that compile to efficient conditional move instructions on modern processors.

@complexity O(1) - Two conditional comparisons @optimization Compiles to efficient conditional move instructions @thread_safety Thread-safe for concurrent read-only access

@mathematical_properties

  • Idempotent: clamp(clamp(x, a, b), a, b) = clamp(x, a, b)
  • Monotonic: If x₁ ≤ x₂, then clamp(x₁, a, b) ≤ clamp(x₂, a, b)
  • Range Preserving: Output always satisfies a ≤ result ≤ b
  • Identity Preserving: If a ≤ x ≤ b, then clamp(x, a, b) = x

@usage_examples

  • Cutoff frequency validation: clamp(frequency, 20.0f, sampleRate/2.0f)
  • Resonance parameter limiting: clamp(resonance, 0.0f, 1.0f)
  • Saturation input conditioning: clamp(signal, -1.0f, 1.0f)

@performance_considerations The function is marked inline to eliminate call overhead while enabling compiler optimizations. Modern compilers typically generate efficient conditional move instructions that avoid branch misprediction penalties.

◆ clamp_f32x4()

float32x4_t clamp_f32x4 ( float32x4_t x,
float min_val,
float max_val )
inline

Vectorized parameter clamping for SIMD audio processing.

Parameters
xInput vector containing four float values to be clamped
min_valMinimum allowed value applied to all vector lanes
max_valMaximum allowed value applied to all vector lanes
Returns
Vector with all lanes clamped to specified range

Provides efficient simultaneous range validation for four floating-point values using ARM NEON SIMD instructions. Essential for maintaining parameter stability in vectorized audio processing while leveraging parallel execution capabilities of modern ARM processors.

@complexity O(1) - Two vector comparison operations @simd_efficiency Processes 4 values with cost of single scalar operation @memory_bandwidth Optimized for aligned vector memory access patterns

@arm_neon_implementation Uses optimized ARM NEON instructions for parallel processing:

  • vmaxq_f32: Parallel maximum operation across four lanes
  • vminq_f32: Parallel minimum operation across four lanes
  • vdupq_n_f32: Efficient scalar-to-vector broadcast operation

@vectorization_benefits

  • Parallel Processing: Four simultaneous clamp operations
  • Instruction Efficiency: Single instruction operates on multiple data
  • Cache Optimization: Improved spatial locality through vector operations
  • Pipeline Utilization: Better processor pipeline efficiency
  • Power Efficiency: Reduced energy per operation compared to scalar processing

@applications

  • Multi-channel audio parameter validation
  • Polyphonic synthesizer voice processing
  • Simultaneous cutoff frequency limiting across filter banks
  • Batch processing of resonance parameters
  • Vector-based saturation and limiting operations

@performance_characteristics Theoretical performance benefits over scalar clamping:

  • Throughput: 4x improvement for aligned data processing
  • Latency: Minimal additional overhead from vectorization
  • Memory: Improved bandwidth utilization through vector loads
  • Energy: Reduced power consumption per operation on ARM processors

◆ fixdenorm()

float fixdenorm ( float val)
inline

Denormal number protection for sustained audio processing performance.

Parameters
valInput floating-point value to be checked and potentially corrected
Returns
0.0f if input is denormal, otherwise returns original value unchanged

Prevents denormal floating-point numbers that can cause severe CPU performance degradation in recursive digital filters. Denormal numbers occur when floating-point values become extremely small and require special processor handling that can be 100x slower than normal arithmetic operations.

@complexity O(1) - Single comparison and conditional assignment @threshold 1×10⁻³⁰ (well below any musically relevant signal levels) @performance_impact Critical for maintaining real-time performance guarantees

@denormal_number_theory IEEE 754 floating-point representation includes denormal (subnormal) numbers that represent values smaller than the minimum normalized value:

  • Normal Range: [1.175×10⁻³⁸, 3.403×10³⁸] for 32-bit float
  • Denormal Range: [1.401×10⁻⁴⁵, 1.175×10⁻³⁸] for 32-bit float
  • Processing Cost: 10-100x slower due to software emulation requirements
  • Musical Relevance: Far below noise floor of any practical audio system

@performance_analysis In recursive digital filters, denormal numbers can cause:

  • Severe CPU performance degradation (10-100x slowdown)
  • Inconsistent real-time performance characteristics
  • Audio dropouts in resource-constrained systems
  • Thermal throttling in mobile and embedded devices
  • Unpredictable latency in real-time audio applications

@threshold_selection_rationale The 1×10⁻³⁰ threshold is chosen because:

  • Well below -600dB (completely inaudible in any practical context)
  • Above IEEE 754 denormal range (catches all denormal conditions)
  • Preserves all musically relevant signal content
  • Provides consistent performance across processor architectures
  • Eliminates precision-related filter instabilities

@usage_patterns Typically applied to filter state variables and feedback signals:

  • Filter stage outputs before storage in delay lines
  • Feedback signals in recursive topologies
  • Envelope generator outputs approaching zero
  • LFO outputs during fade-in/fade-out periods

◆ vdivq_f32()

float32x4_t vdivq_f32 ( float32x4_t a,
float32x4_t b )
inline

Efficient vector division using Newton-Raphson approximation.

Parameters
aNumerator vector (dividend)
bDenominator vector (divisor)
Returns
Division result vector (a ÷ b)

Implements fast vector division essential for SIMD audio processing applications where standard division operations are either unavailable or prohibitively expensive. Uses ARM NEON reciprocal estimate with Newton-Raphson refinement to achieve acceptable accuracy for audio applications while maintaining vectorized performance.

@complexity O(1) - Fixed number of vector operations regardless of input values @accuracy Approximately 16-24 bit precision after refinement @performance Significantly faster than element-wise scalar division

@algorithm_implementation The implementation uses a two-stage process for optimal accuracy:

Stage 1: Initial Estimate

reciprocal = vrecpeq_f32(b); // Hardware reciprocal estimate (~8-bit accuracy)

Stage 2: Newton-Raphson Refinement

reciprocal = vmulq_f32(vrecpsq_f32(b, reciprocal), reciprocal); // ~16-bit accuracy

Stage 3: Final Multiplication

result = vmulq_f32(a, reciprocal); // a × (1/b) = a ÷ b

@mathematical_foundation Newton-Raphson method for reciprocal calculation:

Given f(x) = 1/x - c, finding root gives x = 1/c Iteration formula: x_{n+1} = x_n × (2 - c × x_n)

Where:

  • c = denominator value
  • x_0 = initial hardware estimate
  • Each iteration approximately doubles precision

@accuracy_analysis

  • Initial Estimate: ~8-bit precision (hardware vrecpeq_f32)
  • After 1 Iteration: ~16-bit precision (sufficient for most audio)
  • After 2 Iterations: ~24-bit precision (approaching float precision)
  • Error Characteristics: Monotonically decreasing with each iteration

@performance_comparison Compared to scalar division in vector context:

  • Scalar Approach: 4 individual division operations
  • Vector Approach: 1 estimate + 1 refinement + 1 multiply
  • Speedup: Typically 2-4x faster depending on processor implementation
  • Energy: Reduced power consumption per operation

@usage_contexts Essential for vectorized implementations of:

  • Rational function approximations (tanh, sigmoid)
  • Frequency normalization calculations
  • Gain and scaling factor applications
  • Complex mathematical expressions requiring division
  • Filter coefficient calculations with frequency-dependent terms

@limitations_and_considerations

  • Accuracy Trade-off: Slightly less precise than true division
  • Denormal Handling: May require additional protection for very small denominators
  • Special Values: Undefined behavior for zero denominators (requires pre-validation)
  • Range Sensitivity: Accuracy may vary across different magnitude ranges

Generate initial reciprocal estimate using ARM NEON hardware instruction Provides approximately 8-bit accuracy as starting point for refinement

Apply Newton-Raphson refinement to improve accuracy Formula: x₁ = x₀ × (2 - b × x₀) Implemented as: x₁ = x₀ × vrecpsq_f32(b, x₀) Improves accuracy to approximately 16-bit precision

Compute final division result using refined reciprocal a ÷ b = a × (1 ÷ b)