Skip to content

Commit

Permalink
an update of ESP8266Audio library
Browse files Browse the repository at this point in the history
  • Loading branch information
lyusupov committed Dec 23, 2024
1 parent 80d8eb4 commit 2006d30
Show file tree
Hide file tree
Showing 15 changed files with 207 additions and 88 deletions.
18 changes: 15 additions & 3 deletions software/firmware/source/libraries/ESP8266Audio/README.md
Original file line number Diff line number Diff line change
@@ -1,6 1,10 @@
# ESP8266Audio - supports ESP8266 & ESP32 & Raspberry Pi Pico RP2040 [![Gitter](https://badges.gitter.im/ESP8266Audio/community.svg)](https://gitter.im/ESP8266Audio/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
# ESP8266Audio - supports ESP8266 & ESP32 & Raspberry Pi Pico RP2040 and Pico 2 RP2350 [![Gitter](https://badges.gitter.im/ESP8266Audio/community.svg)](https://gitter.im/ESP8266Audio/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Arduino library for parsing and decoding MOD, WAV, MP3, FLAC, MIDI, AAC, and RTTL files and playing them on an I2S DAC or even using a software-simulated delta-sigma DAC with dynamic 32x-128x oversampling.

# Raspberry Pi Pico (RP2040 and RP2350) Users
Consider using [BackgroundAudio](https://github.com/earlephilhower/BackgroundAudio) instead of this library as it can provide a simpler usage model and better results and performance on the Pico by using an interrupt-based, frame-aligned output model.

# ESP8266 and ESP32 Users
ESP8266 is fully supported and most mature, but ESP32 is also mostly there with built-in DAC as well as external ones.

For real-time, autonomous speech synthesis, check out [ESP8266SAM](https://github.com/earlephilhower/ESP8266SAM), a library which uses this one and a port of an ancient format-based synthesis program to allow your ESP8266 to talk with low memory and no network required.
Expand Down Expand Up @@ -138,7 142,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 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.
Expand Down Expand Up @@ -190,7 194,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.
Expand All @@ -203,12 208,19 @@ 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 regardless of sample rate.
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 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 14,7 @@
"type": "git",
"url": "https://github.com/earlephilhower/ESP8266Audio"
},
"version": "1.9.7",
"version": "1.9.9",
"homepage": "https://github.com/earlephilhower/ESP8266Audio",
"frameworks": "Arduino",
"examples": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 1,5 @@
name=ESP8266Audio
version=1.9.7
version=1.9.9
author=Earle F. Philhower, III
maintainer=Earle F. Philhower, III
sentence=Audio file and I2S sound playing routines for ESP8266, ESP32, and Raspberry Pi Pico RP2040
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,8 443,9 @@ int AudioGeneratorMIDI::PlayMIDI()
for (tgnum = 0; tgnum < num_tonegens; tgnum) { /* find which generator is playing it */
tg = &tonegen[tgnum];
if (tg->playing && tg->track == tracknum && tg->note == trk->note) {
tsf_note_off (g_tsf, tg->instrument, tg->note);
tsf_note_off_fast (g_tsf, tg->instrument, tg->note, tg->playIndex);
tg->playing = false;
tg->playIndex = -1;
trk->tonegens[tgnum] = false;
}
}
Expand Down Expand Up @@ -476,7 477,7 @@ int AudioGeneratorMIDI::PlayMIDI()
if (tg->instrument != midi_chan_instrument[trk->chan]) { /* new instrument for this generator */
tg->instrument = midi_chan_instrument[trk->chan];
}
tsf_note_on (g_tsf, tg->instrument, tg->note, trk->velocity / 127.0); // velocity = 0...127
tg->playIndex = tsf_note_on_fast (g_tsf, tg->instrument, tg->note, trk->velocity / 127.0); // velocity = 0...127
} else {
notes_skipped;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 94,13 @@ class AudioGeneratorMIDI : public AudioGenerator
unsigned long earliest_time = 0;

struct tonegen_status { /* current status of a tone generator */
bool playing; /* is it playing? */
bool playing; /* is it playing? */
char track; /* if so, which track is the note from? */
char note; /* what note is playing? */
char instrument; /* what instrument? */
int playIndex; /* is index provided?
Unique identifier generated when note starts playing.
This help us to turn the note off faster */
} tonegen[MAX_TONEGENS];

struct track_status { /* current processing point of a MIDI track */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 27,9 @@ AudioGeneratorMP3::AudioGeneratorMP3()
file = NULL;
output = NULL;
buff = NULL;
synth = NULL;
frame = NULL;
stream = NULL;
nsCountMax = 1152/32;
madInitted = false;
}
Expand All @@ -37,6 40,9 @@ AudioGeneratorMP3::AudioGeneratorMP3(void *space, int size): preallocateSpace(sp
file = NULL;
output = NULL;
buff = NULL;
synth = NULL;
frame = NULL;
stream = NULL;
nsCountMax = 1152/32;
madInitted = false;
}
Expand All @@ -51,6 57,9 @@ AudioGeneratorMP3::AudioGeneratorMP3(void *buff, int buffSize, void *stream, int
file = NULL;
output = NULL;
buff = NULL;
synth = NULL;
frame = NULL;
stream = NULL;
nsCountMax = 1152/32;
madInitted = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 123,7 @@ bool AudioOutputI2S::SetPinout(int bclk, int wclk, int dout, int mclk)
bclkPin = bclk;
wclkPin = wclk;
doutPin = dout;
#ifdef ESP32
#if defined(ESP32) || defined(ARDUINO_ARCH_RP2040)
mclkPin = mclk;
if (i2sOn)
return SetPinout();
Expand Down Expand Up @@ -182,6 182,8 @@ bool AudioOutputI2S::SetMclk(bool enabled){
if (output_mode == INTERNAL_DAC || output_mode == INTERNAL_PDM)
return false; // Not allowed

use_mclk = enabled;
#elif defined(ARDUINO_ARCH_RP2040)
use_mclk = enabled;
#endif
return true;
Expand Down Expand Up @@ -336,9 338,16 @@ bool AudioOutputI2S::begin(bool txDAC)
#elif defined(ARDUINO_ARCH_RP2040)
(void)txDAC;
if (!i2sOn) {
i2s.setBCLK(bclkPin);
i2s.setDATA(doutPin);
i2s.begin(hertz);
i2s.setSysClk(hertz);
i2s.setBCLK(bclkPin);
i2s.setDATA(doutPin);
i2s.setMCLK(mclkPin);
i2s.setMCLKmult(256);
if (swap_clocks) {
i2s.swapClocks();
}
i2s.setBitsPerSample(bps);
i2s.begin(hertz);
}
#endif
i2sOn = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 71,8 @@ bool AudioOutputMixerStub::stop()
AudioOutputMixer::AudioOutputMixer(int buffSizeSamples, AudioOutput *dest) : AudioOutput()
{
buffSize = buffSizeSamples;
leftAccum = (int32_t*)calloc(sizeof(int32_t), buffSize);
rightAccum = (int32_t*)calloc(sizeof(int32_t), buffSize);
leftAccum = (int32_t*)calloc(buffSize, sizeof(int32_t));
rightAccum = (int32_t*)calloc(buffSize, sizeof(int32_t));
for (int i=0; i<maxStubs; i ) {
stubAllocated[i] = false;
stubRunning[i] = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 105,9 @@ bool AudioOutputPWM::ConsumeSample(int16_t sample[2]) {
MakeSampleStereo16( ms );
}

ms[LEFTCHANNEL] = Amplify(ms[LEFTCHANNEL]);
ms[RIGHTCHANNEL] = Amplify(ms[RIGHTCHANNEL]);

if (pwm.available()) {
pwm.write((int16_t) ms[0]);
pwm.write((int16_t) ms[1]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,14 558,8 @@ static __inline int CLZ(int x)
typedef union _U64 {
Word64 w64;
struct {
#if defined(__XTENSA__) || defined (__riscv)
unsigned int lo32;
signed int hi32;
#else
/* PowerPC = big endian */
signed int hi32;
unsigned int lo32;
#endif
} r;
} U64;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 258,10 @@ int DecodeProgramConfigElement(ProgConfigElement *pce, BitStreamInfo *bsi)
pce->bce[i] |= GetBits(bsi, 4); /* tag select */
}

for (i = 0; i < pce->numLCE; i )
for (i = 0; i < pce->numLCE && i < MAX_NUM_LCE; i )
pce->lce[i] = GetBits(bsi, 4); /* tag select */

for (i = 0; i < pce->numADE; i )
for (i = 0; i < pce->numADE && i < MAX_NUM_ADE; i )
pce->ade[i] = GetBits(bsi, 4); /* tag select */

for (i = 0; i < pce->numCCE; i ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 253,7 @@ static int DecodeOneScaleFactor(BitStreamInfo *bsi)

pi->numPulse = GetBits(bsi, 2) 1; /* add 1 here */
pi->startSFB = GetBits(bsi, 6);
for (i = 0; i < pi->numPulse; i ) {
for (i = 0; i < pi->numPulse && i < MAX_PULSES; i ) {
pi->offset[i] = GetBits(bsi, 5);
pi->amp[i] = GetBits(bsi, 4);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 195,7 @@ static void UnpackSBRGrid(BitStreamInfo *bsi, SBRHeader *sbrHdr, SBRGrid *sbrGri
absBorder = GetBits(bsi, 2) NUM_TIME_SLOTS;
numRelBorder = GetBits(bsi, 2);
sbrGrid->numEnv = numRelBorder 1;
for (rel = 0; rel < numRelBorder; rel )
for (rel = 0; rel < numRelBorder && rel < 3; rel )
relBorder[rel] = 2*GetBits(bsi, 2) 2;

pBits = cLog2[sbrGrid->numEnv 1];
Expand All @@ -221,13 221,13 @@ static void UnpackSBRGrid(BitStreamInfo *bsi, SBRHeader *sbrHdr, SBRGrid *sbrGri
absBorder = GetBits(bsi, 2);
numRelBorder = GetBits(bsi, 2);
sbrGrid->numEnv = numRelBorder 1;
for (rel = 0; rel < numRelBorder; rel )
for (rel = 0; rel < numRelBorder && rel < 3; rel )
relBorder[rel] = 2*GetBits(bsi, 2) 2;

pBits = cLog2[sbrGrid->numEnv 1];
sbrGrid->pointer = GetBits(bsi, pBits);

for (env = 0; env < sbrGrid->numEnv; env )
for (env = 0; env < sbrGrid->numEnv && env < MAX_NUM_ENV; env )
sbrGrid->freqRes[env] = GetBits(bsi, 1);

absBordLead = absBorder;
Expand All @@ -253,16 253,16 @@ static void UnpackSBRGrid(BitStreamInfo *bsi, SBRHeader *sbrHdr, SBRGrid *sbrGri
sbrGrid->numEnv = numRelBorder0 numRelBorder1 1;
ASSERT(sbrGrid->numEnv <= 5);

for (rel = 0; rel < numRelBorder0; rel )
for (rel = 0; rel < numRelBorder0 && rel < 3; rel )
relBorder0[rel] = 2*GetBits(bsi, 2) 2;

for (rel = 0; rel < numRelBorder1; rel )
for (rel = 0; rel < numRelBorder1 && rel < 3; rel )
relBorder1[rel] = 2*GetBits(bsi, 2) 2;

pBits = cLog2[numRelBorder0 numRelBorder1 2];
sbrGrid->pointer = GetBits(bsi, pBits);

for (env = 0; env < sbrGrid->numEnv; env )
for (env = 0; env < sbrGrid->numEnv && env < MAX_NUM_ENV; env )
sbrGrid->freqRes[env] = GetBits(bsi, 1);

numRelLead = numRelBorder0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 801,7 @@ REQUIRED IN ALL CASES
Notice to be included in header file of all Error Corrections and Shared
Modifications:

Portions Copyright 1994-2003 RealNetworks, Inc. All rights reserved.
Portions Copyright 1994-2003 (c) RealNetworks, Inc. All rights reserved.

The contents of this file, and the files included with this file, are
subject to the current version of RealNetworks Community Source License
Expand Down
Loading

0 comments on commit 2006d30

Please sign in to comment.