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
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;
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 || config.sample_rate == 0) {
75 return false;
76 }
77
78 // Calculate the coefficient k and omega: the trigonometric functions are
79 // only evaluated here and not for each block
80 double omega =
82 cos_omega = cos(omega);
83 sin_omega = sin(omega);
84 coeff = 2.0f * cos_omega;
85
86 // Reset state variables
87 reset();
88 return true;
89 }
90
96 bool processSample(float sample) {
97 // Goertzel algorithm core computation
98 float s0 = sample + coeff * s1 - s2;
99 s2 = s1;
100 s1 = s0;
101
102 sample_count++;
103
104 // Check if we've processed a complete block
106 // Calculate magnitude squared
107 float real = s1 - s2 * cos_omega;
108 float imag = s2 * sin_omega;
109 magnitude_squared = (real * real) + (imag * imag);
111
112 // Reset for next block
113 reset();
114 return true;
115 }
116
117 return false;
118 }
119
124 float getMagnitude() const { return magnitude; }
125
131 float getMagnitudeSquared() const { return magnitude_squared; }
132
138 bool isDetected(float threshold) const { return magnitude > threshold; }
139
144 bool isDetected() const { return isDetected(config.threshold); }
145
149 void reset() {
150 s1 = 0.0f;
151 s2 = 0.0f;
152 sample_count = 0;
153 magnitude_squared = 0.0f;
154 }
155
160
164 int getBlockSize() const { return config.block_size; }
165
169 const GoertzelConfig& getConfig() const { return config; }
170
171 void setReference(void* ref) { this->reference = ref; }
172
173 void* getReference() { return reference; }
174
175 protected:
177 float coeff = 0.0f;
178 float cos_omega = 1.0f;
179 float sin_omega = 0.0f;
180 void* reference = nullptr;
181
182 // State variables
183 float s1 = 0.0f;
184 float s2 = 0.0f;
186
187 // Results
188 float magnitude = 0.0f;
189 float magnitude_squared = 0.0f;
190};
191
230 public:
231 // Default Constructor with no output or input
232 GoertzelStream() = default;
237
248 void setAudioInfo(AudioInfo info) override {
251 // Initialize detectors for each frequency
252 begin();
253 }
254
265 GoertzelConfig result;
266 return result;
267 }
268
279 bool begin(GoertzelConfig config) {
280 config.target_frequency = 0;
281 if (config.channel >= config.channels) {
282 LOGE("GoertzelStream: channel %d out of range (max %d)", config.channel,
283 config.channels);
284 return false;
285 }
286 this->default_config = config;
287
288 // Set up audio info from config
289 setAudioInfo(config);
290 return begin();
291 }
292
301 bool begin() {
302 int i = 0;
303 detectors.clear();
304 for (float freq : frequencies) {
306 cfg.target_frequency = freq;
307 GoertzelDetector detector;
308 if (i < references.size()) {
309 detector.setReference(references[i]);
310 }
311 detector.begin(cfg);
312 detectors.push_back(detector);
313 i++;
314 }
315 sample_no = 0;
316 return true;
317 }
318
322 void end() {
323 detectors.clear();
325 }
326
328 void setStream(Stream& in) {
329 p_stream = &in;
330 p_print = &in;
331 }
332
335 setStream((Stream&)io);
337 }
338
340 void setOutput(Print& out) { p_print = &out; }
341
344 setOutput((Print&)out);
346 }
347
359 void setFrequencyDetectionCallback(void (*callback)(float frequency,
360 float magnitude,
361 void* ref)) {
363 }
364
377 size_t write(const uint8_t* data, size_t len) override {
378 // Process samples for detection
379 processSamples(data, len);
380
382 if (p_print == nullptr) return len;
383
384 // Pass data through unchanged
385 return p_print->write(data, len);
386 }
387
400 size_t readBytes(uint8_t* data, size_t len) override {
401 if (p_stream == nullptr) return 0;
402
403 size_t result = p_stream->readBytes(data, len);
404
405 // Process samples for detection
406 processSamples(data, result);
407
408 return result;
409 }
410
419 const GoertzelConfig& getConfig() const { return default_config; }
420
430 void setReference(void* ref) { this->ref = ref; }
431
441 GoertzelDetector& getDetector(int no) { return detectors[no]; }
442
448 void addFrequency(float freq) { frequencies.push_back(freq); }
449
462 void addFrequency(float freq, void* ref) {
465 }
466
467 protected:
468 // Core detection components
474 // Stream I/O components
475 Stream* p_stream = nullptr;
476 Print* p_print = nullptr;
477
478 // Callback system
479 void (*frequency_detection_callback)(float frequency, float magnitude,
480 void* ref) =
481 nullptr;
482 void* ref = this;
483
484 size_t sample_no = 0;
485
490 float magnitude = detector.getMagnitude();
491 if (magnitude > 0.0f)
492 LOGD("frequency: %f / magnitude: %f / threshold: %f", detector.getTargetFrequency(), magnitude, default_config.threshold);
493
494 if (magnitude > default_config.threshold) {
495 float frequency = detector.getTargetFrequency();
496 void* reference = detector.getReference();
497 if (reference == nullptr) reference = ref;
498 if (frequency_detection_callback != nullptr) {
499 frequency_detection_callback(frequency, magnitude, reference);
500 }
501 }
502 }
503
517 template <typename T = int16_t>
518 void processSamplesOfType(const uint8_t* data, size_t data_len,
519 int channels) {
520 const T* samples = reinterpret_cast<const T*>(data);
521 size_t num_samples = data_len / sizeof(T);
522
523 for (size_t i = 0; i < num_samples; i++) {
524 // select only samples for the configured channel
525 if (sample_no % channels == default_config.channel) {
526 float normalized = clip(NumberConverter::toFloatT<T>(samples[i]) *
528 LOGD("sample: %f", normalized);
529 // process all frequencies
530 for (auto& detector : detectors) {
531 if (detector.processSample(normalized)) {
532 checkDetection(detector);
533 }
534 }
535 }
536 sample_no++;
537 }
538 }
539
549 float clip(float value) {
550 // Clip the value to the range [-1.0, 1.0]
551 if (value > 1.0f) return 1.0f;
552 if (value < -1.0f) return -1.0f;
553 return value;
554 }
555
573 void processSamples(const uint8_t* data, size_t data_len) {
574 // return if there is nothing to detect
575 if (detectors.empty()) return;
576 int channels = default_config.channels;
577
579 case 8:
580 processSamplesOfType<uint8_t>(data, data_len, channels);
581 break;
582 case 16:
583 processSamplesOfType<int16_t>(data, data_len, channels);
584 break;
585 case 24:
586 processSamplesOfType<int24_t>(data, data_len, channels);
587 break;
588 case 32:
589 processSamplesOfType<int32_t>(data, data_len, channels);
590 break;
591 default:
592 LOGE("Unsupported bits_per_sample: %d", default_config.bits_per_sample);
593 break;
594 }
595 }
596};
597
598} // 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:155
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:184
void * getReference()
Definition GoerzelStream.h:173
int getBlockSize() const
Get the block size.
Definition GoerzelStream.h:164
float cos_omega
Definition GoerzelStream.h:178
bool isDetected() const
Check if the detected magnitude is above the configured threshold.
Definition GoerzelStream.h:144
bool begin(const GoertzelConfig &config)
Initialize the Goertzel detector with configuration.
Definition GoerzelStream.h:70
float s1
Definition GoerzelStream.h:183
float coeff
Definition GoerzelStream.h:177
GoertzelConfig config
Definition GoerzelStream.h:176
float sin_omega
Definition GoerzelStream.h:179
float getMagnitudeSquared() const
Get the squared magnitude (more efficient if you don't need the actual magnitude)
Definition GoerzelStream.h:131
bool processSample(float sample)
Process a single sample.
Definition GoerzelStream.h:96
bool isDetected(float threshold) const
Check if the detected magnitude is above a threshold.
Definition GoerzelStream.h:138
float magnitude
Definition GoerzelStream.h:188
const GoertzelConfig & getConfig() const
Get the current configuration.
Definition GoerzelStream.h:169
void setReference(void *ref)
Definition GoerzelStream.h:171
int sample_count
Definition GoerzelStream.h:185
float getMagnitude() const
Get the magnitude of the detected frequency.
Definition GoerzelStream.h:124
float getTargetFrequency() const
Get the target frequency.
Definition GoerzelStream.h:159
void reset()
Reset the detector state.
Definition GoerzelStream.h:149
float magnitude_squared
Definition GoerzelStream.h:189
void * reference
Definition GoerzelStream.h:180
AudioStream-based multi-frequency Goertzel detector for real-time audio analysis.
Definition GoerzelStream.h:229
void setStream(AudioStream &io)
Defines/Changes the input & output.
Definition GoerzelStream.h:334
GoertzelStream(Print &out)
Definition GoerzelStream.h:233
void * ref
User-defined reference for callback context.
Definition GoerzelStream.h:482
void processSamplesOfType(const uint8_t *data, size_t data_len, int channels)
Template helper to process samples of a specific type.
Definition GoerzelStream.h:518
GoertzelDetector & getDetector(int no)
Get detector for specific channel.
Definition GoerzelStream.h:441
void addFrequency(float freq, void *ref)
Add a frequency to the detection list with a custom reference pointer.
Definition GoerzelStream.h:462
GoertzelStream(AudioStream &io)
Definition GoerzelStream.h:236
void setOutput(AudioOutput &out)
Defines/Changes the output target.
Definition GoerzelStream.h:343
size_t readBytes(uint8_t *data, size_t len) override
Read data from input stream and process it.
Definition GoerzelStream.h:400
void processSamples(const uint8_t *data, size_t data_len)
Generic sample processing method for both write and readBytes.
Definition GoerzelStream.h:573
bool begin()
Initialize detectors for all frequencies.
Definition GoerzelStream.h:301
GoertzelStream(Stream &io)
Definition GoerzelStream.h:235
float clip(float value)
Clip audio values to prevent overflow.
Definition GoerzelStream.h:549
size_t write(const uint8_t *data, size_t len) override
Process audio data and pass it through.
Definition GoerzelStream.h:377
Vector< void * > references
List of frequencies to detect.
Definition GoerzelStream.h:472
void(* frequency_detection_callback)(float frequency, float magnitude, void *ref)
User callback for detection events.
Definition GoerzelStream.h:479
Stream * p_stream
Input stream for reading audio data.
Definition GoerzelStream.h:475
GoertzelConfig default_config
Current algorithm configuration.
Definition GoerzelStream.h:473
Vector< float > frequencies
List of frequencies to detect.
Definition GoerzelStream.h:471
const GoertzelConfig & getConfig() const
Get the current configuration.
Definition GoerzelStream.h:419
void setReference(void *ref)
Set reference pointer for callback context.
Definition GoerzelStream.h:430
void addFrequency(float freq)
Add a frequency to the detection list.
Definition GoerzelStream.h:448
void end()
Stop the Goertzel detectors and clear resources.
Definition GoerzelStream.h:322
bool begin(GoertzelConfig config)
Initialize with GoertzelConfig.
Definition GoerzelStream.h:279
Vector< GoertzelDetector > detectors
One detector per frequency in frequencies.
Definition GoerzelStream.h:470
size_t sample_no
Sample counter for channel selection.
Definition GoerzelStream.h:484
Print * p_print
Output stream for writing audio data.
Definition GoerzelStream.h:476
void setAudioInfo(AudioInfo info) override
Set audio format and initialize detector array.
Definition GoerzelStream.h:248
GoertzelStream(AudioOutput &out)
Definition GoerzelStream.h:234
void setOutput(Print &out)
Defines/Changes the output target.
Definition GoerzelStream.h:340
void setFrequencyDetectionCallback(void(*callback)(float frequency, float magnitude, void *ref))
Set detection callback function for channel-aware frequency detection.
Definition GoerzelStream.h:359
void checkDetection(GoertzelDetector &detector)
Check if a detector has detected its frequency and invoke callback.
Definition GoerzelStream.h:489
GoertzelConfig defaultConfig()
Returns a default GoertzelConfig instance with standard parameters.
Definition GoerzelStream.h:264
void setStream(Stream &in)
Defines/Changes the input & output.
Definition GoerzelStream.h:328
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:56
void copyFrom(AudioInfo info)
Same as set.
Definition AudioTypes.h:106
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:58
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:60
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:62
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