This commit is contained in:
pschatzmann 2024-07-19 09:08:42 +02:00
commit fab4a76d57
14 changed files with 61506 additions and 77 deletions

6
docs/index.html Normal file
View File

@ -0,0 +1,6 @@
<head>
<meta http-equiv="refresh" content="0; URL=https://pschatzmann.github.io/arduino-audio-tools/modules.html" />
</head>
<body>
<p>If you are not redirected, <a href="https://pschatzmann.github.io/arduino-audio-tools/modules.html">click here</a>.</p>
</body>

View File

@ -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

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

298
jupyter/Jupyter.ipynb Normal file

File diff suppressed because one or more lines are too long

View File

@ -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) {

View File

@ -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]);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff