FastLED 3.9.15
Loading...
Searching...
No Matches
teensy_audio_notefreq Directory Reference
+ Directory dependency graph for teensy_audio_notefreq:

Detailed Description

Pulled from: https://github.com/PaulStoffregen/Audio/tree/master (analyze_notefreq.cpp and analyze_notefreq.h)

Current Status

Overview

The Teensy Audio Library's analyze_notefreq component provides real-time fundamental frequency detection using the Yin algorithm, designed specifically for musical note detection and tuning applications. This component is MIT licensed and authored by Colin Duffy (2015).

Key Features

Original API

Class: AudioAnalyzeNoteFrequency

Public Methods

// Initialize frequency detection with threshold and buffer configuration
bool begin(float threshold = 0.15);
// Set detection uncertainty threshold (0.0 to 1.0)
void threshold(float p);
// Check if a valid frequency has been detected
bool available(void);
// Get detected frequency in Hertz
float read(void);
// Get confidence of frequency detection (0.0 to 1.0)
float probability(void);

Configuration Parameters

Algorithm Details

The Yin algorithm is a well-established pitch detection algorithm that:

  1. Computes the autocorrelation function using cumulative mean normalized difference
  2. Identifies the first local minimum below the threshold
  3. Applies parabolic interpolation for sub-sample accuracy
  4. Returns frequency estimate with confidence measure

Advantages:

FastLED Third-Party Namespace Architecture

FastLED mandates that all third-party libraries be wrapped in the fl::third_party namespace to:

1. Prevent Global Namespace Pollution

// Before: Global namespace collision risk
class AudioAnalyzeNoteFrequency { /* ... */ };
// After: Safely namespaced
namespace fl {
namespace third_party {
namespace teensy_audio {
class AudioAnalyzeNoteFrequency { /* ... */ };
}
}
}
Base definition for an LED controller.
Definition crgb.hpp:179

2. Clear Ownership and Licensing Boundaries

The fl::third_party::teensy_audio namespace makes it immediately clear:

3. Controlled Integration Points

// Third-party code stays isolated
namespace fl::third_party::teensy_audio {
class AudioAnalyzeNoteFrequency { /* Original Teensy Audio API */ };
}
// FastLED provides clean wrappers
namespace fl {
class NoteFrequencyAnalyzer { // Clean C++ API following FastLED conventions
static float detectFrequency(const AnalyzerConfig& config,
fl::span<const float> audio_samples,
float* confidence = nullptr);
private:
fl::third_party::teensy_audio::AudioAnalyzeNoteFrequency analyzer_;
};
}

Proposed FastLED Integration

Phase 1: Library Staging and Namespace Wrapping

1.1 File Structure

src/third_party/teensy_audio_notefreq/
├── README.md (this file)
├── LICENSE.txt (MIT license from original)
├── analyze_notefreq.h (wrapped in fl::third_party::teensy_audio)
├── analyze_notefreq.cpp (wrapped in fl::third_party::teensy_audio)
└── AudioStream.h (minimal stub for Teensy Audio dependency)

1.2 Namespace Wrapping

// analyze_notefreq.h
namespace fl {
namespace third_party {
namespace teensy_audio {
class AudioAnalyzeNoteFrequency {
// Original API preserved
};
}
}
}

1.3 Dependency Handling

The original code depends on Teensy Audio's AudioStream class for audio block management. Options:

Option A: Minimal Stub (Recommended)

Option B: Direct Port

Phase 2: FastLED API Wrapper

2.1 Configuration Structure

namespace fl {
struct NoteFrequencyConfig {
// Detection parameters
float threshold = 0.15f; // Uncertainty threshold (0.0 to 1.0)
float minFrequency = 29.14f; // Minimum detectable frequency (Hz)
float maxFrequency = 5000.0f; // Maximum detectable frequency (Hz)
// Buffer configuration
uint32_t sampleRate = 44100; // Audio sample rate
uint32_t bufferSize = 3072; // Analysis buffer size (samples)
// Output options
bool requireHighConfidence = false; // Only return high-confidence results
float minConfidence = 0.5f; // Minimum confidence threshold
};
}

2.2 Clean FastLED API

