Skip to content

Commit

Permalink
Merge branch 'master' into setrate-click-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
earlephilhower authored Oct 9, 2024
2 parents 4d76f39 + 7e50a43 commit 505533e
Show file tree
Hide file tree
Showing 34 changed files with 377 additions and 219 deletions.
22 changes: 11 additions & 11 deletions .github/workflows/pr-or-master-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ jobs:
matrix:
chunk: [0, 1, 2, 3, 4]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-python@v2
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Build Sketches
Expand All @@ -43,10 +43,10 @@ jobs:
matrix:
chunk: [0, 1, 2, 3, 4]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-python@v2
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Build Sketches
Expand All @@ -66,10 +66,10 @@ jobs:
matrix:
chunk: [0, 1, 2, 3, 4]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-python@v2
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Build Sketches
Expand All @@ -87,10 +87,10 @@ jobs:
name: Host tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-python@v2
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Run host tests
Expand All @@ -115,8 +115,8 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: arduino/arduino-lint-action@v1
- uses: actions/checkout@v4
- uses: arduino/arduino-lint-action@v2
with:
library-manager: 'update'

Expand All @@ -128,7 +128,7 @@ jobs:
run:
shell: bash
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
submodules: true
- name: Run codespell
Expand Down
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Arduino library for parsing and decoding MOD, WAV, MP3, FLAC, MIDI, AAC, and RTT

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.
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 formant-based synthesis program to allow your ESP8266 to talk with low memory and no network required.

## Disclaimer
All this code is released under the GPL, and all of it is to be used at your own risk. If you find any bugs, please let me know via the GitHub issue tracker or drop me an email.
Expand Down Expand Up @@ -138,7 +138,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 +190,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 +204,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
4 changes: 2 additions & 2 deletions examples/PlayMIDIFromSPIFFS/PlayMIDIFromSPIFFS.ino
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include <Arduino.h>

// Do not build on GCC8, GCC8 has a compiler bug
// Do not build on Espressif GCC8+, compiler bug

#if defined(ARDUINO_ARCH_RP2040) || ((__GNUC__ == 8) && (__XTENSA__))
#if defined(ARDUINO_ARCH_RP2040) || (defined(ESP32) && (__GNUC__ >= 8) && (__XTENSA__))
void setup() {}
void loop() {}
#else
Expand Down
2 changes: 1 addition & 1 deletion library.json
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
3 changes: 2 additions & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
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
paragraph=Decode compressed MP3, AAC, FLAC, Screamtracker MOD, MIDI, RTTL, TI Talkie, and WAV and play on an I2S DAC or a software-driven delta-sigma DAC and 1-transistor amplifier.
category=Signal Input/Output
url=https://github.com/earlephilhower/ESP8266Audio
architectures=esp8266,esp32,rp2040
dot_a_linkage=true
4 changes: 4 additions & 0 deletions src/AudioFileSourceHTTPStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,11 @@ uint32_t AudioFileSourceHTTPStream::readInternal(void *data, uint32_t len, bool
}
if ((size > 0) && (pos >= size)) return 0;

#if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR >= 3
NetworkClient *stream = http.getStreamPtr();
#else
WiFiClient *stream = http.getStreamPtr();
#endif

// Can't read past EOF...
if ( (size > 0) && (len > (uint32_t)(pos - size)) ) len = pos - size;
Expand Down
4 changes: 4 additions & 0 deletions src/AudioFileSourceHTTPStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ class AudioFileSourceHTTPStream : public AudioFileSource

private:
virtual uint32_t readInternal(void *data, uint32_t len, bool nonBlock);
#if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR >= 3
NetworkClient client;
#else
WiFiClient client;
#endif
HTTPClient http;
int pos;
int size;
Expand Down
4 changes: 4 additions & 0 deletions src/AudioFileSourceICYStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ uint32_t AudioFileSourceICYStream::readInternal(void *data, uint32_t len, bool n
}
if ((size > 0) && (pos >= size)) return 0;

#if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR >= 3
NetworkClient *stream = http.getStreamPtr();
#else
WiFiClient *stream = http.getStreamPtr();
#endif

// Can't read past EOF...
if ( (size > 0) && (len > (uint32_t)(pos - size)) ) len = pos - size;
Expand Down
2 changes: 1 addition & 1 deletion src/AudioGeneratorFLAC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ char AudioGeneratorFLAC::error_cb_str[64];
void AudioGeneratorFLAC::error_cb(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status)
{
(void) decoder;
strncpy_P(error_cb_str, FLAC__StreamDecoderErrorStatusString[status], 64);
strncpy_P(error_cb_str, FLAC__StreamDecoderErrorStatusString[status], sizeof(AudioGeneratorFLAC::error_cb_str) - 1);
cb.st((int)status, error_cb_str);
}

