mirror of
https://github.com/pschatzmann/arduino-audio-tools.git
synced 2024-09-21 02:17:31 +00:00
Introduce BaseStream class
This commit is contained in:
parent
cb5b33231f
commit
727985d94f
@ -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
|
||||
|
@ -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/*"
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define I2S_BUFFER_COUNT 8
|
||||
#define I2S_BUFFER_SIZE 256
|
||||
#define DEFAULT_BUFFER_SIZE 2048
|
@ -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
|
||||
|
@ -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(){
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
524
src/AudioTools/BaseStream.h
Normal 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
|
@ -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; };
|
||||
|
||||
|
@ -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()};
|
||||
|
73
src/Communication/RadioHeadStream.h
Normal file
73
src/Communication/RadioHeadStream.h
Normal 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
|
@ -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};
|
||||
|
@ -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; }
|
||||
|
@ -78,9 +78,11 @@ class Task {
|
||||
return ref;
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
int getCoreID() {
|
||||
return xPortGetCoreID();
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
TaskHandle_t xHandle = nullptr;
|
||||
|
Loading…
Reference in New Issue
Block a user