namespace fl {
class NoteFrequencyAnalyzer {
public:
// Static detection method
static bool detectFrequency(const NoteFrequencyConfig& config,
fl::span<const float> audio_samples,
float* frequency,
float* confidence = nullptr,
fl::string* error_message = nullptr);
// Platform support detection
static bool isSupported();
// Stateful analyzer for streaming audio
class StreamingAnalyzer {
public:
explicit StreamingAnalyzer(const NoteFrequencyConfig& config);
// Process audio samples incrementally
void processSamples(fl::span<const float> samples);
// Check if frequency is available
bool available() const;
// Get detected frequency
float frequency() const;
// Get detection confidence
float confidence() const;
private:
fl::third_party::teensy_audio::AudioAnalyzeNoteFrequency analyzer_;
NoteFrequencyConfig config_;
};
};
}

Phase 3: Integration Implementation

3.1 Bridge Implementation (fl/audio/note_frequency.cpp)

namespace fl {
class NoteFrequencyAnalyzerImpl {
public:
NoteFrequencyAnalyzerImpl(const NoteFrequencyConfig& config)
: config_(config) {
analyzer_.begin(config.threshold);
}
bool detect(fl::span<const float> samples, float* freq, float* conf) {
// Feed samples to analyzer
feedSamples(samples);
// Check if frequency detected
if (!analyzer_.available()) {
return false;
}
// Get results
float detected_freq = analyzer_.read();
float detected_conf = analyzer_.probability();
// Apply configuration filters
if (detected_freq < config_.minFrequency ||
detected_freq > config_.maxFrequency) {
return false;
}
if (config_.requireHighConfidence &&
detected_conf < config_.minConfidence) {
return false;
}
// Return results
if (freq) *freq = detected_freq;
if (conf) *conf = detected_conf;
return true;
}
private:
void feedSamples(fl::span<const float> samples);
fl::third_party::teensy_audio::AudioAnalyzeNoteFrequency analyzer_;
NoteFrequencyConfig config_;
};
}

3.2 Memory Management

3.3 Type Conversions

// Teensy Audio uses float samples in range [-1.0, 1.0]
// FastLED may use int16_t or other formats
void convertSamples(fl::span<const int16_t> input,
fl::span<float> output) {
for (size_t i = 0; i < input.size(); ++i) {
output[i] = input[i] / 32768.0f;
}
}
constexpr fl::size size() const FL_NOEXCEPT
Definition span.h:458

Integration Challenges and Solutions

Challenge 1: AudioStream Dependency

Problem: Original code inherits from AudioStream class for Teensy Audio framework integration.

Solutions:

  1. Minimal Stub Approach: Create lightweight AudioStream base class with required virtual methods
  2. Direct Algorithm Port: Extract Yin algorithm core, remove streaming framework dependency
  3. Adapter Pattern: Wrap analyzer and translate between FastLED and Teensy Audio semantics

Recommendation: Use Minimal Stub for fastest integration, preserving original code quality.

Challenge 2: Audio Block Management

Problem: Teensy Audio uses specific audio block structure (128 samples per block, reference counting).

Solutions:

  1. Emulate Blocks: Create compatible block structure in stub
  2. Buffer Conversion: Convert FastLED audio buffers to block format on-the-fly
  3. Algorithm Refactor: Modify to accept arbitrary buffer sizes

Recommendation: Emulate blocks in stub for minimal code changes.

Challenge 3: Platform Dependencies

Problem: Original code may have ARM-specific optimizations or Teensy hardware assumptions.

Solutions:

  1. Portable Fallbacks: Ensure C++ implementation works on all platforms
  2. Feature Detection: Use #ifdef guards for platform-specific optimizations
  3. FastLED Portability: Leverage existing FastLED cross-platform patterns

Recommendation: Test on multiple platforms early, add portable fallbacks as needed.

Challenge 4: Real-time Requirements

Problem: Musical note detection requires consistent low-latency processing.

Solutions:

  1. Performance Profiling: Benchmark on target embedded platforms
  2. Buffer Size Tuning: Allow configuration of latency vs. accuracy trade-off
  3. Optimization: Consider fixed-point math for microcontrollers if needed

Testing Strategy

Unit Tests (tests/audio_note_frequency.cpp)

Phase 1: Basic Functionality

TEST_CASE("NoteFrequency initialization") {
fl::NoteFrequencyConfig config;
fl::NoteFrequencyAnalyzer::StreamingAnalyzer analyzer(config);
CHECK(analyzer.confidence() == 0.0f);
}
TEST_CASE("NoteFrequency known frequency detection") {
// Generate pure 440Hz sine wave (A4)
std::vector<float> samples = generateSineWave(440.0f, 1.0f, 44100, 4096);
float freq, conf;
bool detected = fl::NoteFrequencyAnalyzer::detectFrequency(
fl::NoteFrequencyConfig{}, samples, &freq, &conf);
CHECK(detected);
CHECK(freq == Approx(440.0f).epsilon(0.01)); // Within 1%
CHECK(conf > 0.8f); // High confidence
}

