Introduce BaseStream class

This commit is contained in:
pschatzmann 2024-05-02 19:45:20 +02:00
parent cb5b33231f
commit 727985d94f
23 changed files with 770 additions and 685 deletions

View File

@ -1,40 +1,18 @@
../examples/examples-basic-api/base-adc-a2dp -> rc=0
../examples/examples-basic-api/base-adc-average-mono-serial -> rc=0
../examples/examples-basic-api/base-adc-measure -> rc=0
../examples/examples-basic-api/base-adc-serial -> rc=0
../examples/examples-basic-api/base-file_raw-serial -> rc=0
../examples/examples-basic-api/base-generator-a2dp -> rc=0
../examples/examples-basic-api/base-i2s-a2dp -> rc=0
../examples/examples-basic-api/base-player-a2dp -> rc=0
../examples/examples-basic-api/base-SynchronizedBufferRTOS -> rc=0
../examples/examples-player/player-callback-i2s -> rc=0
../examples/examples-player/player-littlefs-i2s -> rc=0
../examples/examples-player/player-sdfat-a2dp -> rc=0
../examples/examples-player/player-sdfat-analog -> rc=0
../examples/examples-player/player-sdfat-ffti2s -> rc=0
../examples/examples-player/player-sdfat-i2s -> rc=0
../examples/examples-player/player-sd-i2s -> rc=0
../examples/examples-player/player-spiffs-i2s -> rc=0
../examples/examples-player/player-url-i2s -> rc=0
../examples/examples-player/player-url_icy-i2s -> rc=0
../examples/examples-player/player-url_subclass-i2s -> rc=0
../examples/examples-webserver/streams-effect-webserver_wav -> rc=0
../examples/examples-webserver/streams-flite-webserver_wav -> rc=1
../examples/examples-webserver/streams-generator-webserver_aac -> rc=0
../examples/examples-webserver/streams-generator-webserverex_wav -> rc=0
../examples/examples-webserver/streams-generator-webserverex_wav1 -> rc=0
../examples/examples-webserver/streams-generator-webserver_mp3 -> rc=0
../examples/examples-webserver/streams-generator-webserver_ogg -> rc=0
../examples/examples-webserver/streams-generator-webserver_wav -> rc=0
../examples/examples-webserver/streams-i2s-webserver_wav -> rc=0
../examples/examples-webserver/streams-sam-webserver_wav -> rc=0
../examples/examples-webserver/streams-tts-webserver_wav -> rc=0
../examples/examples-stream/streams-a2dp-serial -> rc=0
../examples/examples-stream/streams-adc-i2s -> rc=0
../examples/examples-stream/streams-adc-serial -> rc=0
../examples/examples-stream/streams-adsr-i2s -> rc=0
../examples/examples-stream/streams-eth_url_mp3_helix-i2s -> rc=1
../examples/examples-stream/streams-generator-a2dp -> rc=0
../examples/examples-stream/streams-generator-analog -> rc=0
../examples/examples-stream/streams_generator_bin_serial -> rc=0
../examples/examples-stream/streams-generator-formatconverter-i2s -> rc=0
@ -46,11 +24,10 @@
../examples/examples-stream/streams-generator-spdif -> rc=0
../examples/examples-stream/streams-generator-volume -> rc=0
../examples/examples-stream/streams-generator-wm8960 -> rc=0
../examples/examples-stream/streams-i2s-a2dp -> rc=0
../examples/examples-stream/streams-i2s-filter-i2s -> rc=0
../examples/examples-stream/streams-i2s-i2s -> rc=0
../examples/examples-stream/streams-i2s-i2s-2 -> rc=0
../examples/examples-stream/streams-i2s_pdm-serial -> rc=1
../examples/examples-stream/streams-i2s_pdm-serial -> rc=0
../examples/examples-stream/streams-i2s-serial -> rc=0
../examples/examples-stream/streams-i2s-serial_16bit -> rc=0
../examples/examples-stream/streams-i2s-tf -> rc=0
@ -67,35 +44,14 @@
../examples/examples-stream/streams-sd_mp3-i2s -> rc=0
../examples/examples-stream/streams-sd_wav4-i2s -> rc=0
../examples/examples-stream/streams-tf-i2s -> rc=0
../examples/examples-stream/streams-url_aac-i2s -> rc=0
../examples/examples-stream/streams-url-file -> rc=0
../examples/examples-stream/streams-url_flac-i2s -> rc=0
../examples/examples-stream/streams-url-measuring -> rc=0
../examples/examples-stream/streams-url_mp3-analog -> rc=0
../examples/examples-stream/streams-url_mp3_helix-i2s -> rc=0
../examples/examples-stream/streams-url_mp3_helix-i2s_32bit -> rc=0
../examples/examples-stream/streams-url_mp3_mad-i2s -> rc=0
../examples/examples-stream/streams-url_mp3-metadata -> rc=0
../examples/examples-stream/streams-url_mp3-metadata2 -> rc=0
../examples/examples-stream/streams-url_mts-hex -> rc=1
../examples/examples-stream/streams-url_raw-i2s -> rc=0
../examples/examples-stream/streams-url_raw-serial -> rc=0
../examples/examples-stream/streams-url_vorbis_i2s -> rc=0
../examples/examples-audiokit/basic-a2dp-audiokit -> rc=0
../examples/examples-audiokit/basic-a2dp-eq-audiokit -> rc=0
../examples/examples-audiokit/basic-audiokit-a2dp -> rc=0
../examples/examples-audiokit/player-sd_a2dp-audiokit -> rc=0
../examples/examples-audiokit/player-sd-audiokit -> rc=0
../examples/examples-audiokit/player-sdfat-audiokit -> rc=0
../examples/examples-audiokit/player-sdmmc-audiokit -> rc=0
../examples/examples-audiokit/player-url_icy-audiokit -> rc=0
../examples/examples-audiokit/README.md -> rc=1
../examples/examples-audiokit/stream-hls-audiokit -> rc=1
../examples/examples-audiokit/streams-a2dp-audiokit -> rc=0
../examples/examples-audiokit/streams-audiokit-audiokit -> rc=0
../examples/examples-audiokit/streams-audiokit-effects-audiokit -> rc=0
../examples/examples-audiokit/streams-audiokit-fft -> rc=0
../examples/examples-audiokit/streams-audiokit-fft-led -> rc=1
../examples/examples-audiokit/streams-audiokit-fft-led -> rc=0
../examples/examples-audiokit/streams-audiokit-filter-audiokit -> rc=0
../examples/examples-audiokit/streams-audiokit-multioutput -> rc=0
../examples/examples-audiokit/streams-audiokit-multioutput-server -> rc=0
@ -105,9 +61,6 @@
../examples/examples-audiokit/streams-audiokit-sd_wav -> rc=0
../examples/examples-audiokit/streams-audiokit-serial -> rc=0
../examples/examples-audiokit/streams-audiokit-tf -> rc=0
../examples/examples-audiokit/streams-audiokit-webserver_aac -> rc=0
../examples/examples-audiokit/streams-audiokit-webserver_mp3 -> rc=1
../examples/examples-audiokit/streams-audiokit-webserver_wav -> rc=0
../examples/examples-audiokit/streams-file_loop-audiokit -> rc=0
../examples/examples-audiokit/streams-generator-audiokit -> rc=0
../examples/examples-audiokit/streams-generator_fromarray-audiokit -> rc=0
@ -116,23 +69,20 @@
../examples/examples-audiokit/streams-generator_sinfromtable-audiokit -> rc=0
../examples/examples-audiokit/streams-memory_mp3-audiokit -> rc=0
../examples/examples-audiokit/streams-memory_pcm-mixer-audiokit -> rc=0
../examples/examples-audiokit/streams-pins-audiokit -> rc=1
../examples/examples-audiokit/streams-pins-audiokit -> rc=0
../examples/examples-audiokit/streams-sd-audiokit -> rc=0
../examples/examples-audiokit/streams-sdmmc_wav-audiokit -> rc=0
../examples/examples-audiokit/streams-synth-a2dp -> rc=1
../examples/examples-audiokit/streams-synth-audiokit -> rc=0
../examples/examples-audiokit/streams-synthbasic1-audiokit -> rc=0
../examples/examples-audiokit/streams-synthbasic2-audiokit -> rc=0
../examples/examples-audiokit/streams-synthbasic3-audiokit -> rc=1
../examples/examples-audiokit/streams-synthstk-audiokit -> rc=0
../examples/examples-audiokit/streams-tf-audiokit -> rc=0
../examples/examples-audiokit/streams-url_aac-audiokit -> rc=0
../examples/examples-audiokit/streams-url_mp3-audiokit -> rc=0
../examples/examples-tts/streams-azure_tts-i2s -> rc=0
../examples/examples-tts/streams-espeak-audiokit -> rc=1
../examples/examples-tts/streams-espeak-i2s -> rc=1
../examples/examples-tts/streams-flite-audiokit -> rc=1
../examples/examples-tts/streams-flite-i2s -> rc=1
../examples/examples-tts/streams-espeak-audiokit -> rc=0
../examples/examples-tts/streams-espeak-i2s -> rc=0
../examples/examples-tts/streams-flite-audiokit -> rc=0
../examples/examples-tts/streams-flite-i2s -> rc=0
../examples/examples-tts/streams-google-audiokit -> rc=0
../examples/examples-tts/streams-sam-audiokit -> rc=0
../examples/examples-tts/streams-sam-i2s -> rc=0
@ -185,6 +135,25 @@
../examples/examples-dsp/examples-faust/streams-i2s-faust_copy-i2s -> rc=0
../examples/examples-dsp/examples-faust/streams-i2s-faust_guitarix-i2s -> rc=0
../examples/examples-dsp/examples-faust/streams-i2s-faust_pitchshift-i2s -> rc=0
../examples/examples-communication/a2dp/base-adc-a2dp -> rc=0
../examples/examples-communication/a2dp/base-generator-a2dp -> rc=0
../examples/examples-communication/a2dp/base-i2s-a2dp -> rc=0
../examples/examples-communication/a2dp/base-player-a2dp -> rc=0
../examples/examples-communication/a2dp/basic-a2dp-audiokit -> rc=0
../examples/examples-communication/a2dp/basic-a2dp-eq-audiokit -> rc=0
../examples/examples-communication/a2dp/basic-a2dp-fft -> rc=0
../examples/examples-communication/a2dp/basic-a2dp-fft-led -> rc=0
../examples/examples-communication/a2dp/basic-a2dp-i2s -> rc=1
../examples/examples-communication/a2dp/basic-a2dp-mixer-i2s -> rc=0
../examples/examples-communication/a2dp/basic-a2dp-spdif -> rc=0
../examples/examples-communication/a2dp/basic-audiokit-a2dp -> rc=0
../examples/examples-communication/a2dp/player-sd_a2dp-audiokit -> rc=0
../examples/examples-communication/a2dp/player-sdfat-a2dp -> rc=0
../examples/examples-communication/a2dp/streams-a2dp-audiokit -> rc=0
../examples/examples-communication/a2dp/streams-a2dp-serial -> rc=0
../examples/examples-communication/a2dp/streams-generator-a2dp -> rc=0
../examples/examples-communication/a2dp/streams-i2s-a2dp -> rc=0
../examples/examples-communication/a2dp/streams-synth-a2dp -> rc=1
../examples/examples-communication/esp-now/codec/communication-codec-espnow-receive -> rc=0
../examples/examples-communication/esp-now/codec/communication-codec-espnow-receive_measure -> rc=0
../examples/examples-communication/esp-now/codec/communication-codec-espnow-send -> rc=0
@ -197,11 +166,62 @@
../examples/examples-communication/ip/communication-ip-receive -> rc=0
../examples/examples-communication/ip/communication-ip-receive_measure -> rc=0
../examples/examples-communication/ip/communication-ip-send -> rc=0
../examples/examples-communication/rtsp/communication-audiokit-rtsp -> rc=0
../examples/examples-communication/rtsp/communication-codec-rtsp -> rc=0
../examples/examples-communication/rtsp/communication-generator-rtsp -> rc=0
../examples/examples-communication/udp/communication-udp-receive -> rc=0
../examples/examples-communication/udp/communication-udp-send -> rc=0
../examples/examples-communication/vban/player-sdmmc-vban -> rc=0
../examples/examples-communication/vban/streams-audiokit-vban -> rc=0
../examples/examples-communication/vban/streams-generator-vban -> rc=0
../examples/examples-communication/vban/streams-vban-audiokit -> rc=0
../examples/examples-communication/rtsp/communication-audiokit-rtsp -> rc=1
../examples/examples-communication/rtsp/communication-codec-rtsp -> rc=1
../examples/examples-communication/rtsp/communication-generator-rtsp -> rc=1
../examples/examples-communication/rtsp/communication-rtsp-audiokit -> rc=0
../examples/examples-communication/rtsp/communication-rtsp-i2s -> rc=0
../examples/examples-communication/serial/send-8bit-receive -> rc=0
../examples/examples-communication/serial/send-adpcm_framed-receive -> rc=1
../examples/examples-communication/serial/send-adpcm-receive -> rc=0
../examples/examples-communication/serial/send-receive -> rc=0
../examples/examples-communication/snapcast/snapclient-i2s -> rc=1
../examples/examples-communication/spi/spi-master -> rc=0
../examples/examples-communication/spi/spi-slave-esp32 -> rc=0
../examples/examples-communication/spi/spi-slave-rp2040 -> rc=1
../examples/examples-communication/http-client/player-url-i2s -> rc=0
../examples/examples-communication/http-client/player-url_icy-audiokit -> rc=0
../examples/examples-communication/http-client/player-url_icy-i2s -> rc=0
../examples/examples-communication/http-client/player-url_subclass-i2s -> rc=0
../examples/examples-communication/http-client/streams-eth_url_mp3_helix-i2s -> rc=1
../examples/examples-communication/http-client/streams-url_aac-audiokit -> rc=0
../examples/examples-communication/http-client/streams-url_aac-i2s -> rc=0
../examples/examples-communication/http-client/streams-url-file -> rc=0
../examples/examples-communication/http-client/streams-url_flac-i2s -> rc=0
../examples/examples-communication/http-client/streams-url-measuring -> rc=0
../examples/examples-communication/http-client/streams-url_mp3-analog -> rc=0
../examples/examples-communication/http-client/streams-url_mp3-audiokit -> rc=0
../examples/examples-communication/http-client/streams-url_mp3_helix-i2s -> rc=0
../examples/examples-communication/http-client/streams-url_mp3_helix-i2s_32bit -> rc=0
../examples/examples-communication/http-client/streams-url_mp3_mad-i2s -> rc=0
../examples/examples-communication/http-client/streams-url_mp3-metadata -> rc=0
../examples/examples-communication/http-client/streams-url_mp3-metadata2 -> rc=0
../examples/examples-communication/http-client/streams-url_mts-hex -> rc=1
../examples/examples-communication/http-client/streams-url_raw-i2s -> rc=0
../examples/examples-communication/http-client/streams-url_raw-serial -> rc=0
../examples/examples-communication/http-client/streams-url_vorbis_i2s -> rc=0
../examples/examples-communication/http-server/player-sd-webserverex_mp3 -> rc=0
../examples/examples-communication/http-server/README.md -> rc=1
../examples/examples-communication/http-server/streams-audiokit-webserver_aac -> rc=0
../examples/examples-communication/http-server/streams-audiokit-webserver_mp3 -> rc=0
../examples/examples-communication/http-server/streams-audiokit-webserver_wav -> rc=0
../examples/examples-communication/http-server/streams-effect-webserver_wav -> rc=0
../examples/examples-communication/http-server/streams-flite-webserver_wav -> rc=0
../examples/examples-communication/http-server/streams-generator-webserver_aac -> rc=0
../examples/examples-communication/http-server/streams-generator-webserverex_wav -> rc=0
../examples/examples-communication/http-server/streams-generator-webserverex_wav1 -> rc=0
../examples/examples-communication/http-server/streams-generator-webserver_mp3 -> rc=0
../examples/examples-communication/http-server/streams-generator-webserver_ogg -> rc=0
../examples/examples-communication/http-server/streams-generator-webserver_wav -> rc=0
../examples/examples-communication/http-server/streams-i2s-webserver_wav -> rc=0
../examples/examples-communication/http-server/streams-sam-webserver_wav -> rc=0
../examples/examples-communication/http-server/streams-tts-webserver_wav -> rc=0
../examples/tests/adc/read-csv -> rc=0
../examples/tests/adc/read-csv_unsigned -> rc=0
../examples/tests/adc/read-speed -> rc=0
@ -213,7 +233,7 @@
../examples/tests/codecs/test-codec-adpcm-xq -> rc=0
../examples/tests/codecs/test-codec-aptx -> rc=0
../examples/tests/codecs/test-codec-base64 -> rc=0
../examples/tests/codecs/test-codec-codec2 -> rc=1
../examples/tests/codecs/test-codec-codec2 -> rc=0
../examples/tests/codecs/test-codec-flac -> rc=0
../examples/tests/codecs/test-codec-g711_alaw -> rc=0
../examples/tests/codecs/test-codec-g711_ulaw -> rc=0
@ -247,6 +267,9 @@
../examples/tests/conversion/channel-converter-reduce-in -> rc=0
../examples/tests/conversion/channel-converter-reduce-out -> rc=0
../examples/tests/conversion/format-converter-in -> rc=0
../examples/tests/conversion/pipeline-in -> rc=0
../examples/tests/conversion/pipeline-out -> rc=0
../examples/tests/conversion/resample-mixer-in -> rc=0
../examples/tests/conversion/test-panning -> rc=0
../examples/tests/conversion/test-resample-in -> rc=0
../examples/tests/conversion/test-resample-out -> rc=0
@ -261,12 +284,12 @@
../examples/tests/etc/test-mulit-compilation-units -> rc=0
../examples/tests/etc/test-pins -> rc=0
../examples/tests/etc/test-ringbufferfile -> rc=0
../examples/tests/etc/test-tdm -> rc=1
../examples/tests/etc/test-tdm -> rc=0
../examples/tests/etc/test-write-memory -> rc=0
../examples/tests/fft/fft-cmsis -> rc=1
../examples/tests/fft/fft-esp32 -> rc=1
../examples/tests/fft/fft-esp32 -> rc=0
../examples/tests/fft/fft-espressif -> rc=0
../examples/tests/fft/fft-kiss -> rc=1
../examples/tests/fft/fft-kiss -> rc=0
../examples/tests/fft/fft-real -> rc=0
../examples/tests/fft/fft-topn -> rc=0
../examples/tests/fft/fft-window -> rc=0

