arduino-audio-tools
Loading...
Searching...
No Matches
GoerzelStream.h
Go to the documentation of this file.
1#pragma once
2
3#include <math.h>
4
5#include "AudioStreams.h"
6
7#ifndef M_PI
8#define M_PI (3.14159265358979323846f)
9#endif
10
11namespace audio_tools {
12
24struct GoertzelConfig : public AudioInfo {
26 float target_frequency = 0.0f;
29 int block_size = 205;
32 float threshold = 0.5f;
34 float volume = 1.0f;
36 uint8_t channel = 0;
37
38 GoertzelConfig() = default;
40 GoertzelConfig(const AudioInfo& info) : AudioInfo(info) {
41 target_frequency = 440.0f;
42 block_size = 205;
43 threshold = 0.5f;
44 }
45};
46
63 public:
64 GoertzelDetector() = default;
65
71 this->config = config;
72 this->sample_count = 0;
73
74 if (config.target_frequency == 0.0f) {
75 return false;
76 }
77
78 // Calculate the coefficient k and omega
79 float omega = (2.0f * M_PI * config.target_frequency) / config.sample_rate;
80 coeff = 2.0f * cos(omega);
81
82 // Reset state variables
83 reset();
84 return true;
85 }
86
92 bool processSample(float sample) {
93 // Goertzel algorithm core computation
94 float s0 = sample + coeff * s1 - s2;
95 s2 = s1;
96 s1 = s0;
97
99
100 // Check if we've processed a complete block
102 // Calculate magnitude squared
103 float real = s1 - s2 * cos(2.0f * M_PI * config.target_frequency /
105 float imag =
107 magnitude_squared = (real * real) + (imag * imag);
109
110 // Reset for next block
111 reset();
112 return true;
113 }
114
115 return false;
116 }
117
122 float getMagnitude() const { return magnitude; }
123
129 float getMagnitudeSquared() const { return magnitude_squared; }
130
136 bool isDetected(float threshold) const { return magnitude > threshold; }
137
142 bool isDetected() const { return isDetected(config.threshold); }
143
147 void reset() {
148 s1 = 0.0f;
149 s2 = 0.0f;
150 sample_count = 0;
151 magnitude_squared = 0.0f;
152 }
153
158
162 int getBlockSize() const { return config.block_size; }
163
167 const GoertzelConfig& getConfig() const { return config; }
168
169 void setReference(void* ref) { this->reference = ref; }
170
171 void* getReference() { return reference; }
172
173 protected:
175 float coeff = 0.0f;
176 void* reference = nullptr;
177
178 // State variables
179 float s1 = 0.0f;
180 float s2 = 0.0f;
182
183 // Results
184 float magnitude = 0.0f;
185 float magnitude_squared = 0.0f;
186};
187
226 public:
227 // Default Constructor with no output or input
228 GoertzelStream() = default;
233
244 void setAudioInfo(AudioInfo info) override {
247 // Initialize detectors for each frequency
248 begin();
249 }
250
261 GoertzelConfig result;
262 return result;
263 }
264
275 bool begin(GoertzelConfig config) {
276 config.target_frequency = 0;
277 if (config.channel >= config.channels) {
278 LOGE("GoertzelStream: channel %d out of range (max %d)", config.channel,
279 config.channels);
280 return false;
281 }
282 this->default_config = config;
283
284 // Set up audio info from config
285 setAudioInfo(config);
286 return begin();
287 }
288
297 bool begin() {
298 int i = 0;
299 detectors.clear();
300 for (float freq : frequencies) {
302 cfg.target_frequency = freq;
303 GoertzelDetector detector;
304 if (i < references.size()) {
305 detector.setReference(references[i]);
306 }
307 detector.begin(cfg);
308 detectors.push_back(detector);
309 i++;
310 }
311 sample_no = 0;
312 return true;
313 }
314
318 void end() {
319 detectors.clear();
321 }
322
324 void setStream(Stream& in) {
325 p_stream = &in;
326 p_print = &in;
327 }
328
331 setStream((Stream&)io);
333 }
334
336 void setOutput(Print& out) { p_print = &out; }
337
340 setOutput((Print&)out);
342 }
343
355 void setFrequencyDetectionCallback(void (*callback)(float frequency,
356 float magnitude,
357 void* ref)) {
359 }
360
373 size_t write(const uint8_t* data, size_t len) override {
374 // Process samples for detection
375 processSamples(data, len);
376
378 if (p_print == nullptr) return len;
379
380 // Pass data through unchanged
381 return p_print->write(data, len);
382 }
383
396 size_t readBytes(uint8_t* data, size_t len) override {
397 if (p_stream == nullptr) return 0;
398
399 size_t result = p_stream->readBytes(data, len);
400
401 // Process samples for detection
402 processSamples(data, result);
403
404 return result;
405 }
406
415 const GoertzelConfig& getConfig() const { return default_config; }
416
426 void setReference(void* ref) { this->ref = ref; }
427
437 GoertzelDetector& getDetector(int no) { return detectors[no]; }
438
444 void addFrequency(float freq) { frequencies.push_back(freq); }
445
458 void addFrequency(float freq, void* ref) {
461 }
462
463 protected:
464 // Core detection components
470 // Stream I/O components
471 Stream* p_stream = nullptr;
472 Print* p_print = nullptr;
473
474 // Callback system
475 void (*frequency_detection_callback)(float frequency, float magnitude,
476 void* ref) =
477 nullptr;
478 void* ref = this;
479
480 size_t sample_no = 0;
481
486 float magnitude = detector.getMagnitude();
487 if (magnitude > 0.0f)
488 LOGD("frequency: %f / magnitude: %f / threshold: %f", detector.getTargetFrequency(), magnitude, default_config.threshold);
489
490 if (magnitude > default_config.threshold) {
491 float frequency = detector.getTargetFrequency();
492 void* reference = detector.getReference();
493 if (reference == nullptr) reference = ref;
494 if (frequency_detection_callback != nullptr) {
495 frequency_detection_callback(frequency, magnitude, reference);
496 }
497 }
498 }
499
513 template <typename T = int16_t>
514 void processSamplesOfType(const uint8_t* data, size_t data_len,
515 int channels) {
516 const T* samples = reinterpret_cast<const T*>(data);
517 size_t num_samples = data_len / sizeof(T);
518
519 for (size_t i = 0; i < num_samples; i++) {
520 // select only samples for the configured channel
521 if (sample_no % channels == default_config.channel) {
522 float normalized = clip(NumberConverter::toFloatT<T>(samples[i]) *
524 LOGD("sample: %f", normalized);
525 // process all frequencies
526 for (auto& detector : detectors) {
527 if (detector.processSample(normalized)) {
528 checkDetection(detector);
529 }
530 }
531 }
532 sample_no++;
533 }
534 }
535
545 float clip(float value) {
546 // Clip the value to the range [-1.0, 1.0]
547 if (value > 1.0f) return 1.0f;
548 if (value < -1.0f) return -1.0f;
549 return value;
550 }
551
569 void processSamples(const uint8_t* data, size_t data_len) {
570 // return if there is nothing to detect
571 if (detectors.empty()) return;
572 int channels = default_config.channels;
573
575 case 8:
576 processSamplesOfType<uint8_t>(data, data_len, channels);
577 break;
578 case 16:
579 processSamplesOfType<int16_t>(data, data_len, channels);
580 break;
581 case 24:
582 processSamplesOfType<int24_t>(data, data_len, channels);
583 break;
584 case 32:
585 processSamplesOfType<int32_t>(data, data_len, channels);
586 break;
587 default:
588 LOGE("Unsupported bits_per_sample: %d", default_config.bits_per_sample);
589 break;
590 }
591 }
592};
593
594} // namespace audio_tools
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define M_PI
Definition MDFEchoCancellationConfig.h:66
Definition Arduino.h:56
virtual size_t write(const uint8_t *data, size_t len)
Definition Arduino.h:120
Definition Arduino.h:136
virtual size_t readBytes(uint8_t *data, size_t len)
Definition Arduino.h:140
virtual void addNotifyAudioChange(AudioInfoSupport &bi)
Adds target to be notified about audio changes.
Definition AudioTypes.h:149
Abstract Audio Ouptut class.
Definition AudioOutput.h:25
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:120
AudioInfo info
Definition BaseStream.h:171
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition BaseStream.h:128
virtual void end()
Definition BaseStream.h:41
Single-frequency Goertzel algorithm implementation for tone detection. The Goertzel algorithm is an e...
Definition GoerzelStream.h:62
float s2
Definition GoerzelStream.h:180
void * getReference()
Definition GoerzelStream.h:171
int getBlockSize() const
Get the block size.
Definition GoerzelStream.h:162
bool isDetected() const
Check if the detected magnitude is above the configured threshold.
Definition GoerzelStream.h:142
bool begin(const GoertzelConfig &config)
Initialize the Goertzel detector with configuration.
Definition GoerzelStream.h:70
float s1
Definition GoerzelStream.h:179
float coeff
Definition GoerzelStream.h:175
GoertzelConfig config
Definition GoerzelStream.h:174
float getMagnitudeSquared() const
Get the squared magnitude (more efficient if you don't need the actual magnitude)
Definition GoerzelStream.h:129
bool processSample(float sample)
Process a single sample.
Definition GoerzelStream.h:92
bool isDetected(float threshold) const
Check if the detected magnitude is above a threshold.
Definition GoerzelStream.h:136
float magnitude
Definition GoerzelStream.h:184
const GoertzelConfig & getConfig() const
Get the current configuration.
Definition GoerzelStream.h:167
void setReference(void *ref)
Definition GoerzelStream.h:169
int sample_count
Definition GoerzelStream.h:181
float getMagnitude() const
Get the magnitude of the detected frequency.
Definition GoerzelStream.h:122
float getTargetFrequency() const
Get the target frequency.
Definition GoerzelStream.h:157
void reset()
Reset the detector state.
Definition GoerzelStream.h:147
float magnitude_squared
Definition GoerzelStream.h:185
void * reference
Definition GoerzelStream.h:176
AudioStream-based multi-frequency Goertzel detector for real-time audio analysis.
Definition GoerzelStream.h:225
void setStream(AudioStream &io)
Defines/Changes the input & output.
Definition GoerzelStream.h:330
GoertzelStream(Print &out)
Definition GoerzelStream.h:229
void * ref
User-defined reference for callback context.
Definition GoerzelStream.h:478
void processSamplesOfType(const uint8_t *data, size_t data_len, int channels)
Template helper to process samples of a specific type.
Definition GoerzelStream.h:514
GoertzelDetector & getDetector(int no)
Get detector for specific channel.
Definition GoerzelStream.h:437
void addFrequency(float freq, void *ref)
Add a frequency to the detection list with a custom reference pointer.
Definition GoerzelStream.h:458
GoertzelStream(AudioStream &io)
Definition GoerzelStream.h:232
void setOutput(AudioOutput &out)
Defines/Changes the output target.
Definition GoerzelStream.h:339
size_t readBytes(uint8_t *data, size_t len) override
Read data from input stream and process it.
Definition GoerzelStream.h:396
void processSamples(const uint8_t *data, size_t data_len)
Generic sample processing method for both write and readBytes.
Definition GoerzelStream.h:569
bool begin()
Initialize detectors for all frequencies.
Definition GoerzelStream.h:297
GoertzelStream(Stream &io)
Definition GoerzelStream.h:231
float clip(float value)
Clip audio values to prevent overflow.
Definition GoerzelStream.h:545
size_t write(const uint8_t *data, size_t len) override
Process audio data and pass it through.
Definition GoerzelStream.h:373
Vector< void * > references
List of frequencies to detect.
Definition GoerzelStream.h:468
void(* frequency_detection_callback)(float frequency, float magnitude, void *ref)
User callback for detection events.
Definition GoerzelStream.h:475
Stream * p_stream
Input stream for reading audio data.
Definition GoerzelStream.h:471
GoertzelConfig default_config
Current algorithm configuration.
Definition GoerzelStream.h:469
Vector< float > frequencies
List of frequencies to detect.
Definition GoerzelStream.h:467
const GoertzelConfig & getConfig() const
Get the current configuration.
Definition GoerzelStream.h:415
void setReference(void *ref)
Set reference pointer for callback context.
Definition GoerzelStream.h:426
void addFrequency(float freq)
Add a frequency to the detection list.
Definition GoerzelStream.h:444
void end()
Stop the Goertzel detectors and clear resources.
Definition GoerzelStream.h:318
bool begin(GoertzelConfig config)
Initialize with GoertzelConfig.
Definition GoerzelStream.h:275
Vector< GoertzelDetector > detectors
One detector per frequency in frequencies.
Definition GoerzelStream.h:466
size_t sample_no
Sample counter for channel selection.
Definition GoerzelStream.h:480
Print * p_print
Output stream for writing audio data.
Definition GoerzelStream.h:472
void setAudioInfo(AudioInfo info) override
Set audio format and initialize detector array.
Definition GoerzelStream.h:244
GoertzelStream(AudioOutput &out)
Definition GoerzelStream.h:230
void setOutput(Print &out)
Defines/Changes the output target.
Definition GoerzelStream.h:336
void setFrequencyDetectionCallback(void(*callback)(float frequency, float magnitude, void *ref))
Set detection callback function for channel-aware frequency detection.
Definition GoerzelStream.h:355
void checkDetection(GoertzelDetector &detector)
Check if a detector has detected its frequency and invoke callback.
Definition GoerzelStream.h:485
GoertzelConfig defaultConfig()
Returns a default GoertzelConfig instance with standard parameters.
Definition GoerzelStream.h:260
void setStream(Stream &in)
Defines/Changes the input & output.
Definition GoerzelStream.h:324
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
void push_back(T &&value)
Definition Vector.h:182
int size()
Definition Vector.h:178
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:51
void copyFrom(AudioInfo info)
Same as set.
Definition AudioTypes.h:101
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:53
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:57
Configuration for Goertzel algorithm detectors.
Definition GoerzelStream.h:24
float volume
Volume factor for normalization - scales input samples before processing.
Definition GoerzelStream.h:34
float threshold
Definition GoerzelStream.h:32
int block_size
Definition GoerzelStream.h:29
uint8_t channel
channel used for detection when used in a stream
Definition GoerzelStream.h:36
GoertzelConfig(const AudioInfo &info)
Copy constructor from AudioInfo.
Definition GoerzelStream.h:40
float target_frequency
Target frequency to detect in Hz (same for all channels)
Definition GoerzelStream.h:26