Phase 2: Musical Note Range

TEST_CASE("NoteFrequency musical note range") {
// Test standard guitar tuning: E2, A2, D3, G3, B3, E4
float notes[] = {82.41f, 110.0f, 146.83f, 196.0f, 246.94f, 329.63f};
for (float expected : notes) {
auto samples = generateSineWave(expected, 1.0f, 44100, 4096);
float detected;
bool success = fl::NoteFrequencyAnalyzer::detectFrequency(
fl::NoteFrequencyConfig{}, samples, &detected);
CHECK(success);
CHECK(detected == Approx(expected).epsilon(0.02));
}
}

Phase 3: Edge Cases

TEST_CASE("NoteFrequency low SNR handling") {
// Test with noisy signal
auto signal = generateSineWave(440.0f, 0.5f, 44100, 4096);
auto noise = generateWhiteNoise(0.3f, 4096);
for (size_t i = 0; i < signal.size(); ++i) {
signal[i] += noise[i];
}
float freq, conf;
bool detected = fl::NoteFrequencyAnalyzer::detectFrequency(
fl::NoteFrequencyConfig{}, signal, &freq, &conf);
// Should still detect but with lower confidence
if (detected) {
CHECK(freq == Approx(440.0f).epsilon(0.05));
CHECK(conf < 0.9f); // Lower confidence expected
}
}
TEST_CASE("NoteFrequency harmonic-rich signal") {
// Test with square wave (odd harmonics)
auto samples = generateSquareWave(440.0f, 1.0f, 44100, 4096);
float freq;
bool detected = fl::NoteFrequencyAnalyzer::detectFrequency(
fl::NoteFrequencyConfig{}, samples, &freq);
CHECK(detected);
CHECK(freq == Approx(440.0f).epsilon(0.02)); // Should find fundamental
}
uint8_t noise[NUM_LAYERS][WIDTH][HEIGHT]
Definition Fire2023.h:97

Test Data Requirements

Integration Tests

Build System Integration

CMake Configuration

# src/third_party/teensy_audio_notefreq/CMakeLists.txt
add_library(teensy_audio_notefreq
analyze_notefreq.cpp
# AudioStream.cpp (if needed)
)
target_include_directories(teensy_audio_notefreq
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)
# Add to FastLED build
target_link_libraries(FastLED PRIVATE teensy_audio_notefreq)

Platform Support

// fl/audio/note_frequency.h
namespace fl {
inline bool NoteFrequencyAnalyzer::isSupported() {
#if defined(ESP32) || defined(ARDUINO) || defined(__linux__) || defined(_WIN32)
return true; // Algorithm is portable
#else
return false;
#endif
}
}

Documentation Requirements

API Documentation (fl/audio/note_frequency.h)

class NoteFrequencyAnalyzer { /* ... */ };

User Guide Section

Add to FastLED documentation:

Timeline and Milestones

Milestone 1: Library Integration (Est. 1-2 days)

Milestone 2: FastLED API Wrapper (Est. 2-3 days)

Milestone 3: Testing and Validation (Est. 2-3 days)

Milestone 4: Documentation and Examples (Est. 1-2 days)

Success Criteria

Functional Requirements

✅ Detects fundamental frequency of musical notes (E2-E6: 82Hz-1318Hz) ✅ Accuracy within 2% of true frequency for clean signals ✅ Processes audio in real-time on ESP32/Teensy platforms ✅ Provides confidence metric for detection quality ✅ Handles harmonic-rich signals (guitar, bass, voice)

Code Quality

✅ All code in fl::third_party::teensy_audio namespace ✅ Clean FastLED API following project conventions ✅ Comprehensive error handling and validation ✅ Full test coverage with unit and integration tests ✅ Documented API with usage examples

Performance

✅ Processing latency < 100ms for typical configurations ✅ Memory footprint < 20KB for analyzer instance ✅ No dynamic allocation in real-time processing path ✅ Consistent performance across FastLED-supported platforms

License and Attribution

Original Work:

MIT License Summary: Permission is hereby granted, free of charge, to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, subject to including the copyright notice and permission notice in all copies.

Integration into FastLED:

References

Yin Algorithm

Teensy Audio Library

FastLED Integration Patterns


This integration document serves as a blueprint for incorporating Teensy Audio's note frequency detection into FastLED, following established third-party integration patterns and maintaining code quality standards.