diff --git a/README.md b/README.md index 9a36ff9010..7bf440aba2 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,10 @@ Some basic C++ classes that can be used for Audio Processing privided as Arduino - AudioOutputWithCallback class to provide callback integration with ESP8266Audio This functionality provides the glue which makes different audio processing components and libraries work together. -We also provide plenty of examples that demonstrate how to implement the different scenarios. +We also provide plenty of examples that demonstrate how to implement the different scenarios. + +The design philosophy is based on the Arduino conventions: we use the ```begin()``` and ```end()``` methods to start and stop the processing and we propagate the use of Streams. + ## Optional Libraries diff --git a/examples/file_mp3-a2dp/file_mp3-a2dp.ino b/examples/file_mp3-a2dp/file_mp3-a2dp.ino index b0502165cb..256101a8e0 100644 --- a/examples/file_mp3-a2dp/file_mp3-a2dp.ino +++ b/examples/file_mp3-a2dp/file_mp3-a2dp.ino @@ -11,7 +11,7 @@ #include #include "AudioFileSourceSD.h" #include "AudioGeneratorMP3.h" -#include "AudioOutputWithCallback.h" +#include "ESP8266AudioSupport.h" #include "BluetoothA2DPSource.h" #include "AudioTools.h" diff --git a/sandbox/file_raw-I2S_external_dac/README.md b/sandbox/file_raw-I2S_external_dac/README.md new file mode 100644 index 0000000000..e820878bff --- /dev/null +++ b/sandbox/file_raw-I2S_external_dac/README.md @@ -0,0 +1,40 @@ +# Stream SD File to I2S external DAC + +We are reading a raw audio file from the SD card and write the data to the I2S interface. The audio file must be available using 16 bit integers with 2 channels. + +[Audacity](https://www.audacityteam.org/) might help you out here: export with the file name audio.raw as RAW signed 16 bit PCM and copy it to the SD card. In my example I was using the file [audio.raw](https://pschatzmann.github.io/arduino-audio-tools/resources/audio.raw). + +### SD Pins: + +The SD module is connected with the help of the SPI bus + +![sd](https://pschatzmann.github.io/arduino-audio-tools/resources/sd-module.jpeg) + +We connect the SD to the ESP32: + +| SD | ESP32 +|---------|--------------- +| VCC | 5V +| GND | GND +| CS | CS GP5 +| SCK | SCK GP18 +| MOSI | MOSI GP23 +| MISO | MISO GP19 + + +### External DAC: + +| DAC | ESP32 +| --------| --------------- +| VDD | 5V +| GND | GND +| SD | OUT (GPIO22) +| L/R | GND +| WS | WS (GPIO15) +| SCK | BCK (GPIO14) + + + + + + diff --git a/sandbox/file_raw-I2S_external_dac/file_raw-I2S_external_dac.ino b/sandbox/file_raw-I2S_external_dac/file_raw-I2S_external_dac.ino new file mode 100644 index 0000000000..49d2b5284e --- /dev/null +++ b/sandbox/file_raw-I2S_external_dac/file_raw-I2S_external_dac.ino @@ -0,0 +1,46 @@ +/** + * @file file_raw-external_dac.ino + * @author Phil Schatzmann + * @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/file_raw-external_dac/README.md + * + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +#include "AudioTools.h" +#include +#include + +using namespace audio_tools; + +File sound_file; +I2S i2s; +I2SStream i2s_stream(i2s); +StreamCopy streamCopy(i2s_stream, sound_file, 1024); +const char* file_name = "/audio.raw"; +const int sd_ss_pin = 5; + + +// Arduino Setup +void setup(void) { + Serial.begin(115200); + + // Setup SD and open file + SD.begin(sd_ss_pin); + sound_file = SD.open(file_name, FILE_READ); + + // start I2S with external DAC + Serial.println("starting I2S..."); + i2s.begin(i2s.defaultConfig(TX_MODE)); +} + +// Arduino loop - repeated processing +void loop() { + if (streamCopy.copy()){ + Serial.print("."); + } else { + Serial.println(); + Serial.println("Copy ended"); + delay(10000); + } +} \ No newline at end of file diff --git a/sandbox/file_raw-I2S_internal_dac/README.md b/sandbox/file_raw-I2S_internal_dac/README.md new file mode 100644 index 0000000000..144f1a7236 --- /dev/null +++ b/sandbox/file_raw-I2S_internal_dac/README.md @@ -0,0 +1,41 @@ +# Stream SD File to I2S internal DAC + +We are reading a raw audio file from the SD card and write the data to the analog pins of the ESP32 using the I2S interface. The audio file must be available using 16 bit integers with 2 channels. + +[Audacity](https://www.audacityteam.org/) might help you out here: export with the file name audio.raw as RAW signed 16 bit PCM and copy it to the SD card. In my example I was using the file [audio.raw](https://pschatzmann.github.io/arduino-audio-tools/resources/audio.raw). + +### SD Pins: + +The SD module is connected with the help of the SPI bus + +![sd](https://pschatzmann.github.io/arduino-audio-tools/resources/sd-module.jpeg) + +We connect the SD to the ESP32: + +| SD | ESP32 +|---------|--------------- +| VCC | 5V +| GND | GND +| CS | CS GP5 +| SCK | SCK GP18 +| MOSI | MOSI GP23 +| MISO | MISO GP19 + + +### Amplifier Pins: + +To hear the sound we connect the ESP32 to an amplifier module: The analog output is available on GPIO25 & GPIO26. You could also use some earphones. + + +| Amp | ESP32 +|---------|--------------- +| + | 5V +| - | GND +| L | GPIO25 +| R | GPIO26 +| T | GND + + + + + diff --git a/sandbox/file_raw-I2S_internal_dac/file_raw-I2S_internal_dac.ino b/sandbox/file_raw-I2S_internal_dac/file_raw-I2S_internal_dac.ino new file mode 100644 index 0000000000..c5c548876d --- /dev/null +++ b/sandbox/file_raw-I2S_internal_dac/file_raw-I2S_internal_dac.ino @@ -0,0 +1,48 @@ +/** + * @file file_raw-internal_dac.ino + * @author Phil Schatzmann + * @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/file_raw-internal_dac/README.md + * + * @author Phil Schatzmann + * @copyright GPLv3 + */ + +#include "AudioTools.h" +#include +#include + +using namespace audio_tools; + +File sound_file; +I2S i2s; +I2SStream i2s_stream(i2s); +StreamCopy streamCopy(i2s_stream, sound_file, 1024); +const char* file_name = "/audio.raw"; +const int sd_ss_pin = 5; + + +// Arduino Setup +void setup(void) { + Serial.begin(115200); + + // Setup SD and open file + SD.begin(sd_ss_pin); + sound_file = SD.open(file_name, FILE_READ); + + // start I2S with internal DAC -> GPIO25 & GPIO26 + Serial.println("starting I2S..."); + I2SConfig config = i2s.defaultConfig(TX_MODE); + config.i2s.mode = static_cast( I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN); + i2s.begin(config); +} + +// Arduino loop - repeated processing +void loop() { + if (streamCopy.copy()){ + Serial.print("."); + } else { + Serial.println(); + Serial.println("Copy ended"); + delay(10000); + } +} \ No newline at end of file diff --git a/sandbox/flash_midi-a2dp/flash_midi-a2dp.ino b/sandbox/flash_midi-a2dp/flash_midi-a2dp.ino index 5871aa328f..fbd647ae58 100644 --- a/sandbox/flash_midi-a2dp/flash_midi-a2dp.ino +++ b/sandbox/flash_midi-a2dp/flash_midi-a2dp.ino @@ -9,7 +9,7 @@ #include #include "AudioGeneratorMIDI.h" -#include "AudioOutputWithCallback.h" +#include "ESP8266AudioSupport.h" #include "BluetoothA2DPSource.h" #include "AudioTools.h" #include "Undertale_Megalovania.h" // midi diff --git a/src/AudioTools/AudioLogger.h b/src/AudioTools/AudioLogger.h index b3d92d413a..8d2ba41755 100644 --- a/src/AudioTools/AudioLogger.h +++ b/src/AudioTools/AudioLogger.h @@ -36,6 +36,12 @@ class AudioLogger { void begin(Stream& out, LogLevel level=SOUND_LOG_LEVEL) { this->log_stream_ptr = &out; this->log_level = level; + this->active = true; + } + + /// stops the logger + void end(){ + this->active = false; } /// checks if the logging is active @@ -66,7 +72,7 @@ class AudioLogger { /// printf support int printf(LogLevel current_level, const char* fmt, ...) const { int len = 0; - if (log_stream_ptr!=nullptr && current_level >= log_level){ + if (this->active && log_stream_ptr!=nullptr && current_level >= log_level){ char serial_printf_buffer[PRINTF_BUFFER_SIZE] = {0}; va_list args; va_start(args,fmt); @@ -80,7 +86,7 @@ class AudioLogger { /// write an message to the log void log(LogLevel current_level, const char *str, const char* str1=nullptr, const char* str2=nullptr) const { - if (log_stream_ptr!=nullptr){ + if (this->active && log_stream_ptr!=nullptr){ if (current_level >= log_level){ log_stream_ptr->print((char*)str); if (str1!=nullptr){ @@ -109,7 +115,8 @@ class AudioLogger { protected: Stream *log_stream_ptr; - LogLevel log_level; + LogLevel log_level; + bool active; AudioLogger(){ diff --git a/src/AudioTools/Streams.h b/src/AudioTools/Streams.h index 899bde7da3..e526de5552 100644 --- a/src/AudioTools/Streams.h +++ b/src/AudioTools/Streams.h @@ -93,7 +93,7 @@ class MemoryStream : public Stream { */ class StreamCopy { public: - StreamCopy(Stream &from, Stream &to, int buffer_size){ + StreamCopy(Stream &to, Stream &from, int buffer_size){ this->from = &from; this->to = &to; this->buffer_size = buffer_size; @@ -129,6 +129,8 @@ class StreamCopy { }; + + #ifdef ESP32 /** * @brief Represents the content of a URL as Stream. We use the ESP32 ESP HTTP Client API diff --git a/src/AudioWAV.h b/src/AudioWAV.h index 8a0cb60113..e57640bc48 100644 --- a/src/AudioWAV.h +++ b/src/AudioWAV.h @@ -149,14 +149,6 @@ class WAVHeader { size_t data_pos = 0; size_t sound_pos = 0; - void logInfo(){ - WAVLogger.printf(AudioLogger::Info,"WAVHeader sound_pos: ", sound_pos); - WAVLogger.printf(AudioLogger::Info,"WAVHeader channels: ", headerInfo.channels); - WAVLogger.printf(AudioLogger::Info,"WAVHeader bits_per_sample: ", headerInfo.bits_per_sample); - WAVLogger.printf(AudioLogger::Info,"WAVHeader sample_rate: ", headerInfo.sample_rate); - WAVLogger.printf(AudioLogger::Info,"WAVHeader format: ", headerInfo.format); - } - uint32_t read_tag() { uint32_t tag = 0; tag = (tag << 8) | getChar(); @@ -210,6 +202,15 @@ class WAVHeader { bool eof() { return data_pos>=len-1; } + + void logInfo(){ + WAVLogger.printf(AudioLogger::Info,"WAVHeader sound_pos: ", sound_pos); + WAVLogger.printf(AudioLogger::Info,"WAVHeader channels: ", headerInfo.channels); + WAVLogger.printf(AudioLogger::Info,"WAVHeader bits_per_sample: ", headerInfo.bits_per_sample); + WAVLogger.printf(AudioLogger::Info,"WAVHeader sample_rate: ", headerInfo.sample_rate); + WAVLogger.printf(AudioLogger::Info,"WAVHeader format: ", headerInfo.format); + } + }; AudioBaseInfoDependent AudioBaseInfoDependentNone; diff --git a/src/AudioOutputWithCallback.h b/src/ESP8266AudioSupport.h similarity index 66% rename from src/AudioOutputWithCallback.h rename to src/ESP8266AudioSupport.h index 1be0f20c8a..efa28bc389 100644 --- a/src/AudioOutputWithCallback.h +++ b/src/ESP8266AudioSupport.h @@ -1,4 +1,3 @@ - #pragma once #include "AudioOutput.h" @@ -13,9 +12,9 @@ namespace audio_tools { * @author Phil Schatzmann * @copyright GPLv3 */ -class AudioOutputWithCallback : public AudioOutput -{ +class AudioOutputWithCallback : public AudioOutput { public: + // Default constructor AudioOutputWithCallback(int bufferSize, int bufferCount ){ buffer_ptr = new NBuffer(bufferSize, bufferCount); } @@ -54,4 +53,32 @@ class AudioOutputWithCallback : public AudioOutput bool active; }; +/** + * @brief ESP8266Audio Output to Stream + * + */ +class SerialOutputStream(): public AudioOutput { + public: + SerialOutputStream(Stream &out){ + this->out = out; + } + + virtual bool begin() { + active = start; + } + + virtual bool ConsumeSample(int16_t sample[2]){ + if (active) out.write((uint8_t*)sample, const_exptr(2*sizeof(int16_t))); + } + + virtual bool stop() { + active = false; + } + + protected: + const Stream &out; + bool active; +}; + } +