View File

@ -31,7 +31,6 @@ function compile_example {
rm build-examples-log.txt
compile_example "esp32:esp32:esp32" "../examples/examples-basic-api/base*"
compile_example "esp32:esp32:esp32" "../examples/examples-player/player*"
compile_example "esp32:esp32:esp32" "../examples/examples-webserver/str*"
compile_example "esp32:esp32:esp32" "../examples/examples-stream/streams*"
compile_example "esp32:esp32:esp32" "../examples/examples-audiokit/*"
compile_example "esp32:esp32:esp32" "../examples/examples-tts/streams*"
@ -40,11 +39,19 @@ compile_example "esp32:esp32:esp32" "../examples/examples-dsp/examples-mozzi/*"
compile_example "esp32:esp32:esp32" "../examples/examples-dsp/examples-pd/*"
compile_example "esp32:esp32:esp32" "../examples/examples-dsp/examples-stk/*"
compile_example "esp32:esp32:esp32" "../examples/examples-dsp/examples-faust/streams*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/a2dp/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/esp-now/codec/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/esp-now/pcm/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/esp-now/speed-test/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/ip/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/udp/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/vban/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/rtsp/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/serial/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/snapcast/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/spi/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/http-client/*"
compile_example "esp32:esp32:esp32" "../examples/examples-communication/http-server/*"
compile_example "esp32:esp32:esp32" "../examples/tests/adc/*"
compile_example "esp32:esp32:esp32" "../examples/tests/basic/*"
compile_example "esp32:esp32:esp32" "../examples/tests/codecs/*"

View File

@ -6,9 +6,9 @@
*/
#define USE_MIDI
#include "BluetoothA2DPSource.h"
#include "AudioTools.h"
#include "AudioLibs/AudioBoardStream.h"
#include "AudioTools.h" // must be first
#include "AudioLibs/AudioBoardStream.h" // https://github.com/pschatzmann/arduino-audio-driver
#include "BluetoothA2DPSource.h" // https://github.com/pschatzmann/ESP32-A2DP
BluetoothA2DPSource a2dp_source;

View File

@ -1,5 +0,0 @@
#pragma once
#define I2S_BUFFER_COUNT 8
#define I2S_BUFFER_SIZE 256
#define DEFAULT_BUFFER_SIZE 2048

View File

@ -6,7 +6,6 @@
* @copyright GPLv3
*/
#include "AudioConfigLocal.h"
#include "AudioTools.h"
@ -15,7 +14,7 @@ AudioInfo info(44100, 2, 16);
SineWaveGenerator<sound_t> sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000
GeneratedSoundStream<sound_t> sound(sineWave); // Stream generated from sine wave
SPDIFOutput out;
StreamCopy copier(out, sound); // copies sound into i2s
StreamCopy copier(out, sound, 2048); // copies sound into i2s
// Arduino Setup
void setup(void) {
@ -28,6 +27,9 @@ void setup(void) {
auto config = out.defaultConfig();
config.copyFrom(info);
config.pin_data = 23;
config.buffer_size = 384;
config.buffer_count = 8;
out.begin(config);
// Setup sine wave

View File

@ -25,9 +25,10 @@ void setup(){
Serial.begin(115200);
AudioLogger::instance().begin(Serial, AudioLogger::Info);
analog.begin(analog.defaultConfig(TX_MODE));
// begin processing
auto cfg = out.defaultConfig();
out.begin(cfg);
out.begin();
}
void loop(){

View File

@ -568,6 +568,11 @@ class EncodedAudioStream : public ReformatBaseStream {
enc_out.setOutput(&out);
}
AudioInfo defaultConfig() {
AudioInfo ai;
return ai;
}
bool begin(AudioInfo info) {
setAudioInfo(info);
return begin();
@ -601,6 +606,9 @@ class EncodedAudioStream : public ReformatBaseStream {
float getByteFactor() { return 1.0f; }
/// Defines the class specific custom log level
void setLogLevel(AudioLogger::LogLevel level) { enc_out.setLogLevel(level); }
protected:
EncodedAudioOutput enc_out;

View File

@ -76,6 +76,7 @@ enum MTSStreamType {
* @brief MPEG-TS (MTS) decoder. Extracts the AAC audio data from a MPEG-TS (MTS) data stream. You can
* define the relevant stream types via the API.
* The parsing logic was taken from: https://github.com/Yokohama-Miyazawa/M5HLSPlayer/blob/main/src/AudioGeneratorTS.cpp
* Status: experimental!
* @ingroup codecs
* @ingroup decoder
* @author Phil Schatzmann
@ -86,7 +87,7 @@ class MTSDecoder : public AudioDecoder {
public:
MTSDecoder() = default;
void begin() override {
bool begin() override {
TRACED();
// default supported stream types
@ -103,6 +104,7 @@ class MTSDecoder : public AudioDecoder {
}
is_active = true;
return true;
}
void end() override {

View File

@ -431,7 +431,7 @@ public:
// output values
T out_value = pitchShift(value);
LOGD("PitchShiftOutput %d -> %d", value, out_value);
LOGD("PitchShiftOutput %f -> %d", value, (int) out_value);
T out_array[channels];
for (int ch = 0; ch < channels; ch++) {
out_array[ch] = out_value;

View File

@ -1,6 +1,6 @@
#pragma once
#include "AudioTools/AudioStreams.h"
#include "AudioTools/BaseStream.h"
#ifdef ARDUINO
# include "FS.h"
# define READTYPE char
@ -20,7 +20,7 @@ namespace audio_tools {
* @author Phil Schatzmann
* @copyright GPLv3
*/
template <class FileType> class FileLoopT : public AudioStream {
template <class FileType> class FileLoopT : public BaseStream {
public:
FileLoopT() = default;
FileLoopT(FileType file, int count = -1, int rewindPos = -1) {
@ -30,7 +30,7 @@ public:
}
// restarts the file from the beginning
bool begin() override {
bool begin() {
TRACEI();
// automatic determination of start pos
if (start_pos <= 0){
@ -48,7 +48,7 @@ public:
}
// closes the file
void end() override {
void end() {
TRACEI();
current_file.close();
}
@ -135,6 +135,8 @@ public:
/// @return true as long as we are looping
bool isLoopActive() { return loop_count > 0 || loop_count == -1; }
size_t write(const uint8_t* data, size_t len) { return current_file.write(data, len);}
protected:
int start_pos = -1;
int loop_count = -1;

View File

@ -57,7 +57,7 @@ class PureDataStream : public AudioStream {
out_channels = p_heavy->getNumOutputChannels();
if (out_channels > 0) buffer_read.resize(buffer_size);
if (in_channels > 0) buffer_write.resize(buffer_size);
if (audioInfo() != audioInfoIn()) {
if (audioInfo() != audioInfoOut()) {
LOGW("rate: %d, channels: in=%d, out=%d", sample_rate, in_channels,
out_channels);
} else {

View File

@ -9,7 +9,7 @@
* @copyright GPLv3
*/
namespace audio_tools {
class StdioStream : public AudioStream {
class StdioStream : public BaseStream {
public:
AudioInfo defaultConfig() {
AudioInfo def;
@ -19,12 +19,7 @@ public:
return def;
}
bool begin(AudioInfo cfg) {
info = cfg;
return begin();
}
bool begin() override {
bool begin() {
is_open = true;
return true;
}
@ -46,13 +41,12 @@ public:
return ::write(1, buffer, len);
}
void end() override {
void end() {
is_open = false;
}
protected:
bool is_open = false;
FILE *out;
};
} // namespace audio_tools

View File

@ -127,7 +127,7 @@ class SPDIFOutput : public AudioStream {
/// destructor
virtual ~SPDIFOutput() { end(); }
/// Starting with default settings
/// Starting with last or default settings
bool begin() { return begin(cfg); }
/// Start with the provided parameters

View File

@ -6,28 +6,12 @@
#include "AudioTools/AudioLogger.h"
#include "AudioTools/BaseConverter.h"
#include "AudioEffects/SoundGenerator.h"
#include "AudioTools/BaseStream.h"
#ifndef IRAM_ATTR
# define IRAM_ATTR
#endif
#ifdef USE_STREAM_WRITE_OVERRIDE
# define STREAM_WRITE_OVERRIDE override
#else
# define STREAM_WRITE_OVERRIDE
#endif
#ifdef USE_STREAM_READ_OVERRIDE
# define STREAM_READ_OVERRIDE override
#else
# define STREAM_READ_OVERRIDE
#endif
#ifdef USE_STREAM_READCHAR_OVERRIDE
# define STREAM_READCHAR_OVERRIDE override
#else
# define STREAM_READCHAR_OVERRIDE
#endif
namespace audio_tools {
@ -37,15 +21,12 @@ namespace audio_tools {
* @author Phil Schatzmann
* @copyright GPLv3
*/
class AudioStream : public Stream, public AudioInfoSupport, public AudioInfoSource {
class AudioStream : public BaseStream, public AudioInfoSupport, public AudioInfoSource {
public:
AudioStream() = default;
virtual ~AudioStream() = default;
AudioStream(AudioStream const&) = delete;
AudioStream& operator=(AudioStream const&) = delete;
virtual bool begin(){return true;}
virtual void end(){}
// Call from subclass or overwrite to do something useful
virtual void setAudioInfo(AudioInfo newInfo) override {
@ -64,34 +45,17 @@ class AudioStream : public Stream, public AudioInfoSupport, public AudioInfoSour
}
virtual size_t readBytes(uint8_t *buffer, size_t length) STREAM_READ_OVERRIDE { return not_supported(0, "readBytes"); }
virtual size_t readBytes(uint8_t *buffer, size_t length) override { return not_supported(0, "readBytes"); }
virtual size_t write(const uint8_t *buffer, size_t size) override{ return not_supported(0,"write"); }
virtual size_t write(uint8_t ch) override {
tmp_out.resize(MAX_SINGLE_CHARS);
if (tmp_out.isFull()){
flush();
}
return tmp_out.write(ch);
}
virtual int available() override { return DEFAULT_BUFFER_SIZE; };
operator bool() { return available() > 0; }
operator bool() { return info && available() > 0; }
virtual AudioInfo audioInfo() override {
return info;
}
virtual int availableForWrite() override { return DEFAULT_BUFFER_SIZE; }
virtual void flush() override {
if (tmp_out.available()>0){
write((const uint8_t*)tmp_out.address(), tmp_out.available());
}
}
/// Writes len bytes of silence (=0).
virtual void writeSilence(size_t len){
@ -107,52 +71,15 @@ class AudioStream : public Stream, public AudioInfoSupport, public AudioInfoSour
return length;
}
// Methods which should be suppressed in the documentation
#ifndef DOXYGEN
virtual size_t readBytes(char *buffer, size_t length) STREAM_READCHAR_OVERRIDE {
return readBytes((uint8_t *)buffer, length);
}
virtual int read() override {
refillReadBuffer();
return tmp_in.read();
}
virtual int peek() override {
refillReadBuffer();
return tmp_in.peek();
}
#endif
protected:
AudioInfo info;
RingBuffer<uint8_t> tmp_in{0};
RingBuffer<uint8_t> tmp_out{0};
virtual int not_supported(int out, const char* msg="") {
virtual int not_supported(int out, const char *msg = "") {
LOGE("AudioStream: %s unsupported operation!", msg);
// trigger stacktrace
assert(false);
return out;
}
void refillReadBuffer() {
tmp_in.resize(MAX_SINGLE_CHARS);
if (tmp_in.isEmpty()){
TRACED();
const int len = tmp_in.size();
uint8_t bytes[len];
int len_eff = readBytes(bytes, len);
//LOGD("tmp_in available: %d / size: %d / to be written %d", tmp_in.available(), tmp_in.size(), len_eff);
tmp_in.writeArray(bytes,len_eff);
}
}
};
/**
@ -240,7 +167,7 @@ class MemoryStream : public AudioStream {
}
/// Copy Constructor
MemoryStream(MemoryStream& source) {
MemoryStream(MemoryStream& source) : AudioStream() {
copy(source);
}
@ -461,200 +388,46 @@ class MemoryStream : public AudioStream {
};
/**
* @brief MemoryStream which is written and read using the internal RAM. For each write the data is allocated
* on the heap.
* @brief An AudioStream backed by a Ringbuffer. We can write to the end and read from
* the beginning of the stream
* @ingroup io
* @author Phil Schatzmann
* @copyright GPLv3
*/
class DynamicMemoryStream : public AudioStream {
public:
struct DataNode {
size_t len=0;
uint8_t* data=nullptr;
class RingBufferStream : public AudioStream {
public:
RingBufferStream(int size = DEFAULT_BUFFER_SIZE) { resize(size); }
DataNode() = default;
/// Constructor
DataNode(void*inData, int len){
this->len = len;
this->data = (uint8_t*) malloc(len);
assert(this->data!=nullptr);
memcpy(this->data, inData, len);
}
~DataNode(){
if (data!=nullptr) {
free(data);
data = nullptr;
}
}
};
DynamicMemoryStream() = default;
DynamicMemoryStream(bool isLoop, int defaultBufferSize=DEFAULT_BUFFER_SIZE ) {
this->default_buffer_size = defaultBufferSize;
is_loop = isLoop;
}
// Assign values from ref, clearing the original ref
void assign(DynamicMemoryStream &ref){
audio_list.swap(ref.audio_list);
it = ref.it;
total_available=ref.total_available;
default_buffer_size = ref.default_buffer_size;
alloc_failed = ref.alloc_failed;;
is_loop = ref.is_loop;
ref.clear();
}
/// Intializes the processing
virtual bool begin() override {
clear();
temp_audio.resize(default_buffer_size);
return true;
}
virtual void end() override {
clear();
}
/// Automatically rewinds to the beginning when reaching the end
virtual void setLoop(bool loop){
is_loop = loop;
}
void clear() {
DataNode *p_node;
bool ok;
do{
ok = audio_list.pop_front(p_node);
if (ok){
delete p_node;
}
} while (ok);
temp_audio.reset();
total_available = 0;
alloc_failed = false;
rewind();
}
size_t size(){
return total_available;
}
/// Sets the read position to the beginning
void rewind() {
it = audio_list.begin();
}
virtual size_t write(const uint8_t *buffer, size_t size) override {
DataNode *p_node = new DataNode((void*)buffer, size);
if (p_node->data!=nullptr){
alloc_failed = false;
total_available += size;
audio_list.push_back(p_node);
// setup interator to point to first record
if (it == audio_list.end()){
it = audio_list.begin();
}
return size;
}
alloc_failed = true;
return 0;
virtual int available() override {
// LOGD("RingBufferStream::available: %zu",buffer->available());
return buffer.available();
}
virtual int availableForWrite() override {
return alloc_failed ? 0 : default_buffer_size;
}
virtual int available() override {
if (it == audio_list.end()){
if (is_loop) rewind();
if (it == audio_list.end()) {
return 0;
}
}
return (*it)->len;
return buffer.availableForWrite();
}
virtual size_t readBytes(uint8_t *buffer, size_t length) override {
// provide unprocessed data
if (temp_audio.available()>0){
return temp_audio.readArray(buffer, length);
}
virtual void flush() override {}
virtual int peek() override { return buffer.peek(); }
virtual int read() override { return buffer.read(); }
// We have no more data
if (it==audio_list.end()){
if (is_loop){
rewind();
} else {
// stop the processing
return 0;
}
}
// provide data from next node
DataNode *p_node = *it;
int result_len = min(length, (size_t) p_node->len);
memcpy(buffer, p_node->data, result_len);
// save unprocessed data to temp buffer
if (p_node->len>length){
uint8_t *start = p_node->data+result_len;
int uprocessed_len = p_node->len - length;
temp_audio.writeArray(start, uprocessed_len);
}
//move to next pos
++it;
return result_len;
virtual size_t readBytes(uint8_t *data, size_t length) override {
return buffer.readArray(data, length);
}
List<DataNode*> &list() {
return audio_list;
virtual size_t write(const uint8_t *data, size_t len) override {
// LOGD("RingBufferStream::write: %zu",len);
return buffer.writeArray(data, len);
}
/// @brief Post processing after the recording. We add a smooth transition at the beginning and at the end
/// @tparam T
/// @param factor
template<typename T>
void postProcessSmoothTransition(int channels, float factor = 0.01, int remove=0){
if (remove>0){
for (int j=0;j<remove;j++){
DataNode* node = nullptr;
audio_list.pop_front(node);
if (node!=nullptr) delete node;
node = nullptr;
audio_list.pop_back(node);
if (node!=nullptr) delete node;
}
}
virtual size_t write(uint8_t c) override { return buffer.write(c); }
// Remove popping noise
SmoothTransition<T> clean_start(channels, true, false, factor);
auto first = *list().begin();
if (first!=nullptr){
clean_start.convert(first->data,first->len);
}
void resize(int size) { buffer.resize(size); }
SmoothTransition<T> clean_end(channels, false, true, factor);
auto last = * (--(list().end()));
if (last!=nullptr){
clean_end.convert(last->data,last->len);
}
}
protected:
List<DataNode*> audio_list;
List<DataNode*>::Iterator it = audio_list.end();
size_t total_available=0;
int default_buffer_size=DEFAULT_BUFFER_SIZE;
bool alloc_failed = false;
RingBuffer<uint8_t> temp_audio{DEFAULT_BUFFER_SIZE};
bool is_loop = false;
size_t size() { return buffer.size(); }
protected:
RingBuffer<uint8_t> buffer{0};
};
/**
@ -865,200 +638,6 @@ class BufferedStream : public ModifyingStream {
}
};
/**
* @brief The Arduino Stream which provides silence and simulates a null device
* when used as audio target or audio source
* @ingroup io
* @author Phil Schatzmann
* @copyright GPLv3
*/
class NullStream : public AudioStream {
public:
bool begin(AudioInfo info) {
this->info = info;
return true;
}
AudioInfo defaultConfig() {
AudioInfo info;
return info;
}
size_t write(const uint8_t *buffer, size_t len) override{
return len;
}
size_t readBytes(uint8_t *buffer, size_t len) override{
memset(buffer,0, len);
return len;
}
void setAudioInfo(AudioInfo info) override {
this->info = info;
}
};
/**
* @brief A Stream backed by a Ringbuffer. We can write to the end and read from
* the beginning of the stream
* @ingroup io
* @author Phil Schatzmann
* @copyright GPLv3
*/
class RingBufferStream : public AudioStream {
public:
RingBufferStream(int size = DEFAULT_BUFFER_SIZE) {
resize(size);
}
virtual int available() override {
// LOGD("RingBufferStream::available: %zu",buffer->available());
return buffer.available();
}
virtual int availableForWrite() override {
return buffer.availableForWrite();
}
virtual void flush() override {}
virtual int peek() override { return buffer.peek(); }
virtual int read() override { return buffer.read(); }
virtual size_t readBytes(uint8_t *data, size_t length) override {
return buffer.readArray(data, length);
}
virtual size_t write(const uint8_t *data, size_t len) override {
// LOGD("RingBufferStream::write: %zu",len);
return buffer.writeArray(data, len);
}
virtual size_t write(uint8_t c) override { return buffer.write(c); }
void resize(int size){
buffer.resize(size);
}
size_t size() {
return buffer.size();
}
protected:
RingBuffer<uint8_t> buffer{0};
};
/**
* @brief AudioStream class which stores the data in a temporary queue buffer.
* The queue can be consumed e.g. by a callback function by calling readBytes();
* @ingroup io
* @author Phil Schatzmann
* @copyright GPLv3
*/
template <class T>
class QueueStream : public AudioStream {
public:
/// Default constructor
QueueStream(int bufferSize, int bufferCount, bool autoRemoveOldestDataIfFull=false)
: AudioStream() {
owns_buffer = true;
callback_buffer_ptr = new NBuffer<T>(bufferSize, bufferCount);
remove_oldest_data = autoRemoveOldestDataIfFull;
}
/// Create stream from any BaseBuffer subclass
QueueStream(BaseBuffer<T> &buffer){
owns_buffer = false;
callback_buffer_ptr = &buffer;
}
virtual ~QueueStream() {
if(owns_buffer) {
delete callback_buffer_ptr;
}
}
/// Activates the output
virtual bool begin() override {
TRACED();
active = true;
return true;
}
/// Activate only when filled buffer reached %
virtual bool begin(size_t activeWhenPercentFilled){
// determine total buffer size in bytes
size_t size = callback_buffer_ptr->size() * sizeof(T);
// calculate limit
active_limit = size * activeWhenPercentFilled / 100;
return true;
}
/// stops the processing
virtual void end() override {
TRACED();
active = false;
};
int available() override {
return active ? callback_buffer_ptr->available()*sizeof(T) : 0;
}
int availableForWrite() override {
return callback_buffer_ptr->availableForWrite()*sizeof(T);
}
virtual size_t write(const uint8_t *data, size_t len) override {
if (active_limit==0 && !active) return 0;
// activate automaticaly when limit has been reached
if (active_limit > 0 && !active && available() >= active_limit){
this->active = true;
}
// make space by deleting oldest entries
if (remove_oldest_data){
int available_bytes = callback_buffer_ptr->availableForWrite()*sizeof(T);
if ((int)len>available_bytes){
int gap = len-available_bytes;
uint8_t tmp[gap];
readBytes(tmp, gap);
}
}
return callback_buffer_ptr->writeArray(data, len / sizeof(T));
}
virtual size_t readBytes(uint8_t *data, size_t len) override {
if (!active) return 0;
return callback_buffer_ptr->readArray(data, len / sizeof(T));
}
/// Clears the data in the buffer
void clear() {
if (active){
callback_buffer_ptr->reset();
}
}
/// Returns true if active
operator bool(){
return active;
}
protected:
BaseBuffer<T> *callback_buffer_ptr;
size_t active_limit = 0;
bool active;
bool remove_oldest_data;
bool owns_buffer;
};
// support legacy name
template <typename T>
using CallbackBufferedStream = QueueStream<T>;
/**
* @brief Both the data of the read or write
@ -1868,11 +1447,11 @@ class CallbackStream : public ModifyingStream {
setAudioInfo(info);
return begin();
}
virtual bool begin() override {
active = true;
return true;
}
void end() override { active = false;}
int available() override {
@ -1966,105 +1545,6 @@ class CallbackStream : public ModifyingStream {
int available_bytes = -1;
};
/**
* @brief Provides data from a concatenation of Streams. Please note that the provided
* Streams can be played only once! You will need to reset them (e.g. moving the file pointer to the beginning)
* and readd them back if you want to process them a second time. The default timeout on the available() method
* is set to 0. This might be not good if you use e.g. a URLStream.
* @ingroup io
* @author Phil Schatzmann
* @copyright GPLv3
*/
class CatStream : public AudioStream {
public:
CatStream(){
_timeout = 0;
}
void add(Stream *stream){
input_streams.push_back(stream);
}
void add(Stream &stream){
input_streams.push_back(&stream);
}
bool begin() override {
is_active = true;
return AudioStream::begin();
}
void end() override {
is_active = false;
return AudioStream::end();
}
int available() override {
if (!is_active) return 0;
if (!moveToNextStreamOnEnd()){
return 0;
}
return availableWithTimout();
}
size_t readBytes(uint8_t* data, size_t len) override {
if (!is_active) return 0;
if (!moveToNextStreamOnEnd()){
return 0;
}
return p_current_stream->readBytes(data, len);
}
/// Returns true if active and we still have data
operator bool(){
return is_active && available()>0;
}
void setOnBeginCallback(void (*callback)(Stream* stream) ){
begin_callback = callback;
}
void setOnEndCallback(void (*callback)(Stream* stream) ){
end_callback = callback;
}
protected:
Vector<Stream*> input_streams;
Stream *p_current_stream = nullptr;
bool is_active = false;
void (*begin_callback)(Stream* stream) = nullptr;
void (*end_callback)(Stream* stream) = nullptr;
/// moves to the next stream if necessary: returns true if we still have a valid stream
bool moveToNextStreamOnEnd(){
// keep on running
if (p_current_stream!=nullptr && p_current_stream->available()>0) return true;
// at end?
if ((p_current_stream==nullptr || availableWithTimout()==0)){
if (end_callback && p_current_stream) end_callback(p_current_stream);
if (!input_streams.empty()) {
LOGI("using next stream");
p_current_stream = input_streams[0];
input_streams.pop_front();
if (begin_callback && p_current_stream) begin_callback(p_current_stream);
} else {
p_current_stream = nullptr;
}
}
// returns true if we have a valid stream
return p_current_stream!=nullptr;
}
int availableWithTimout(){
int result = p_current_stream->available();
if (result==0){
for (int j=0; j <_timeout/10;j++){
delay(10);
result = p_current_stream->available();
if (result!=0) break;
}
}
return result;
}
};
/**
* @brief Stream to which we can apply Filters for each channel. The filter

View File

@ -595,6 +595,11 @@ struct AppropriateSumType<int32_t> {
using type = int64_t;
};
/**
* @brief Provides reduced sampling rates through binning: typed implementation
* @ingroup convert
*/
template <typename T>
class BinT : public BaseConverter {
public:

524
src/AudioTools/BaseStream.h Normal file
View File

@ -0,0 +1,524 @@
#pragma once
#include "AudioTools/Buffers.h"
#ifdef ARDUINO
#include "Stream.h"
#endif
#ifdef USE_STREAM_WRITE_OVERRIDE
#define STREAM_WRITE_OVERRIDE override
#else
#define STREAM_WRITE_OVERRIDE
#endif
#ifdef USE_STREAM_READ_OVERRIDE
#define STREAM_READ_OVERRIDE override
#else
#define STREAM_READ_OVERRIDE
#endif
#ifdef USE_STREAM_READCHAR_OVERRIDE
#define STREAM_READCHAR_OVERRIDE override
#else
#define STREAM_READCHAR_OVERRIDE
#endif
namespace audio_tools {
/**
* @brief Base class for all Streams. It relies on write(const uint8_t *buffer,
* size_t size) and readBytes(uint8_t *buffer, size_t length).
* @author Phil Schatzmann
* @copyright GPLv3
*/
class BaseStream : public Stream {
public:
BaseStream() = default;
virtual ~BaseStream() = default;
BaseStream(BaseStream const &) = delete;
BaseStream &operator=(BaseStream const &) = delete;
virtual bool begin(){return true;}
virtual void end(){}
virtual size_t readBytes(uint8_t *buffer,
size_t length) STREAM_READ_OVERRIDE = 0;
virtual size_t write(const uint8_t *buffer, size_t size) override = 0;
virtual size_t write(uint8_t ch) override {
tmp_out.resize(MAX_SINGLE_CHARS);
if (tmp_out.isFull()) {
flush();
}
return tmp_out.write(ch);
}
virtual int available() override { return DEFAULT_BUFFER_SIZE; };
virtual int availableForWrite() override { return DEFAULT_BUFFER_SIZE; }
virtual void flush() override {
if (tmp_out.available() > 0) {
write((const uint8_t *)tmp_out.address(), tmp_out.available());
}
}
// Methods which should be suppressed in the documentation
#ifndef DOXYGEN
virtual size_t readBytes(char *buffer,
size_t length) STREAM_READCHAR_OVERRIDE {
return readBytes((uint8_t *)buffer, length);
}
virtual int read() override {
refillReadBuffer();
return tmp_in.read();
}
virtual int peek() override {
refillReadBuffer();
return tmp_in.peek();
}
#endif
protected:
RingBuffer<uint8_t> tmp_in{0};
RingBuffer<uint8_t> tmp_out{0};
void refillReadBuffer() {
tmp_in.resize(MAX_SINGLE_CHARS);
if (tmp_in.isEmpty()) {
TRACED();
const int len = tmp_in.size();
uint8_t bytes[len];
int len_eff = readBytes(bytes, len);
// LOGD("tmp_in available: %d / size: %d / to be written %d",
// tmp_in.available(), tmp_in.size(), len_eff);
tmp_in.writeArray(bytes, len_eff);
}
}
};
/**
* @brief Provides data from a concatenation of Streams. Please note that the
* provided Streams can be played only once! You will need to reset them (e.g.
* moving the file pointer to the beginning) and readd them back if you want to
* process them a second time. The default timeout on the available() method is
* set to 0. This might be not good if you use e.g. a URLStream.
* @ingroup io
* @author Phil Schatzmann
* @copyright GPLv3
*/
class CatStream : public BaseStream {
public:
CatStream() = default;
void add(Stream *stream) { input_streams.push_back(stream); }
void add(Stream &stream) { input_streams.push_back(&stream); }
bool begin() {
is_active = true;
return true;
}
void end() { is_active = false; }
int available() override {
if (!is_active) return 0;
if (!moveToNextStreamOnEnd()) {
return 0;
}
return availableWithTimout();
}
size_t readBytes(uint8_t *data, size_t len) override {
if (!is_active) return 0;
if (!moveToNextStreamOnEnd()) {
return 0;
}
return p_current_stream->readBytes(data, len);
}
/// Returns true if active and we still have data
operator bool() { return is_active && available() > 0; }
void setOnBeginCallback(void (*callback)(Stream *stream)) {
begin_callback = callback;
}
void setOnEndCallback(void (*callback)(Stream *stream)) {
end_callback = callback;
}
void setTimeout(uint32_t t) { _timeout = t; }
protected:
Vector<Stream *> input_streams;
Stream *p_current_stream = nullptr;
bool is_active = false;
void (*begin_callback)(Stream *stream) = nullptr;
void (*end_callback)(Stream *stream) = nullptr;
uint_fast32_t _timeout = 0;
/// moves to the next stream if necessary: returns true if we still have a
/// valid stream
bool moveToNextStreamOnEnd() {
// keep on running
if (p_current_stream != nullptr && p_current_stream->available() > 0)
return true;
// at end?
if ((p_current_stream == nullptr || availableWithTimout() == 0)) {
if (end_callback && p_current_stream) end_callback(p_current_stream);
if (!input_streams.empty()) {
LOGI("using next stream");
p_current_stream = input_streams[0];
input_streams.pop_front();
if (begin_callback && p_current_stream)
begin_callback(p_current_stream);
} else {
p_current_stream = nullptr;
}
}
// returns true if we have a valid stream
return p_current_stream != nullptr;
}
int availableWithTimout() {
int result = p_current_stream->available();
if (result == 0) {
for (int j = 0; j < _timeout / 10; j++) {
delay(10);
result = p_current_stream->available();
if (result != 0) break;
}
}
return result;
}
};
/**
* @brief The Arduino Stream which provides silence and simulates a null device
* when used as audio target or audio source
* @ingroup io
* @author Phil Schatzmann
* @copyright GPLv3
*/
class NullStream : public BaseStream {
public:
size_t write(const uint8_t *buffer, size_t len) override { return len; }
size_t readBytes(uint8_t *buffer, size_t len) override {
memset(buffer, 0, len);
return len;
}
};
/**
* @brief Stream class which stores the data in a temporary queue buffer.
* The queue can be consumed e.g. by a callback function by calling readBytes();
* @ingroup io
* @author Phil Schatzmann
* @copyright GPLv3
*/
template <class T>
class QueueStream : public BaseStream {
public:
/// Default constructor
QueueStream(int bufferSize, int bufferCount,
bool autoRemoveOldestDataIfFull = false) {
owns_buffer = true;
callback_buffer_ptr = new NBuffer<T>(bufferSize, bufferCount);
remove_oldest_data = autoRemoveOldestDataIfFull;
}
/// Create stream from any BaseBuffer subclass
QueueStream(BaseBuffer<T> &buffer) {
owns_buffer = false;
callback_buffer_ptr = &buffer;
}
virtual ~QueueStream() {
if (owns_buffer) {
delete callback_buffer_ptr;
}
}
/// Activates the output
virtual bool begin() {
TRACED();
active = true;
return true;
}
/// Activate only when filled buffer reached %
virtual bool begin(size_t activeWhenPercentFilled) {
// determine total buffer size in bytes
size_t size = callback_buffer_ptr->size() * sizeof(T);
// calculate limit
active_limit = size * activeWhenPercentFilled / 100;
return true;
}
/// stops the processing
virtual void end() {
TRACED();
active = false;
};
int available() override {
return active ? callback_buffer_ptr->available() * sizeof(T) : 0;
}
int availableForWrite() override {
return callback_buffer_ptr->availableForWrite() * sizeof(T);
}
virtual size_t write(const uint8_t *data, size_t len) override {
if (active_limit == 0 && !active) return 0;
// activate automaticaly when limit has been reached
if (active_limit > 0 && !active && available() >= active_limit) {
this->active = true;
}
// make space by deleting oldest entries
if (remove_oldest_data) {
int available_bytes =
callback_buffer_ptr->availableForWrite() * sizeof(T);
if ((int)len > available_bytes) {
int gap = len - available_bytes;
uint8_t tmp[gap];
readBytes(tmp, gap);
}
}
return callback_buffer_ptr->writeArray(data, len / sizeof(T));
}
virtual size_t readBytes(uint8_t *data, size_t len) override {
if (!active) return 0;
return callback_buffer_ptr->readArray(data, len / sizeof(T));
}
/// Clears the data in the buffer
void clear() {
if (active) {
callback_buffer_ptr->reset();
}
}
/// Returns true if active
operator bool() { return active; }
protected:
BaseBuffer<T> *callback_buffer_ptr;
size_t active_limit = 0;
bool active;
bool remove_oldest_data;
bool owns_buffer;
};
// support legacy name
template <typename T>
using CallbackBufferedStream = QueueStream<T>;
/**
* @brief MemoryStream which is written and read using the internal RAM. For each write the data is allocated
* on the heap.
* @ingroup io
* @author Phil Schatzmann
* @copyright GPLv3
*/
class DynamicMemoryStream : public BaseStream {
public:
struct DataNode {
size_t len=0;
uint8_t* data=nullptr;
DataNode() = default;
/// Constructor
DataNode(void*inData, int len){
this->len = len;
this->data = (uint8_t*) malloc(len);
assert(this->data!=nullptr);
memcpy(this->data, inData, len);
}
~DataNode(){
if (data!=nullptr) {
free(data);
data = nullptr;
}
}
};
DynamicMemoryStream() = default;
DynamicMemoryStream(bool isLoop, int defaultBufferSize=DEFAULT_BUFFER_SIZE ) {
this->default_buffer_size = defaultBufferSize;
is_loop = isLoop;
}
// Assign values from ref, clearing the original ref
void assign(DynamicMemoryStream &ref){
audio_list.swap(ref.audio_list);
it = ref.it;
total_available=ref.total_available;
default_buffer_size = ref.default_buffer_size;
alloc_failed = ref.alloc_failed;;
is_loop = ref.is_loop;
ref.clear();
}
/// Intializes the processing
virtual bool begin() {
clear();
temp_audio.resize(default_buffer_size);
return true;
}
virtual void end() {
clear();
}
/// Automatically rewinds to the beginning when reaching the end
virtual void setLoop(bool loop){
is_loop = loop;
}
void clear() {
DataNode *p_node;
bool ok;
do{
ok = audio_list.pop_front(p_node);
if (ok){
delete p_node;
}
} while (ok);
temp_audio.reset();
total_available = 0;
alloc_failed = false;
rewind();
}
size_t size(){
return total_available;
}
/// Sets the read position to the beginning
void rewind() {
it = audio_list.begin();
}
virtual size_t write(const uint8_t *buffer, size_t size) override {
DataNode *p_node = new DataNode((void*)buffer, size);
if (p_node->data!=nullptr){
alloc_failed = false;
total_available += size;
audio_list.push_back(p_node);
// setup interator to point to first record
if (it == audio_list.end()){
it = audio_list.begin();
}
return size;
}
alloc_failed = true;
return 0;
}
virtual int availableForWrite() override {
return alloc_failed ? 0 : default_buffer_size;
}
virtual int available() override {
if (it == audio_list.end()){
if (is_loop) rewind();
if (it == audio_list.end()) {
return 0;
}
}
return (*it)->len;
}
virtual size_t readBytes(uint8_t *buffer, size_t length) override {
// provide unprocessed data
if (temp_audio.available()>0){
return temp_audio.readArray(buffer, length);
}
// We have no more data
if (it==audio_list.end()){
if (is_loop){
rewind();
} else {
// stop the processing
return 0;
}
}
// provide data from next node
DataNode *p_node = *it;
int result_len = min(length, (size_t) p_node->len);
memcpy(buffer, p_node->data, result_len);
// save unprocessed data to temp buffer
if (p_node->len>length){
uint8_t *start = p_node->data+result_len;
int uprocessed_len = p_node->len - length;
temp_audio.writeArray(start, uprocessed_len);
}
//move to next pos
++it;
return result_len;
}
List<DataNode*> &list() {
return audio_list;
}
/// @brief Post processing after the recording. We add a smooth transition at the beginning and at the end
/// @tparam T
/// @param factor
template<typename T>
void postProcessSmoothTransition(int channels, float factor = 0.01, int remove=0){
if (remove>0){
for (int j=0;j<remove;j++){
DataNode* node = nullptr;
audio_list.pop_front(node);
if (node!=nullptr) delete node;
node = nullptr;
audio_list.pop_back(node);
if (node!=nullptr) delete node;
}
}
// Remove popping noise
SmoothTransition<T> clean_start(channels, true, false, factor);
auto first = *list().begin();
if (first!=nullptr){
clean_start.convert(first->data,first->len);
}
SmoothTransition<T> clean_end(channels, false, true, factor);
auto last = * (--(list().end()));
if (last!=nullptr){
clean_end.convert(last->data,last->len);
}
}
protected:
List<DataNode*> audio_list;
List<DataNode*>::Iterator it = audio_list.end();
size_t total_available=0;
int default_buffer_size=DEFAULT_BUFFER_SIZE;
bool alloc_failed = false;
RingBuffer<uint8_t> temp_audio{DEFAULT_BUFFER_SIZE};
bool is_loop = false;
};
} // namespace audio_tools

View File

@ -2,9 +2,9 @@
#include <WiFiUdp.h>
#include <esp_now.h>
#include "AudioTools/AudioStreams.h"
#include "AudioTools/Buffers.h"
#include "AudioTools/BaseStream.h"
#include "AudioBasic/Str.h"
#include "AudioTools/Buffers.h"
namespace audio_tools {
@ -70,7 +70,7 @@ struct ESPNowStreamConfig {
* @author Phil Schatzmann
* @copyright GPLv3
*/
class ESPNowStream : public AudioStream {
class ESPNowStream : public BaseStream {
public:
ESPNowStream() { ESPNowStreamSelf = this; };

View File

@ -1,6 +1,6 @@
#pragma once
#include "AudioConfig.h"
#include "AudioTools/AudioStreams.h"
#include "AudioTools/BaseStream.h"
#include <stdio.h>
#include <stdlib.h>
@ -27,7 +27,7 @@ namespace audio_tools {
*/
template <int bytecount, class block_t>
class HammingFEC : public Stream {
class HammingFEC : public BaseStream {
public:
HammingFEC(Stream &stream){
p_stream = &stream;
@ -72,21 +72,6 @@ class HammingFEC : public Stream {
return raw.readArray(data, len);
}
/// To be avoided
size_t write(uint8_t c) override {
return write(&c, 1);
}
/// To be avoided
int read() override {
uint8_t ch;
size_t len = readBytes(&ch, 1);
return len == 1 ? ch : -1;
}
/// To be avoided
int peek() override {return -1;}
protected:
SingleBuffer<uint8_t> raw{bytecount};
SingleBuffer<uint8_t> encoded{encodedSize()};

View File

@ -0,0 +1,73 @@
#pragma once
#include "AudioTools/BaseStream.h"
#include "RHGenericDriver.h"
namespace audio_tools {
/**
* @brief Arduino Stream which is using the RadioHead library to send and
* receive data. We use the river API directly.
* @ingroup communications
* @author Phil Schatzmann
* @copyright GPLv3
*/
class ReadioHeadStream : public Stream {
public:
ReadioHeadStream(RHGenericDriver &driver) { setDriver(driver); }
void setDriver(RHGenericDriver &driver) { p_driver = &driver; }
bool begin() {
if (p_driver == nullptr) return false;
p_driver->setMode(mode == RX_MODE ? RHGenericDriver::RHMode::RHModeRx
: RHGenericDriver::RHMode::RHModeTx);
return p_driver->init();
}
void end() { p_driver->setMode(RHGenericDriver::RHMode::RHModeSleep); }
int available() override {
if (mode == TX_MODE) return 0;
return p_driver->available() ? p_driver->maxMessageLength() : 0;
}
size_t readBytes(uint8_t *data, size_t len) override {
if (mode == TX_MODE) return 0;
int open = len;
int processed = 0;
while (open > 0) {
uint8_t av = available();
if (av == 0) break;
p_driver->recv(data + processed, &av);
open -= av;
processed += av;
}
return processed;
}
int availableForWrite() override {
if (mode == RX_MODE) return 0;
return p_driver->maxMessageLength();
}
size_t write(const uint8_t *data, size_t len) override {
if (mode == RX_MODE) return 0;
int open = len;
int processed = 0;
while (open > 0) {
int av = available();
if (av == 0) break;
p_driver->send(data + processed, av);
open -= av;
processed += av;
}
return processed;
}
protected:
RHGenericDriver *p_driver = nullptr;
RxTxMode mode;
};
} // namespace audio_tools

View File

@ -1,7 +1,7 @@
#pragma once
#include "AudioConfig.h"
#include "AudioTools/AudioStreams.h"
#include "AudioTools/BaseStream.h"
#include "FEC/ReedSolomon/rs.hpp"
namespace audio_tools {
@ -14,7 +14,7 @@ namespace audio_tools {
* @copyright GPLv3
**/
template <int bytecount, int additional_bytes>
class ReedSolomonFEC : public Stream {
class ReedSolomonFEC : public BaseStream {
public:
ReedSolomonFEC(Stream &stream){
@ -57,20 +57,6 @@ class ReedSolomonFEC : public Stream {
return encoded.readArray(data, len);
}
/// To be avoided
size_t write(uint8_t c) override {
return write(&c, 1);
}
/// To be avoided
int read() override {
uint8_t ch;
size_t len = readBytes(&ch, 1);
return len == 1 ? ch : -1;
}
/// To be avoided
int peek() override {return -1;}
protected:
SingleBuffer<uint8_t> raw{bytecount};

View File

@ -3,7 +3,7 @@
#include <esp_now.h>
#include "AudioBasic/Str.h"
#include "AudioTools/AudioStreams.h"
#include "AudioTools/BaseStream.h"
#include "AudioTools/Buffers.h"
namespace audio_tools {
@ -18,7 +18,7 @@ namespace audio_tools {
* @copyright GPLv3
*/
class UDPStream : public Stream {
class UDPStream : public BaseStream {
public:
/// Default Constructor
UDPStream() = default;
@ -118,10 +118,6 @@ public:
return bytes_read;
}
virtual size_t write(uint8_t) { return 0; }
virtual int read() { return -1; }
virtual int peek() { return -1; }
void setSSID(const char *ssid) { this->ssid = ssid; }
void setPassword(const char *pwd) { this->password = pwd; }

View File

@ -78,9 +78,11 @@ class Task {
return ref;
}
#ifdef ESP32
int getCoreID() {
return xPortGetCoreID();
}
#endif
protected:
TaskHandle_t xHandle = nullptr;