mirror of
https://github.com/pschatzmann/arduino-audio-tools.git
synced 2024-09-21 02:17:31 +00:00
Updates to BaseConverter and continous ADC of ESP32 (#1621)
* Converter Updates Updated Binning converter. Handling of partial bins. Results in binsize less samples Added ChannelDiff converter. Computes pairwise difference between channels. results in half the channels Added ChannelAvg converter. Computes pairwise average between channels. Results in half the channels * Testing and Debugging of Diff, Bin and BinDiff
This commit is contained in:
parent
0e6f90c44d
commit
0f3329630d
@ -69,7 +69,7 @@ adcConfig.adc_channels[1] = ADC_CHANNEL_5;
|
||||
```
|
||||
|
||||
## ADC unit 1 channels on common ESP32 boards
|
||||
Audio tools supports ADC Unit 1 only.
|
||||
Audio tools continous ADC framewaork supports ADC Unit 1 only.
|
||||
|
||||
### Sparkfun ESP32 Thing Plus (ESP32)
|
||||
- A2, ADC1_CH6
|
||||
@ -78,6 +78,14 @@ Audio tools supports ADC Unit 1 only.
|
||||
- 32, ADC1_CH4
|
||||
- 33, ADC1_CH5
|
||||
|
||||
### Sparkfun ESP32 Thing Plus C (ESP32)
|
||||
- A2, ADC1_CH6
|
||||
- A3, ADC1_CH3
|
||||
- A4, ADC1_CH0
|
||||
- A5, ADC1_CH7
|
||||
- 32/6, ADC1_CH4
|
||||
- 33/10, ADC1_CH5
|
||||
|
||||
### Sparkfun ESP32 Qwiic Pocket Development (ESP32C6)
|
||||
- 2, ADC1_CH2
|
||||
- 3, ADC1_CH3
|
||||
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @file channel-converter-avg.ino
|
||||
* @brief Test calculating pairwise average of channels
|
||||
* @author Urs Utzinger
|
||||
* @copyright GPLv3
|
||||
**/
|
||||
|
||||
#include "AudioTools.h"
|
||||
|
||||
AudioInfo info1(44100, 1, 16);
|
||||
AudioInfo info2(44100, 2, 16);
|
||||
SineWaveGenerator<int16_t> sineWave1(32000); // subclass of SoundGenerator with max amplitude of 32000
|
||||
SineWaveGenerator<int16_t> sineWave2(32000); // subclass of SoundGenerator with max amplitude of 32000
|
||||
GeneratedSoundStream<int16_t> sound1(sineWave1); // stream generated from sine wave1
|
||||
GeneratedSoundStream<int16_t> sound2(sineWave2); // stream generated from sine wave2
|
||||
InputMerge<int16_t> imerge; // merge two inputs to stereo
|
||||
ChannelAvg averager; // channel averager
|
||||
ConverterStream<int16_t> averaged_stream(imerge, averager); // pipe the sound to the averager
|
||||
CsvOutput<int16_t> serial_out(Serial); // serial output
|
||||
StreamCopy copier(serial_out, averaged_stream); // stream the binner output to serial port
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
|
||||
// Open Serial
|
||||
Serial.begin(115200);
|
||||
while(!Serial); // wait for Serial to be ready
|
||||
|
||||
AudioLogger::instance().begin(Serial, AudioLogger::Warning);
|
||||
|
||||
// Setup sine waves
|
||||
sineWave1.begin(info1, N_B4);
|
||||
sineWave2.begin(info1, N_B5);
|
||||
|
||||
// Merge input to stereo
|
||||
imerge.add(sound1);
|
||||
imerge.add(sound2);
|
||||
imerge.begin(info2);
|
||||
|
||||
// Define CSV Output
|
||||
serial_out.begin(info1);
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop - copy sound to out with conversion
|
||||
void loop() {
|
||||
copier.copy();
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @file channel-converter-avg.ino
|
||||
* @brief Test calculating average of two channels
|
||||
* @author Urs Utzinger
|
||||
* @copyright GPLv3
|
||||
**/
|
||||
|
||||
#include "AudioTools.h"
|
||||
|
||||
#define BINSIZE 4
|
||||
|
||||
AudioInfo info1(44100, 1, 16);
|
||||
AudioInfo info2(44100, 2, 16);
|
||||
AudioInfo info_out(44100/BINSIZE, 2, 16);
|
||||
SineWaveGenerator<int16_t> sineWave1(32000); // subclass of SoundGenerator with max amplitude of 32000
|
||||
SineWaveGenerator<int16_t> sineWave2(32000); // subclass of SoundGenerator with max amplitude of 32000
|
||||
GeneratedSoundStream<int16_t> sound1(sineWave1); // stream generated from sine wave1
|
||||
GeneratedSoundStream<int16_t> sound2(sineWave2); // stream generated from sine wave2
|
||||
InputMerge<int16_t> imerge; // merge two inputs to stereo
|
||||
Bin binner; // channel averager
|
||||
ConverterStream<int16_t> binned_stream(imerge, binner); // pipe the sound to the averager
|
||||
CsvOutput<int16_t> serial_out(Serial); // serial output
|
||||
StreamCopy copier(serial_out, binned_stream); // stream the binner output to serial port
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
|
||||
// Open Serial
|
||||
Serial.begin(115200);
|
||||
while(!Serial); // wait for Serial to be ready
|
||||
|
||||
AudioLogger::instance().begin(Serial, AudioLogger::Warning);
|
||||
|
||||
// Setup sine waves
|
||||
sineWave1.begin(info1, N_B4);
|
||||
sineWave2.begin(info1, N_B5);
|
||||
|
||||
// Merge input to stereo
|
||||
imerge.add(sound1);
|
||||
imerge.add(sound2);
|
||||
imerge.begin(info2);
|
||||
|
||||
// Configure binning
|
||||
binner.setChannels(2);
|
||||
binner.setBits(16);
|
||||
binner.setBinSize(BINSIZE);
|
||||
binner.setAverage(true);
|
||||
|
||||
// Define CSV Output
|
||||
serial_out.begin(info_out);
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop - copy sound to out with conversion
|
||||
void loop() {
|
||||
copier.copy();
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @file channel-converter-bin-diff.ino
|
||||
* @author Urs Utzinger
|
||||
* @brief On two channels reduce number of samples by binning, then compute difference between two channels
|
||||
* @copyright GPLv3
|
||||
**/
|
||||
|
||||
#include "AudioTools.h"
|
||||
|
||||
#define BINSIZE 4
|
||||
|
||||
AudioInfo info1(44100, 1, 16);
|
||||
AudioInfo info2(44100, 2, 16);
|
||||
AudioInfo info_out(44100/BINSIZE, 1, 16);
|
||||
SineWaveGenerator<int16_t> sineWave1(16000); // subclass of SoundGenerator with max amplitude of 32000
|
||||
SineWaveGenerator<int16_t> sineWave2(16000); // subclass of SoundGenerator with max amplitude of 32000
|
||||
GeneratedSoundStream<int16_t> sound1(sineWave1); // stream generated from sine wave
|
||||
GeneratedSoundStream<int16_t> sound2(sineWave2); // stream generated from sine wave
|
||||
InputMerge<int16_t> imerge;
|
||||
ChannelBinDiff bindiffer; // Binning each channel by average length, setup see below
|
||||
ConverterStream<int16_t> converted_stream(imerge, bindiffer); // pipe the merged sound to the converter
|
||||
CsvOutput<int16_t> serial_out(Serial); // serial output
|
||||
StreamCopy copier(serial_out, converted_stream); // stream the binner output to serial port
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
// Open Serial
|
||||
Serial.begin(115200);
|
||||
while(!Serial);
|
||||
|
||||
AudioLogger::instance().begin(Serial, AudioLogger::Debug); // Info, Warning, Error, Debug
|
||||
|
||||
// Setup sine wave
|
||||
sineWave1.begin(info1, N_B4);
|
||||
sineWave2.begin(info1, N_B5);
|
||||
|
||||
// Merge input to stereo
|
||||
imerge.add(sound1);
|
||||
imerge.add(sound2);
|
||||
imerge.begin(info2);
|
||||
|
||||
// Setup binning
|
||||
bindiffer.setChannels(2);
|
||||
bindiffer.setBits(16);
|
||||
bindiffer.setBinSize(BINSIZE);
|
||||
bindiffer.setAverage(true);
|
||||
|
||||
// Define CSV Output
|
||||
serial_out.begin(info_out);
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop - copy sound to out with conversion
|
||||
void loop() {
|
||||
copier.copy();
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* @file channel-converter-decimate.ino
|
||||
* @author Urs Utzinger
|
||||
* @brief Reduce samples by binning; which is summing consecutive samples and optionally dividing by the number of samples summed.
|
||||
* @copyright GPLv3
|
||||
**/
|
||||
|
||||
#include "AudioTools.h"
|
||||
#define FACTOR 4
|
||||
|
||||
AudioInfo info1(44100, 1, 16);
|
||||
AudioInfo info2(44100, 2, 16);
|
||||
AudioInfo infoO(44100/FACTOR, 2, 16);
|
||||
SineWaveGenerator<int16_t> sineWave1(16000); // subclass of SoundGenerator with max amplitude of 32000
|
||||
SineWaveGenerator<int16_t> sineWave2(16000); // subclass of SoundGenerator with max amplitude of 32000
|
||||
GeneratedSoundStream<int16_t> sound1(sineWave1); // stream generated from sine wave
|
||||
GeneratedSoundStream<int16_t> sound2(sineWave2); // stream generated from sine wave
|
||||
InputMerge<int16_t> imerge;
|
||||
Decimate decimater; // decimate by 4 on 1 channel
|
||||
ConverterStream<int16_t> decimated_stream(imerge, decimater); // pipe the sound to the binner
|
||||
CsvOutput<int16_t> serial_out(Serial); // serial output
|
||||
StreamCopy copier(serial_out, decimated_stream); // stream the binner output to serial port
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
|
||||
// Open Serial
|
||||
Serial.begin(115200);
|
||||
while(!Serial);
|
||||
|
||||
AudioLogger::instance().begin(Serial, AudioLogger::Warning);
|
||||
|
||||
//
|
||||
decimater.setChannels(2);
|
||||
decimater.setFactor(FACTOR);
|
||||
|
||||
// Merge input to stereo
|
||||
imerge.add(sound1);
|
||||
imerge.add(sound2);
|
||||
imerge.begin(info2);
|
||||
|
||||
// Setup sine wave
|
||||
sineWave1.begin(info1, N_B4);
|
||||
sineWave2.begin(info1, N_B5);
|
||||
|
||||
// Define CSV Output
|
||||
serial_out.begin(infoO);
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop - copy sound to out with conversion
|
||||
void loop() {
|
||||
copier.copy();
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @file channel-converter-diff.ino
|
||||
* @brief Test calculating parwise difference of channels
|
||||
* @author Urs Utzinger
|
||||
* @copyright GPLv3
|
||||
**/
|
||||
|
||||
#include "AudioTools.h"
|
||||
|
||||
AudioInfo info1(44100, 1, 16);
|
||||
AudioInfo info2(44100, 2, 16);
|
||||
SineWaveGenerator<int16_t> sineWave1(16000); // subclass of SoundGenerator with max amplitude of 32000
|
||||
SineWaveGenerator<int16_t> sineWave2(16000); // subclass of SoundGenerator with max amplitude of 32000
|
||||
GeneratedSoundStream<int16_t> sound1(sineWave1); // stream generated from sine wave1
|
||||
GeneratedSoundStream<int16_t> sound2(sineWave2); // stream generated from sine wave2
|
||||
InputMerge<int16_t> imerge; // merge two inputs to stereo
|
||||
ChannelDiff differ; // channel averager
|
||||
ConverterStream<int16_t> diffed_stream(imerge, differ); // pipe the sound to the averager
|
||||
CsvOutput<int16_t> serial_out(Serial); // serial output
|
||||
StreamCopy copier(serial_out, diffed_stream); // stream the binner output to serial port
|
||||
|
||||
// Arduino Setup
|
||||
void setup(void) {
|
||||
|
||||
// Open Serial
|
||||
Serial.begin(115200);
|
||||
while(!Serial); // wait for Serial to be ready
|
||||
|
||||
AudioLogger::instance().begin(Serial, AudioLogger::Warning);
|
||||
|
||||
// Setup sine waves
|
||||
sineWave1.begin(info1, N_B4);
|
||||
sineWave2.begin(info1, N_B5);
|
||||
|
||||
// Merge input to stereo
|
||||
imerge.add(sound1);
|
||||
imerge.add(sound2);
|
||||
imerge.begin(info2);
|
||||
|
||||
// Define CSV Output
|
||||
serial_out.begin(info1);
|
||||
|
||||
}
|
||||
|
||||
// Arduino loop - copy sound to out with conversion
|
||||
void loop() {
|
||||
copier.copy();
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
# define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1
|
||||
# define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1
|
||||
# define ADC_CHANNELS {ADC_CHANNEL_6, ADC_CHANNEL_7}
|
||||
# define ADC_CHANNELS {ADC_CHANNEL_7, ADC_CHANNEL_0, ADC_CHANNEL_3, ADC_CHANNEL_6, ADC_CHANNEL_4, ADC_CHANNEL_5}
|
||||
# define AUDIO_ADC_GET_CHANNEL(p_data) ((p_data)->type1.channel)
|
||||
# define AUDIO_ADC_GET_DATA(p_data) ((p_data)->type1.data)
|
||||
# define HAS_ESP32_DAC
|
||||
@ -21,20 +21,20 @@
|
||||
# define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
|
||||
# define AUDIO_ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel)
|
||||
# define AUDIO_ADC_GET_DATA(p_data) ((p_data)->type2.data)
|
||||
# define ADC_CHANNELS {ADC_CHANNEL_2, ADC_CHANNEL_3}
|
||||
# define ADC_CHANNELS {ADC_CHANNEL_7, ADC_CHANNEL_0, ADC_CHANNEL_3, ADC_CHANNEL_6, ADC_CHANNEL_4, ADC_CHANNEL_5}
|
||||
# define HAS_ESP32_DAC
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C6
|
||||
# define ADC_CONV_MODE ADC_CONV_ALTER_UNIT //ESP32C3 only supports alter mode
|
||||
# define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
|
||||
# define AUDIO_ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel)
|
||||
# define AUDIO_ADC_GET_DATA(p_data) ((p_data)->type2.data)
|
||||
# define ADC_CHANNELS {ADC_CHANNEL_2, ADC_CHANNEL_3}
|
||||
# define ADC_CHANNELS {ADC_CHANNEL_7, ADC_CHANNEL_0, ADC_CHANNEL_3, ADC_CHANNEL_6, ADC_CHANNEL_4, ADC_CHANNEL_5}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
# define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1
|
||||
# define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
|
||||
# define AUDIO_ADC_GET_CHANNEL(p_data) ((p_data)->type2.channel)
|
||||
# define AUDIO_ADC_GET_DATA(p_data) ((p_data)->type2.data)
|
||||
# define ADC_CHANNELS {ADC_CHANNEL_2, ADC_CHANNEL_3} // or channel 4 & 5
|
||||
# define ADC_CHANNELS {ADC_CHANNEL_8, ADC_CHANNEL_9, ADC_CHANNEL_4, ADC_CHANNEL_5, ADC_CHANNEL_4, ADC_CHANNEL_5}
|
||||
#endif
|
||||
|
||||
// #define GET_ADC_UNIT_FROM_CHANNEL(x) ((x >> 3) & 0x1)
|
||||
@ -77,8 +77,7 @@ class AnalogConfigESP32V1 : public AudioInfo {
|
||||
adc_digi_output_format_t adc_output_type = ADC_OUTPUT_TYPE;
|
||||
uint8_t adc_attenuation = ADC_ATTEN_DB_12; // full voltage range of 3.9V
|
||||
uint8_t adc_bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;
|
||||
/// ESP32: ADC_CHANNEL_6, ADC_CHANNEL_7; others ADC_CHANNEL_2, ADC_CHANNEL_3
|
||||
adc_channel_t adc_channels[2] = ADC_CHANNELS;
|
||||
adc_channel_t adc_channels[6] = ADC_CHANNELS;
|
||||
|
||||
/// Default constructor
|
||||
AnalogConfigESP32V1(RxTxMode rxtxMode=TX_MODE) {
|
||||
|
@ -39,7 +39,7 @@ class BaseConverter {
|
||||
*/
|
||||
class NOPConverter : public BaseConverter {
|
||||
public:
|
||||
virtual size_t convert(uint8_t(*src), size_t size) { return size; };
|
||||
size_t convert(uint8_t(*src), size_t size) override { return size; };
|
||||
};
|
||||
|
||||
/**
|
||||
@ -110,7 +110,7 @@ class ConverterAutoCenterT : public BaseConverter {
|
||||
this->is_dynamic = isDynamic;
|
||||
}
|
||||
|
||||
size_t convert(uint8_t(*src), size_t byte_count) {
|
||||
size_t convert(uint8_t(*src), size_t byte_count) override {
|
||||
size_t size = byte_count / channels / sizeof(T);
|
||||
T *sample = (T *)src;
|
||||
setup((T *)src, size);
|
||||
@ -194,7 +194,10 @@ class ConverterAutoCenter : public BaseConverter {
|
||||
begin(channels, bitsPerSample);
|
||||
}
|
||||
~ConverterAutoCenter() {
|
||||
if (p_converter != nullptr) delete p_converter;
|
||||
if (p_converter != nullptr) {
|
||||
delete p_converter;
|
||||
p_converter = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void begin(int channels, int bitsPerSample, bool isDynamic = false) {
|
||||
@ -241,7 +244,7 @@ class ConverterSwitchLeftAndRight : public BaseConverter {
|
||||
public:
|
||||
ConverterSwitchLeftAndRight(int channels = 2) { this->channels = channels; }
|
||||
|
||||
size_t convert(uint8_t *src, size_t byte_count) {
|
||||
size_t convert(uint8_t *src, size_t byte_count) override {
|
||||
if (channels == 2) {
|
||||
int size = byte_count / channels / sizeof(T);
|
||||
T *sample = (T *)src;
|
||||
@ -361,7 +364,7 @@ class ConverterToInternalDACFormat : public BaseConverter {
|
||||
public:
|
||||
ConverterToInternalDACFormat(int channels = 2) { this->channels = channels; }
|
||||
|
||||
size_t convert(uint8_t *src, size_t byte_count) {
|
||||
size_t convert(uint8_t *src, size_t byte_count) override {
|
||||
int size = byte_count / channels / sizeof(T);
|
||||
T *sample = (T *)src;
|
||||
for (int i = 0; i < size; i++) {
|
||||
@ -486,16 +489,21 @@ class DecimateT : public BaseConverter {
|
||||
DecimateT(int factor, int channels) {
|
||||
setChannels(channels);
|
||||
setFactor(factor);
|
||||
count = 0; // Initialize count to 0
|
||||
}
|
||||
|
||||
/// Defines the number of channels
|
||||
void setChannels(int channels) { this->channels = channels; }
|
||||
|
||||
/// Sets the factor: e.g. with 4 we keep every forth sample
|
||||
/// Sets the factor: e.g. with 4 we keep every fourth sample
|
||||
void setFactor(int factor) { this->factor = factor; }
|
||||
|
||||
size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); }
|
||||
|
||||
size_t convert(uint8_t *target, uint8_t *src, size_t size) {
|
||||
|
||||
assert(size % (sizeof(T) * channels) == 0); // Ensure proper buffer size
|
||||
|
||||
int frame_count = size / (sizeof(T) * channels);
|
||||
T *p_target = (T *)target;
|
||||
T *p_source = (T *)src;
|
||||
@ -504,23 +512,24 @@ class DecimateT : public BaseConverter {
|
||||
for (int i = 0; i < frame_count; i++) {
|
||||
if (++count == factor) {
|
||||
count = 0;
|
||||
// only keep even samples
|
||||
// Only keep every "factor" samples
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
*p_target++ = p_source[i + ch];
|
||||
*p_target++ = p_source[i * channels + ch]; // Corrected indexing
|
||||
result_size += sizeof(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LOGI("%d: %d -> %d ",factor, (int)size, (int)result_size);
|
||||
return result_size;
|
||||
}
|
||||
|
||||
operator bool() { return factor > 1; };
|
||||
operator bool() { return factor > 1; }
|
||||
|
||||
protected:
|
||||
int channels = 2;
|
||||
int factor = 1;
|
||||
uint16_t count = 0;
|
||||
uint16_t count;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -570,106 +579,175 @@ class Decimate : public BaseConverter {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides a reduced sampling rate through binning
|
||||
* @brief We reduce the number of samples in a datastream by summing (binning) or averaging.
|
||||
* This will result in the same number of channels but binSize times less samples.
|
||||
* If Average is true the sum is divided by binSize.
|
||||
* @author Urs Utzinger
|
||||
* @ingroup convert
|
||||
* @tparam T
|
||||
*/
|
||||
|
||||
// Helper template to define the integer type for the summation based on input
|
||||
// data type T
|
||||
template <typename T>
|
||||
struct AppropriateSumType;
|
||||
template <>
|
||||
struct AppropriateSumType<int8_t> {
|
||||
using type = int16_t;
|
||||
};
|
||||
template <>
|
||||
struct AppropriateSumType<int16_t> {
|
||||
using type = int32_t;
|
||||
};
|
||||
template <>
|
||||
struct AppropriateSumType<int24_t> {
|
||||
using type = int32_t;
|
||||
};
|
||||
template <>
|
||||
struct AppropriateSumType<int32_t> {
|
||||
using type = int64_t;
|
||||
struct AppropriateSumType {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides reduced sampling rates through binning: typed implementation
|
||||
* @ingroup convert
|
||||
*/
|
||||
template <>
|
||||
struct AppropriateSumType<int8_t> {
|
||||
using type = int16_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AppropriateSumType<int16_t> {
|
||||
using type = int32_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AppropriateSumType<int24_t> {
|
||||
using type = int32_t; // Assuming int24_t is a custom 24-bit integer type
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AppropriateSumType<int32_t> {
|
||||
using type = int64_t;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class BinT : public BaseConverter {
|
||||
public:
|
||||
BinT() = default;
|
||||
BinT(int binSize, int channels, bool average) {
|
||||
setChannels(channels);
|
||||
setBinSize(binSize);
|
||||
setAverage(average);
|
||||
setAverage(average);
|
||||
this->partialBinSize = 0;
|
||||
this->partialBin = new T[channels];
|
||||
std::fill(this->partialBin, this->partialBin + channels, 0); // Initialize partialBin with zeros
|
||||
}
|
||||
/// Defines the number of channels
|
||||
|
||||
~BinT() {
|
||||
delete[] this->partialBin;
|
||||
}
|
||||
|
||||
void setChannels(int channels) { this->channels = channels; }
|
||||
|
||||
/// Sets the bins: e.g. with 4 we sum 4 sample
|
||||
void setBinSize(int binSize) { this->binSize = binSize; }
|
||||
|
||||
/// Enables averaging: e.g. when true it divides the sum by number of bins
|
||||
void setAverage(bool average) { this->average = average; }
|
||||
|
||||
size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); }
|
||||
|
||||
size_t convert(uint8_t *target, uint8_t *src, size_t size) {
|
||||
int frame_count = size / (sizeof(T) * channels);
|
||||
// The binning takes the following into account
|
||||
// 1) if size is too small it will add up data to partialBin and return 0 size
|
||||
// 2) if there is sufficient data to fill Bins but there is partial data remaining it will be added to the partial Bin
|
||||
// 3) if there was previous partial Bin it will be filled with the new data
|
||||
// 4) if there is insufficient new data to fill the partial Bin it will fill the partial Bin with the new data
|
||||
LOGD("Binning %d samples of %d size buffer", size / sizeof(T), size);
|
||||
|
||||
assert(size % (sizeof(T) * channels) == 0); // Ensure proper buffer size
|
||||
|
||||
int sample_count = size / (sizeof(T) * channels); // new available samples in each channel
|
||||
int total_samples = partialBinSize + sample_count; // total samples available for each channel including previous number of sample in partial bin
|
||||
int bin_count = total_samples / binSize; // number of bins we can make
|
||||
int remaining_samples = total_samples % binSize; // remaining samples after binning
|
||||
T *p_target = (T *)target;
|
||||
T *p_source = (T *)src;
|
||||
size_t result_size = 0;
|
||||
|
||||
// Allocate stack memory for sums to avoid dynamic allocation overhead.
|
||||
// Ensure you have enough stack space or adjust accordingly for your
|
||||
// environment.
|
||||
// Allocate sum for each channel with appropriate type
|
||||
typename AppropriateSumType<T>::type sums[channels];
|
||||
int current_sample = 0; // current sample index
|
||||
|
||||
// Is there a partial bin from the previous call?
|
||||
// ----
|
||||
if (partialBinSize > 0) {
|
||||
|
||||
int samples_needed = binSize - partialBinSize;
|
||||
bool have_enough_samples = (samples_needed < sample_count);
|
||||
int samples_to_bin = have_enough_samples ? samples_needed : sample_count;
|
||||
|
||||
for (int i = 0; i < frame_count; i += binSize) {
|
||||
// Initialize sums for each channel to the first element in the bin
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
sums[ch] = (i * channels + ch < frame_count * channels)
|
||||
? p_source[i * channels + ch]
|
||||
: static_cast<T>(0);
|
||||
sums[ch] = partialBin[ch];
|
||||
}
|
||||
|
||||
// Sum up binSize number of samples for each channel, starting from the
|
||||
// second sample in the bin
|
||||
for (int j = 1; j < binSize && (i + j) < frame_count; j++) {
|
||||
for (int j = 0; j < samples_to_bin; j++) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
sums[ch] += p_source[(i + j) * channels + ch];
|
||||
sums[ch] += p_source[current_sample * channels + ch];
|
||||
}
|
||||
current_sample++;
|
||||
}
|
||||
|
||||
// Compute average or sum for each channel and write to target buffer
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
if (have_enough_samples) {
|
||||
// Store the completed bin
|
||||
if (average) {
|
||||
T avg = static_cast<T>(sums[ch] / binSize);
|
||||
*p_target++ = avg;
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
p_target[result_size / sizeof(T)] = static_cast<T>(sums[ch] / binSize);
|
||||
result_size += sizeof(T);
|
||||
}
|
||||
} else {
|
||||
*p_target++ = static_cast<T>(sums[ch]);
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
p_target[result_size / sizeof(T)] = static_cast<T>(sums[ch]);
|
||||
result_size += sizeof(T);
|
||||
}
|
||||
}
|
||||
partialBinSize = 0;
|
||||
} else {
|
||||
// Not enough samples to complete the bin, update partialBin
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
partialBin[ch] = sums[ch];
|
||||
}
|
||||
partialBinSize += current_sample;
|
||||
return result_size;
|
||||
}
|
||||
result_size += sizeof(T) * channels;
|
||||
}
|
||||
|
||||
// LOGI("%d: %d -> %d avg:%s", binSize, (int)size, (int)result_size, average
|
||||
// ? "on" : "off");
|
||||
// Fill bins
|
||||
// ----
|
||||
for (int i = 0; i < bin_count; i++) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
sums[ch] = p_source[current_sample * channels + ch]; // Initialize sums with first value in the input buffer
|
||||
}
|
||||
|
||||
for (int j = 1; j < binSize; j++) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
sums[ch] += p_source[(current_sample + j) * channels + ch];
|
||||
}
|
||||
}
|
||||
current_sample += binSize;
|
||||
|
||||
// Store the bin result
|
||||
if (average) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
p_target[result_size / sizeof(T)] = static_cast<T>(sums[ch] / binSize);
|
||||
result_size += sizeof(T);
|
||||
}
|
||||
} else {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
p_target[result_size / sizeof(T)] = static_cast<T>(sums[ch]);
|
||||
result_size += sizeof(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store the remaining samples in the partial bin
|
||||
// ----
|
||||
for (int i = 0; i < remaining_samples; i++) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
partialBin[ch] += p_source[(current_sample + i) * channels + ch];
|
||||
}
|
||||
}
|
||||
partialBinSize = remaining_samples;
|
||||
|
||||
return result_size;
|
||||
}
|
||||
|
||||
operator bool() { return binSize > 1; };
|
||||
|
||||
protected:
|
||||
int channels = 2;
|
||||
int binSize = 1;
|
||||
bool average = true;
|
||||
uint16_t count = 0;
|
||||
T *partialBin;
|
||||
int partialBinSize;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -681,17 +759,15 @@ class Bin : public BaseConverter {
|
||||
public:
|
||||
Bin() = default;
|
||||
Bin(int binSize, int channels, bool average, int bits_per_sample) {
|
||||
setBinSize(binSize);
|
||||
setChannels(channels);
|
||||
setBinSize(binSize);
|
||||
setAverage(average);
|
||||
setBits(bits_per_sample);
|
||||
setBits(bits_per_sample);
|
||||
}
|
||||
/// Defines the number of channels
|
||||
|
||||
void setChannels(int channels) { this->channels = channels; }
|
||||
void setBits(int bits) { this->bits = bits; }
|
||||
/// Sets the binning size: e.g. with 4 we sum 4 samples
|
||||
void setBinSize(int binSize) { this->binSize = binSize; }
|
||||
/// Enables averaging: e.g. when true it divides the sum by number of bins
|
||||
void setAverage(bool average) { this->average = average; }
|
||||
|
||||
size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); }
|
||||
@ -718,11 +794,8 @@ class Bin : public BaseConverter {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
operator bool() { return binSize > 1; };
|
||||
|
||||
protected:
|
||||
int channels = 2;
|
||||
int bits = 16;
|
||||
@ -730,6 +803,380 @@ class Bin : public BaseConverter {
|
||||
bool average = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief We calculate the difference between pairs of channels in a datastream.
|
||||
* E.g. if we have 4 channels we end up with 2 channels.
|
||||
* The channels will be
|
||||
* channel_1 - channel_2
|
||||
* channel_3 - channel_4
|
||||
* This is similar to background subtraction between two channels but will
|
||||
* also work for quadric, sexic or octic audio.
|
||||
* This will not work if you provide single channel data!
|
||||
* @author Urs Utzinger
|
||||
* @ingroup convert
|
||||
* @tparam T
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
class ChannelDiffT : public BaseConverter {
|
||||
public:
|
||||
ChannelDiffT() {}
|
||||
|
||||
size_t convert(uint8_t *src, size_t size) override { return convert(src, src, size); }
|
||||
|
||||
size_t convert(uint8_t *target, uint8_t *src, size_t size) {
|
||||
LOGD("convert %d samples of %d size buffer", size / sizeof(T), size);
|
||||
|
||||
// Ensure the buffer size is even for pairs of channels
|
||||
assert(size % (sizeof(T) * 2) == 0);
|
||||
|
||||
int sample_count = size / (sizeof(T) * 2); // Each pair of channels produces one output sample
|
||||
T *p_result = (T *)target;
|
||||
T *p_source = (T *)src;
|
||||
|
||||
for (int i = 0; i < sample_count; i++) {
|
||||
*p_result++ = *p_source++ - *p_source++;
|
||||
}
|
||||
|
||||
return sizeof(T) * sample_count;
|
||||
}
|
||||
};
|
||||
|
||||
class ChannelDiff : public BaseConverter {
|
||||
public:
|
||||
ChannelDiff() = default;
|
||||
ChannelDiff(int bitsPerSample) {
|
||||
setBits(bitsPerSample);
|
||||
}
|
||||
void setBits(int bits) { this->bits = bits; }
|
||||
|
||||
size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); }
|
||||
size_t convert(uint8_t *target, uint8_t *src, size_t size) {
|
||||
switch (bits) {
|
||||
case 8: {
|
||||
ChannelDiffT<int8_t> cd8;
|
||||
return cd8.convert(target, src, size);
|
||||
}
|
||||
case 16: {
|
||||
ChannelDiffT<int16_t> cd16;
|
||||
return cd16.convert(target, src, size);
|
||||
}
|
||||
case 24: {
|
||||
ChannelDiffT<int24_t> cd24;
|
||||
return cd24.convert(target, src, size);
|
||||
}
|
||||
case 32: {
|
||||
ChannelDiffT<int32_t> cd32;
|
||||
return cd32.convert(target, src, size);
|
||||
}
|
||||
default: {
|
||||
LOGE("Number of bits %d not supported.", bits);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
int bits = 16;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief We average pairs of channels in a datastream.
|
||||
* E.g. if we have 4 channels we end up with 2 channels.
|
||||
* The channels will be
|
||||
* (channel_1 + channel_2)/2
|
||||
* (channel_3 - channel_4)/2.
|
||||
* This is equivalent of stereo to mono conversion but will
|
||||
* also work for quadric, sexic or octic audio.
|
||||
* This will not work if you provide single channel data!
|
||||
* @author Urs Utzinger
|
||||
* @ingroup convert
|
||||
* @tparam T
|
||||
*/
|
||||
template <typename T>
|
||||
class ChannelAvgT : public BaseConverter {
|
||||
public:
|
||||
ChannelAvgT() {}
|
||||
|
||||
size_t convert(uint8_t *src, size_t size) override { return convert(src, src, size); }
|
||||
|
||||
size_t convert(uint8_t *target, uint8_t *src, size_t size) {
|
||||
LOGD("convert %d samples of %d size buffer", size / sizeof(T), size);
|
||||
|
||||
assert(size % (sizeof(T) * 2) == 0); // Ensure even number of samples for pairs
|
||||
|
||||
int sample_count = size / (sizeof(T) * 2); // Each pair of channels produces one output sample
|
||||
T *p_result = (T *)target;
|
||||
T *p_source = (T *)src;
|
||||
|
||||
for (int i = 0; i < sample_count; i++) {
|
||||
*p_result++ = (*p_source++ + *p_source++) / 2; // Average the pair of channels
|
||||
}
|
||||
|
||||
return sizeof(T) * sample_count;
|
||||
}
|
||||
};
|
||||
|
||||
class ChannelAvg : public BaseConverter {
|
||||
public:
|
||||
ChannelAvg() = default;
|
||||
ChannelAvg(int bitsPerSample) {
|
||||
setBits(bitsPerSample);
|
||||
}
|
||||
void setBits(int bits) { this->bits = bits; }
|
||||
|
||||
size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); }
|
||||
size_t convert(uint8_t *target, uint8_t *src, size_t size) {
|
||||
switch (bits) {
|
||||
case 8: {
|
||||
ChannelAvgT<int8_t> ca8;
|
||||
return ca8.convert(target, src, size);
|
||||
}
|
||||
case 16: {
|
||||
ChannelAvgT<int16_t> ca16;
|
||||
return ca16.convert(target, src, size);
|
||||
}
|
||||
case 24: {
|
||||
ChannelAvgT<int24_t> ca24;
|
||||
return ca24.convert(target, src, size);
|
||||
}
|
||||
case 32: {
|
||||
ChannelAvgT<int32_t> ca32;
|
||||
return ca32.convert(target, src, size);
|
||||
}
|
||||
default: {
|
||||
LOGE("Number of bits %d not supported.", bits);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
int bits = 16;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief We first bin the channels then we calculate the difference between pairs of channels in a datastream.
|
||||
* E.g. For binning, if we bin 4 samples in each channel we will have 4 times less samples per channel
|
||||
* E.g. For subtracting if we have 4 channels we end up with 2 channels.
|
||||
* The channels will be
|
||||
* channel_1 - channel_2
|
||||
* channel_3 - channel_4
|
||||
* This is the same as combining binning and subtracting channels.
|
||||
* This will not work if you provide single channel data!
|
||||
* @author Urs Utzinger
|
||||
* @ingroup convert
|
||||
* @tparam T
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
class ChannelBinDiffT : public BaseConverter {
|
||||
public:
|
||||
ChannelBinDiffT() = default;
|
||||
ChannelBinDiffT(int binSize, int channels, bool average) {
|
||||
setChannels(channels);
|
||||
setBinSize(binSize);
|
||||
setAverage(average);
|
||||
this->partialBinSize = 0;
|
||||
this->partialBin = new T[channels];
|
||||
std::fill(this->partialBin, this->partialBin + channels, 0); // Initialize partialBin with zeros
|
||||
}
|
||||
|
||||
~ChannelBinDiffT() {
|
||||
delete[] this->partialBin;
|
||||
}
|
||||
|
||||
void setChannels(int channels) {
|
||||
assert((channels % 2) == 0); // Ensure even channel size
|
||||
this->channels = channels;
|
||||
}
|
||||
void setBinSize(int binSize) { this->binSize = binSize; }
|
||||
void setAverage(bool average) { this->average = average; }
|
||||
|
||||
size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); }
|
||||
|
||||
size_t convert(uint8_t *target, uint8_t *src, size_t size) {
|
||||
// The binning works the same as in the BinT class
|
||||
// Here we add subtraction before we store the bins
|
||||
LOGD("Binning and Subtracting %d samples of %d size buffer", size / sizeof(T), size);
|
||||
|
||||
assert(size % (sizeof(T) * channels) == 0); // Ensure proper buffer size
|
||||
|
||||
int sample_count = size / (sizeof(T) * channels); // new available samples in each channel
|
||||
int total_samples = partialBinSize + sample_count; // total samples available for each channel including previous number of sample in partial bin
|
||||
int bin_count = total_samples / binSize; // number of bins we can make
|
||||
int remaining_samples = total_samples % binSize; // remaining samples after binning
|
||||
T *p_target = (T *)target;
|
||||
T *p_source = (T *)src;
|
||||
size_t result_size = 0;
|
||||
|
||||
// Allocate sum for each channel with appropriate type
|
||||
typename AppropriateSumType<T>::type sums[channels];
|
||||
int current_sample = 0; // current sample index
|
||||
|
||||
// Is there a partial bin from the previous call?
|
||||
// ----
|
||||
if (partialBinSize > 0) {
|
||||
|
||||
LOGD("Deal with partial bins");
|
||||
|
||||
int samples_needed = binSize - partialBinSize;
|
||||
bool have_enough_samples = (samples_needed < sample_count);
|
||||
int samples_to_bin = have_enough_samples ? samples_needed : sample_count;
|
||||
|
||||
// initialize
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
sums[ch] = partialBin[ch];
|
||||
}
|
||||
|
||||
// continue binning
|
||||
for (int j = 0; j < samples_to_bin; j++) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
sums[ch] += p_source[current_sample * channels + ch];
|
||||
}
|
||||
current_sample++;
|
||||
}
|
||||
|
||||
// store the bin results or update the partial bin
|
||||
if (have_enough_samples) {
|
||||
// Subtract two channels and store the completed bin
|
||||
if (average) {
|
||||
for (int ch = 0; ch < channels; ch+=2) {
|
||||
p_target[result_size / sizeof(T)] = static_cast<T>((sums[ch] - sums[ch+1]) / binSize);
|
||||
result_size += sizeof(T);
|
||||
}
|
||||
} else {
|
||||
for (int ch = 0; ch < channels; ch+=2) {
|
||||
p_target[result_size / sizeof(T)] = static_cast<T>((sums[ch] - sums[ch+1]));
|
||||
result_size += sizeof(T);
|
||||
}
|
||||
}
|
||||
partialBinSize = 0;
|
||||
LOGD("Partial bins are empty");
|
||||
|
||||
} else {
|
||||
// Not enough samples to complete the bin, update partialBin
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
partialBin[ch] = sums[ch];
|
||||
}
|
||||
partialBinSize += current_sample;
|
||||
LOGD("Partial bins were updated");
|
||||
return result_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill bins
|
||||
// ----
|
||||
LOGD("Fillin bins");
|
||||
for (int i = 0; i < bin_count; i++) {
|
||||
|
||||
LOGD("Current sample %d", current_sample);
|
||||
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
sums[ch] = p_source[current_sample * channels + ch]; // Initialize sums with first value in the input buffer
|
||||
}
|
||||
|
||||
for (int j = 1; j < binSize; j++) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
sums[ch] += p_source[(current_sample + j) * channels + ch];
|
||||
}
|
||||
}
|
||||
current_sample += binSize;
|
||||
|
||||
// Finish binning, then subtact two channel and store the result
|
||||
if (average) {
|
||||
for (int ch = 0; ch < channels; ch+=2) {
|
||||
p_target[result_size / sizeof(T)] = static_cast<T>((sums[ch]-sums[ch+1]) / binSize);
|
||||
result_size += sizeof(T);
|
||||
}
|
||||
} else {
|
||||
for (int ch = 0; ch < channels; ch+=2) {
|
||||
p_target[result_size / sizeof(T)] = static_cast<T>((sums[ch]-sums[ch+1]));
|
||||
result_size += sizeof(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store the remaining samples in the partial bin
|
||||
// ----
|
||||
LOGD("Updating partial bins");
|
||||
for (int i = 0; i < remaining_samples; i++) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
partialBin[ch] += p_source[(current_sample + i) * channels + ch];
|
||||
}
|
||||
}
|
||||
partialBinSize = remaining_samples;
|
||||
|
||||
return result_size;
|
||||
}
|
||||
|
||||
protected:
|
||||
int channels = 2;
|
||||
int binSize = 4;
|
||||
bool average = true;
|
||||
T *partialBin;
|
||||
int partialBinSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides combination of binning and subtracting channels
|
||||
* @author Urs Utzinger
|
||||
* @ingroup convert
|
||||
* @tparam T
|
||||
*/
|
||||
|
||||
class ChannelBinDiff : public BaseConverter {
|
||||
public:
|
||||
ChannelBinDiff() = default;
|
||||
ChannelBinDiff(int binSize, int channels, bool average, int bits_per_sample) {
|
||||
setChannels(channels);
|
||||
setBinSize(binSize);
|
||||
setAverage(average);
|
||||
setBits(bits_per_sample);
|
||||
}
|
||||
|
||||
void setChannels(int channels) {
|
||||
assert((channels % 2) == 0); // Ensure even channel size
|
||||
this->channels = channels;
|
||||
}
|
||||
void setBits(int bits) { this->bits = bits; }
|
||||
void setBinSize(int binSize) { this->binSize = binSize; }
|
||||
void setAverage(bool average) { this->average = average; }
|
||||
|
||||
size_t convert(uint8_t *src, size_t size) { return convert(src, src, size); }
|
||||
size_t convert(uint8_t *target, uint8_t *src, size_t size) {
|
||||
switch (bits) {
|
||||
case 8: {
|
||||
ChannelBinDiffT<int8_t> bd8(binSize, channels, average);
|
||||
return bd8.convert(target, src, size);
|
||||
}
|
||||
case 16: {
|
||||
ChannelBinDiffT<int16_t> bd16(binSize, channels, average);
|
||||
return bd16.convert(target, src, size);
|
||||
}
|
||||
case 24: {
|
||||
ChannelBinDiffT<int24_t> bd24(binSize, channels, average);
|
||||
return bd24.convert(target, src, size);
|
||||
}
|
||||
case 32: {
|
||||
ChannelBinDiffT<int32_t> bd32(binSize, channels, average);
|
||||
return bd32.convert(target, src, size);
|
||||
}
|
||||
default: {
|
||||
LOGE("Number of bits %d not supported.", bits);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
int channels = 2;
|
||||
int bits = 16;
|
||||
int binSize = 4;
|
||||
bool average = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Increases the channel count
|
||||
* @ingroup convert
|
||||
@ -1018,7 +1465,7 @@ class Converter1Channel : public BaseConverter {
|
||||
public:
|
||||
Converter1Channel(Filter<T> &filter) { this->p_filter = &filter; }
|
||||
|
||||
size_t convert(uint8_t *src, size_t size) {
|
||||
size_t convert(uint8_t *src, size_t size) override {
|
||||
T *data = (T *)src;
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
data[j] = p_filter->process(data[j]);
|
||||
|
Loading…
Reference in New Issue
Block a user