From aa0c1d1890d797204602f6ad4dedf22e07a8d782 Mon Sep 17 00:00:00 2001 From: Roman Lut <11955117+RomanLut@users.noreply.github.com> Date: Sat, 7 May 2022 00:36:47 +0300 Subject: [PATCH 1/5] update PDM for ESP32 documentation --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 229d45fa..274fc9ec 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ AudioGeneratorRTTTL: Enjoy the pleasures of monophonic, 4-octave ringtones on y ## AudioOutput classes AudioOutput: Base class for all output drivers. Takes a sample at a time and returns true/false if there is buffer space for it. If it returns false, it is the calling object's (AudioGenerator's) job to keep the data that didn't fit and try again later. -AudioOutputI2S: Interface for any I2S 16-bit DAC. Sends stereo or mono signals out at whatever frequency set. Tested with Adafruit's I2SDAC and a Beyond9032 DAC from eBay. Tested up to 44.1KHz. To use the internal DAC on ESP32, instantiate this class as `AudioOutputI2S(0,1)`, see example `PlayMODFromPROGMEMToDAC` and code in [AudioOutputI2S.cpp](src/AudioOutputI2S.cpp#L29) for details. +AudioOutputI2S: Interface for any I2S 16-bit DAC. Sends stereo or mono signals out at whatever frequency set. Tested with Adafruit's I2SDAC and a Beyond9032 DAC from eBay. Tested up to 44.1KHz. To use the internal DAC on ESP32, instantiate this class as `AudioOutputI2S(0,AudioOutputI2S::INTERNAL_DAC)`, see example `PlayMODFromPROGMEMToDAC` and code in [AudioOutputI2S.cpp](src/AudioOutputI2S.cpp#L29) for details. To use the hardware Pulse Density Modulation (PDM) on ESP32, instantiate this class as `AudioOutputI2S(0,AudioOutputI2S::INTERNAL_PDM)`. For both later cases, default output pins are 25 and 26. AudioOutputI2SNoDAC: Abuses the I2S interface to play music without a DAC. Turns it into a 32x (or higher) oversampling delta-sigma DAC. Use the schematic below to drive a speaker or headphone from the I2STx pin (i.e. Rx). Note that with this interface, depending on the transistor used, you may need to disconnect the Rx pin from the driver to perform serial uploads. Mono-only output, of course. @@ -199,7 +199,8 @@ Use the `AudioOutputI2S*No*DAC` object instead of the `AudioOutputI2S` in your c ESP8266-GND ------------------+ | +------+ K| | | | E| ESP8266-I2SOUT (Rx) -----/\/\/\--+ | \ R| - | +-| +or ESP32 DOUT pin | +-| + | USB 5V -----------------------------+ You may also want to add a 220uF cap from USB5V to GND just to help filter out any voltage droop during high volume playback. @@ -214,10 +215,17 @@ USB-5V -- Speaker + Terminal 2N3904-Collector -- Speaker - Terminal ``` +For ESP32, default output pin is GPIO22. Note that GPIO25 ang GPIO26 are occupied by wclk/bclk and can not be used. + *NOTE*: A prior version of this schematic had a direct connection from the ESP8266 to the base of the transistor. While this does provide the maximum amplitude, it also can draw more current from the 8266 than is safe, and can also cause the transistor to overheat. As of the latest ESP8266Audio release, with the software delta-sigma DAC the LRCLK and BCLK pins *can* be used by an application. Simply use normal `pinMode` and `digitalWrite` or `digitalRead` as desired. +### Hardware PDM on ESP32 + +Hardware PDM outputs 128 * 48Khz pulses regardles of samplerate. +It seems that currently hardware PDM either does not output constant One at maximum sample level, or does not output 3.3V voltage at pulse ( unfortunatelly I not have oscilloscope to test currently) - sound is not as loud as desired. You may consider using software delta-sigma DAC instead. + ### High pitched buzzing with the 1-T circuit The 1-T amp can _NOT_ drive any sort of amplified speaker. If there is a power or USB input to the speaker, or it has lights or Bluetooth or a battery, it can _NOT_ be used with this circuit. From 428fc5f72bda3d91ff20efb2c66d08ba88ac59d1 Mon Sep 17 00:00:00 2001 From: Roman Lut <11955117+RomanLut@users.noreply.github.com> Date: Sat, 7 May 2022 00:37:57 +0300 Subject: [PATCH 2/5] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 274fc9ec..b83f2ebe 100644 --- a/README.md +++ b/README.md @@ -213,9 +213,9 @@ ESP8266-RX(I2S tx) -- Resistor (~1K ohm, not critical) -- 2N3904 Base ESP8266-GND -- 2N3904 Emitter USB-5V -- Speaker + Terminal 2N3904-Collector -- Speaker - Terminal -``` -For ESP32, default output pin is GPIO22. Note that GPIO25 ang GPIO26 are occupied by wclk/bclk and can not be used. +*For ESP32, default output pin is GPIO22. Note that GPIO25 ang GPIO26 are occupied by wclk/bclk and can not be used. +``` *NOTE*: A prior version of this schematic had a direct connection from the ESP8266 to the base of the transistor. While this does provide the maximum amplitude, it also can draw more current from the 8266 than is safe, and can also cause the transistor to overheat. From aa66a685485fc01f7bb95f08f93a756e3d357861 Mon Sep 17 00:00:00 2001 From: Roman Lut <11955117+RomanLut@users.noreply.github.com> Date: Sat, 7 May 2022 00:39:46 +0300 Subject: [PATCH 3/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b83f2ebe..743eba7f 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ AudioGeneratorRTTTL: Enjoy the pleasures of monophonic, 4-octave ringtones on y ## AudioOutput classes AudioOutput: Base class for all output drivers. Takes a sample at a time and returns true/false if there is buffer space for it. If it returns false, it is the calling object's (AudioGenerator's) job to keep the data that didn't fit and try again later. -AudioOutputI2S: Interface for any I2S 16-bit DAC. Sends stereo or mono signals out at whatever frequency set. Tested with Adafruit's I2SDAC and a Beyond9032 DAC from eBay. Tested up to 44.1KHz. To use the internal DAC on ESP32, instantiate this class as `AudioOutputI2S(0,AudioOutputI2S::INTERNAL_DAC)`, see example `PlayMODFromPROGMEMToDAC` and code in [AudioOutputI2S.cpp](src/AudioOutputI2S.cpp#L29) for details. To use the hardware Pulse Density Modulation (PDM) on ESP32, instantiate this class as `AudioOutputI2S(0,AudioOutputI2S::INTERNAL_PDM)`. For both later cases, default output pins are 25 and 26. +AudioOutputI2S: Interface for any I2S 16-bit DAC. Sends stereo or mono signals out at whatever frequency set. Tested with Adafruit's I2SDAC and a Beyond9032 DAC from eBay. Tested up to 44.1KHz. To use the internal DAC on ESP32, instantiate this class as `AudioOutputI2S(0,AudioOutputI2S::INTERNAL_DAC)`, see example `PlayMODFromPROGMEMToDAC` and code in [AudioOutputI2S.cpp](src/AudioOutputI2S.cpp#L29) for details. To use the hardware Pulse Density Modulation (PDM) on ESP32, instantiate this class as `AudioOutputI2S(0,AudioOutputI2S::INTERNAL_PDM)`. For both later cases, default output pins are GPIO25 and GPIO26. AudioOutputI2SNoDAC: Abuses the I2S interface to play music without a DAC. Turns it into a 32x (or higher) oversampling delta-sigma DAC. Use the schematic below to drive a speaker or headphone from the I2STx pin (i.e. Rx). Note that with this interface, depending on the transistor used, you may need to disconnect the Rx pin from the driver to perform serial uploads. Mono-only output, of course. From 923feccef0be1693efaddae55fec891583ec242b Mon Sep 17 00:00:00 2001 From: Roman Lut <11955117+RomanLut@users.noreply.github.com> Date: Sat, 7 May 2022 17:45:17 +0300 Subject: [PATCH 4/5] implemented AudioOutputI2S finishing buffered samples implemented AudioGeneratorMP3 and AudioGeneratorWAV waiting until AudioOutput output buffered samples --- src/AudioGenerator.h | 1 + src/AudioGeneratorMP3.cpp | 15 +++++++++++++-- src/AudioGeneratorWAV.cpp | 25 ++++++++++++++++++++----- src/AudioOutput.h | 1 + src/AudioOutputI2S.cpp | 16 ++++++++++++++++ src/AudioOutputI2S.h | 2 ++ 6 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/AudioGenerator.h b/src/AudioGenerator.h index 6d974876..0a568d01 100644 --- a/src/AudioGenerator.h +++ b/src/AudioGenerator.h @@ -43,6 +43,7 @@ class AudioGenerator protected: bool running; + bool finishing; AudioFileSource *file; AudioOutput *output; int16_t lastSample[2]; diff --git a/src/AudioGeneratorMP3.cpp b/src/AudioGeneratorMP3.cpp index d962c9e0..37b24d72 100644 --- a/src/AudioGeneratorMP3.cpp +++ b/src/AudioGeneratorMP3.cpp @@ -24,6 +24,7 @@ AudioGeneratorMP3::AudioGeneratorMP3() { running = false; + finishing = false; file = NULL; output = NULL; buff = NULL; @@ -34,6 +35,7 @@ AudioGeneratorMP3::AudioGeneratorMP3() AudioGeneratorMP3::AudioGeneratorMP3(void *space, int size): preallocateSpace(space), preallocateSize(size) { running = false; + finishing = false; file = NULL; output = NULL; buff = NULL; @@ -48,6 +50,7 @@ AudioGeneratorMP3::AudioGeneratorMP3(void *buff, int buffSize, void *stream, int preallocateSynthSpace(synth), preallocateSynthSize(synthSize) { running = false; + finishing = false; file = NULL; output = NULL; buff = NULL; @@ -88,13 +91,14 @@ bool AudioGeneratorMP3::stop() stream = NULL; running = false; + finishing = false; output->stop(); return file->close(); } bool AudioGeneratorMP3::isRunning() { - return running; + return running || finishing; } enum mad_flow AudioGeneratorMP3::ErrorToFlow() @@ -209,6 +213,11 @@ bool AudioGeneratorMP3::GetOneSample(int16_t sample[2]) bool AudioGeneratorMP3::loop() { + if ( finishing ) + { + return output->finish() == false; + } + if (!running) goto done; // Nothing to do here! // First, try and push in the stored sample. If we can't, then punt and try later @@ -221,7 +230,9 @@ bool AudioGeneratorMP3::loop() if ( (samplePtr >= synth->pcm.length) && (nsCount >= nsCountMax) ) { retry: if (Input() == MAD_FLOW_STOP) { - return false; + running = false; + finishing = true; + return true; } if (!DecodeNextFrame()) { diff --git a/src/AudioGeneratorWAV.cpp b/src/AudioGeneratorWAV.cpp index 6c8f7a24..a1920e71 100644 --- a/src/AudioGeneratorWAV.cpp +++ b/src/AudioGeneratorWAV.cpp @@ -24,6 +24,7 @@ AudioGeneratorWAV::AudioGeneratorWAV() { running = false; + finishing = false; file = NULL; output = NULL; buffSize = 128; @@ -42,6 +43,7 @@ bool AudioGeneratorWAV::stop() { if (!running) return true; running = false; + finishing = false; free(buff); buff = NULL; output->stop(); @@ -50,7 +52,7 @@ bool AudioGeneratorWAV::stop() bool AudioGeneratorWAV::isRunning() { - return running; + return running || finishing; } @@ -76,6 +78,19 @@ bool AudioGeneratorWAV::GetBufferedData(int bytes, void *dest) bool AudioGeneratorWAV::loop() { + if ( finishing ) + { + if ( output->finish() ) + { + stop(); + return false; + } + else + { + return true; + } + } + if (!running) goto done; // Nothing to do here! // First, try and push in the stored sample. If we can't, then punt and try later @@ -86,18 +101,18 @@ bool AudioGeneratorWAV::loop() { if (bitsPerSample == 8) { uint8_t l, r; - if (!GetBufferedData(1, &l)) stop(); + if (!GetBufferedData(1, &l)) finishing = true; if (channels == 2) { - if (!GetBufferedData(1, &r)) stop(); + if (!GetBufferedData(1, &r)) finishing = true; } else { r = 0; } lastSample[AudioOutput::LEFTCHANNEL] = l; lastSample[AudioOutput::RIGHTCHANNEL] = r; } else if (bitsPerSample == 16) { - if (!GetBufferedData(2, &lastSample[AudioOutput::LEFTCHANNEL])) stop(); + if (!GetBufferedData(2, &lastSample[AudioOutput::LEFTCHANNEL])) finishing = true; if (channels == 2) { - if (!GetBufferedData(2, &lastSample[AudioOutput::RIGHTCHANNEL])) stop(); + if (!GetBufferedData(2, &lastSample[AudioOutput::RIGHTCHANNEL])) finishing = true; } else { lastSample[AudioOutput::RIGHTCHANNEL] = 0; } diff --git a/src/AudioOutput.h b/src/AudioOutput.h index dc157428..52818a9a 100644 --- a/src/AudioOutput.h +++ b/src/AudioOutput.h @@ -44,6 +44,7 @@ class AudioOutput } return count; } + virtual bool finish() { return true; } virtual bool stop() { return false; } virtual void flush() { return; } virtual bool loop() { return true; } diff --git a/src/AudioOutputI2S.cpp b/src/AudioOutputI2S.cpp index 941ffe06..860228b2 100644 --- a/src/AudioOutputI2S.cpp +++ b/src/AudioOutputI2S.cpp @@ -272,6 +272,7 @@ bool AudioOutputI2S::begin(bool txDAC) } #endif i2sOn = true; + finalSamples = 2*128*dma_buf_count; SetRate(hertz); // Default return true; } @@ -338,6 +339,21 @@ void AudioOutputI2S::flush() #endif } +bool AudioOutputI2S::finish() +{ + if (!i2sOn) + return true; + + int16_t sample[2]; + sample[0] = 0; + sample[1] = 0; + + while ( finalSamples > 0 && ConsumeSample(sample)) finalSamples--; + + return finalSamples == 0; +} + + bool AudioOutputI2S::stop() { if (!i2sOn) diff --git a/src/AudioOutputI2S.h b/src/AudioOutputI2S.h index b3f621d2..bdfe4c42 100644 --- a/src/AudioOutputI2S.h +++ b/src/AudioOutputI2S.h @@ -40,6 +40,7 @@ class AudioOutputI2S : public AudioOutput virtual bool begin() override { return begin(true); } virtual bool ConsumeSample(int16_t sample[2]) override; virtual void flush() override; + virtual bool finish() override; virtual bool stop() override; bool begin(bool txDAC); @@ -63,4 +64,5 @@ class AudioOutputI2S : public AudioOutput uint8_t bclkPin; uint8_t wclkPin; uint8_t doutPin; + unsigned long finalSamples; }; From c96e6625c351a2215099d55a4915a338b3210307 Mon Sep 17 00:00:00 2001 From: Roman Lut <11955117+RomanLut@users.noreply.github.com> Date: Sat, 7 May 2022 18:00:29 +0300 Subject: [PATCH 5/5] reverted readme.txt to master state --- README.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 743eba7f..229d45fa 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ AudioGeneratorRTTTL: Enjoy the pleasures of monophonic, 4-octave ringtones on y ## AudioOutput classes AudioOutput: Base class for all output drivers. Takes a sample at a time and returns true/false if there is buffer space for it. If it returns false, it is the calling object's (AudioGenerator's) job to keep the data that didn't fit and try again later. -AudioOutputI2S: Interface for any I2S 16-bit DAC. Sends stereo or mono signals out at whatever frequency set. Tested with Adafruit's I2SDAC and a Beyond9032 DAC from eBay. Tested up to 44.1KHz. To use the internal DAC on ESP32, instantiate this class as `AudioOutputI2S(0,AudioOutputI2S::INTERNAL_DAC)`, see example `PlayMODFromPROGMEMToDAC` and code in [AudioOutputI2S.cpp](src/AudioOutputI2S.cpp#L29) for details. To use the hardware Pulse Density Modulation (PDM) on ESP32, instantiate this class as `AudioOutputI2S(0,AudioOutputI2S::INTERNAL_PDM)`. For both later cases, default output pins are GPIO25 and GPIO26. +AudioOutputI2S: Interface for any I2S 16-bit DAC. Sends stereo or mono signals out at whatever frequency set. Tested with Adafruit's I2SDAC and a Beyond9032 DAC from eBay. Tested up to 44.1KHz. To use the internal DAC on ESP32, instantiate this class as `AudioOutputI2S(0,1)`, see example `PlayMODFromPROGMEMToDAC` and code in [AudioOutputI2S.cpp](src/AudioOutputI2S.cpp#L29) for details. AudioOutputI2SNoDAC: Abuses the I2S interface to play music without a DAC. Turns it into a 32x (or higher) oversampling delta-sigma DAC. Use the schematic below to drive a speaker or headphone from the I2STx pin (i.e. Rx). Note that with this interface, depending on the transistor used, you may need to disconnect the Rx pin from the driver to perform serial uploads. Mono-only output, of course. @@ -199,8 +199,7 @@ Use the `AudioOutputI2S*No*DAC` object instead of the `AudioOutputI2S` in your c ESP8266-GND ------------------+ | +------+ K| | | | E| ESP8266-I2SOUT (Rx) -----/\/\/\--+ | \ R| -or ESP32 DOUT pin | +-| - | + | +-| USB 5V -----------------------------+ You may also want to add a 220uF cap from USB5V to GND just to help filter out any voltage droop during high volume playback. @@ -213,19 +212,12 @@ ESP8266-RX(I2S tx) -- Resistor (~1K ohm, not critical) -- 2N3904 Base ESP8266-GND -- 2N3904 Emitter USB-5V -- Speaker + Terminal 2N3904-Collector -- Speaker - Terminal - -*For ESP32, default output pin is GPIO22. Note that GPIO25 ang GPIO26 are occupied by wclk/bclk and can not be used. ``` *NOTE*: A prior version of this schematic had a direct connection from the ESP8266 to the base of the transistor. While this does provide the maximum amplitude, it also can draw more current from the 8266 than is safe, and can also cause the transistor to overheat. As of the latest ESP8266Audio release, with the software delta-sigma DAC the LRCLK and BCLK pins *can* be used by an application. Simply use normal `pinMode` and `digitalWrite` or `digitalRead` as desired. -### Hardware PDM on ESP32 - -Hardware PDM outputs 128 * 48Khz pulses regardles of samplerate. -It seems that currently hardware PDM either does not output constant One at maximum sample level, or does not output 3.3V voltage at pulse ( unfortunatelly I not have oscilloscope to test currently) - sound is not as loud as desired. You may consider using software delta-sigma DAC instead. - ### High pitched buzzing with the 1-T circuit The 1-T amp can _NOT_ drive any sort of amplified speaker. If there is a power or USB input to the speaker, or it has lights or Bluetooth or a battery, it can _NOT_ be used with this circuit.