9 changes: 5 additions & 4 deletions src/AudioGeneratorMIDI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@

#include "AudioGeneratorMIDI.h"

#if (__GNUC__ == 8) && (__XTENSA__)
// Do not build, GCC8 has a compiler bug
#if defined(ESP32) && (__GNUC__ >= 8) && (__XTENSA__)
// Do not build, Espressif's GCC8+ has a compiler bug
#else // __GNUC__ == 8

#pragma GCC optimize ("O3")
Expand Down 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
9 changes: 6 additions & 3 deletions src/AudioGeneratorMIDI.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
#ifndef _AUDIOGENERATORMIDI_H
#define _AUDIOGENERATORMIDI_H

#if (__GNUC__ == 8) && (__XTENSA__)
// Do not build, GCC8 has a compiler bug
#if defined(ESP32) && (__GNUC__ >= 8) && (__XTENSA__)
// Do not build, Espressif's GCC8+ has a compiler bug
#else // __GNUC__ == 8

#include "AudioGenerator.h"
Expand Down 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
18 changes: 11 additions & 7 deletions src/AudioGeneratorMP3.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
AudioGeneratorMP3
Wrap libmad MP3 library to play audio
Copyright (C) 2017 Earle F. Philhower, III
This program is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -62,7 +62,7 @@ AudioGeneratorMP3::~AudioGeneratorMP3()
free(synth);
free(frame);
free(stream);
}
}
}


Expand Down Expand Up @@ -182,15 +182,15 @@ bool AudioGeneratorMP3::GetOneSample(int16_t sample[2])
output->SetChannels(synth->pcm.channels);
lastChannels = synth->pcm.channels;
}

// If we're here, we have one decoded frame and sent 0 or more samples out
if (samplePtr < synth->pcm.length) {
sample[AudioOutput::LEFTCHANNEL ] = synth->pcm.samples[0][samplePtr];
sample[AudioOutput::RIGHTCHANNEL] = synth->pcm.samples[1][samplePtr];
samplePtr++;
} else {
samplePtr = 0;

switch ( mad_synth_frame_onens(synth, frame, nsCount++) ) {
case MAD_FLOW_STOP:
case MAD_FLOW_BREAK: audioLogger->printf_P(PSTR("msf1ns failed\n"));
Expand Down Expand Up @@ -302,6 +302,7 @@ bool AudioGeneratorMP3::begin(AudioFileSource *source, AudioOutput *output)
synth = reinterpret_cast<struct mad_synth *>(preallocateSynthSpace);
}
else {
output->stop();
audioLogger->printf_P("OOM error in MP3: Want %d/%d/%d/%d bytes, have %d/%d/%d/%d bytes preallocated.\n",
preAllocBuffSize(), preAllocStreamSize(), preAllocFrameSize(), preAllocSynthSize(),
preallocateSize, preallocateStreamSize, preallocateFrameSize, preallocateSynthSize);
Expand All @@ -319,6 +320,7 @@ bool AudioGeneratorMP3::begin(AudioFileSource *source, AudioOutput *output)
p += preAllocSynthSize();
int neededBytes = p - reinterpret_cast<uint8_t *>(preallocateSpace);
if (neededBytes > preallocateSize) {
output->stop();
audioLogger->printf_P("OOM error in MP3: Want %d bytes, have %d bytes preallocated.\n", neededBytes, preallocateSize);
return false;
}
Expand All @@ -336,17 +338,20 @@ bool AudioGeneratorMP3::begin(AudioFileSource *source, AudioOutput *output)
stream = NULL;
frame = NULL;
synth = NULL;

output->stop();
audioLogger->printf_P("OOM error in MP3\n");
return false;
}
}

mad_stream_init(stream);
mad_frame_init(frame);
mad_synth_init(synth);
synth->pcm.length = 0;
mad_stream_options(stream, 0); // TODO - add options support
madInitted = true;

running = true;
return true;
}
Expand Down Expand Up @@ -413,4 +418,3 @@ extern "C" {
}
#endif
}

6 changes: 3 additions & 3 deletions src/AudioGeneratorRTTTL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,13 @@ bool AudioGeneratorRTTTL::GetNextNote()
ptr++;
note++;
}
if (!ReadInt(&scale)) {
scale = defaultOctave;
}
if ((ptr < len) && (buff[ptr] == '.')) {
ptr++;
dur += dur / 2;
}
if (!ReadInt(&scale)) {
scale = defaultOctave;
}
// Eat any trailing whitespace and comma
SkipWhitespace();
if ((ptr < len) && (buff[ptr]==',')) {
Expand Down
Loading

0 comments on commit 505533e

Please sign in to comment.