Skip to content

Commit

Permalink
Add support for Raspberry Pi Pico Arduino Core
Browse files Browse the repository at this point in the history
Adds support for the RP2040 core and the PIO based I2S output device.
https://github.com/earlephilhower/arduino-pico

Poisons some ARM optimizations which don't work on modern Thumbs.
  • Loading branch information
earlephilhower committed Apr 3, 2021
1 parent f4d4e69 commit b37e9d2
Show file tree
Hide file tree
Showing 21 changed files with 121 additions and 78 deletions.
1 change: 1 addition & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ AudioGeneratorWAV KEYWORD1
AudioOutput KEYWORD1
AudioOutputI2S KEYWORD1
AudioOutputI2SNoDAC KEYWORD1
AudioOutputI2SClass KEYWORD1
AudioOutputNull KEYWORD1
AudioOutputBuffer KEYWORD1
AudioOutputSerialWAV KEYWORD1
Expand Down
10 changes: 2 additions & 8 deletions library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ESP8266Audio",
"description": "Audio file format and I2S DAC library",
"description": "Audio file format and I2S DAC library for ESP8266, ESP32, and Raspberry Pi Pico RP2040",
"keywords": "ESP8266, ESP32, MP3, AAC, WAV, MOD, FLAC, RTTTL, MIDI, I2S, DAC, Delta-Sigma, TTS",
"authors": [
{
Expand All @@ -14,14 +14,8 @@
"type": "git",
"url": "https://github.com/earlephilhower/ESP8266Audio"
},
"version": "1.8.1",
"version": "1.9.0",
"homepage": "https://github.com/earlephilhower/ESP8266Audio",
"dependencies": {
"SPI": "1.0",
"SPIFFS": "1.0",
"HTTPClient": "1.0",
"WiFiClientSecure": "1.0"
},
"frameworks": "Arduino",
"examples": [
"examples/*/*.ino"
Expand Down
8 changes: 4 additions & 4 deletions library.properties
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name=ESP8266Audio
version=1.8.1
version=1.9.0
author=Earle F. Philhower, III
maintainer=Earle F. Philhower, III
sentence=Audio file and I2S sound playing routines.
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.
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
architectures=esp8266,esp32,rp2040
4 changes: 4 additions & 0 deletions src/AudioFileSourceHTTPStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#if defined(ESP32) || defined(ESP8266)

#include "AudioFileSourceHTTPStream.h"

AudioFileSourceHTTPStream::AudioFileSourceHTTPStream()
Expand Down Expand Up @@ -152,3 +154,5 @@ uint32_t AudioFileSourceHTTPStream::getPos()
{
return pos;
}

#endif
4 changes: 2 additions & 2 deletions src/AudioFileSourceHTTPStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _AUDIOFILESOURCEHTTPSTREAM_H
#define _AUDIOFILESOURCEHTTPSTREAM_H
#if defined(ESP32) || defined(ESP8266)
#pragma once

#include <Arduino.h>
#ifdef ESP32
Expand Down
5 changes: 5 additions & 0 deletions src/AudioFileSourceICYStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#if defined(ESP32) || defined(ESP8266)

#define _GNU_SOURCE

#include "AudioFileSourceICYStream.h"
Expand Down Expand Up @@ -215,3 +218,5 @@ uint32_t AudioFileSourceICYStream::readInternal(void *data, uint32_t len, bool n
icyByteCount += ret;
return read;
}

#endif
6 changes: 2 additions & 4 deletions src/AudioFileSourceICYStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _AUDIOFILESOURCEICYSTREAM_H
#define _AUDIOFILESOURCEICYSTREAM_H
#if defined(ESP32) || defined(ESP8266)
#pragma once

#include <Arduino.h>
#ifdef ESP32
Expand All @@ -45,6 +45,4 @@ class AudioFileSourceICYStream : public AudioFileSourceHTTPStream
int icyByteCount;
};


#endif

4 changes: 4 additions & 0 deletions src/AudioFileSourceSPIRAMBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#if defined(ESP32) || defined(ESP8266)

#include <Arduino.h>
#include "AudioFileSourceSPIRAMBuffer.h"

Expand Down Expand Up @@ -165,3 +167,5 @@ bool AudioFileSourceSPIRAMBuffer::loop()
}
return true;
}

#endif
4 changes: 2 additions & 2 deletions src/AudioFileSourceSPIRAMBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _AUDIOFILESOURCESPIRAMBUFFER_H
#define _AUDIOFILESOURCESPIRAMBUFFER_H
#if defined(ESP32) || defined(ESP8266)
#pragma once

#include "AudioFileSource.h"
#include <SPI.h>
Expand Down
6 changes: 3 additions & 3 deletions src/AudioGeneratorFLAC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,9 @@ FLAC__StreamDecoderWriteStatus AudioGeneratorFLAC::write_cb(const FLAC__StreamDe
// Hackish warning here. FLAC sends the buffer but doesn't free it until the next call to decode_frame, so we stash
// the pointers here and use it in our loop() instead of memcpy()'ing into yet another buffer.
buffLen = frame->header.blocksize;
buff[0] = buffer[0];
if (frame->header.channels>1) buff[1] = buffer[1];
else buff[1] = buffer[0];
buff[0] = (const int *)buffer[0];
if (frame->header.channels>1) buff[1] = (const int *)buffer[1];
else buff[1] = (const int *)buffer[0];
buffPtr = 0;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
Expand Down
87 changes: 59 additions & 28 deletions src/AudioOutputI2S.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@
#include <Arduino.h>
#ifdef ESP32
#include "driver/i2s.h"
#else
#elif defined(ESP8266)
#include <i2s.h>
#elif defined(ARDUINO_ARCH_RP2040)
#include <I2S.h>
#endif
#include "AudioOutputI2S.h"

#if defined(ESP32) || defined(ESP8266)
AudioOutputI2S::AudioOutputI2S(int port, int output_mode, int dma_buf_count, int use_apll)
{
this->portNo = port;
Expand All @@ -48,18 +51,25 @@ AudioOutputI2S::AudioOutputI2S(int port, int output_mode, int dma_buf_count, int
SetGain(1.0);
}

AudioOutputI2S::~AudioOutputI2S()
bool AudioOutputI2S::SetPinout()
{
#ifdef ESP32
if (i2sOn) {
audioLogger->printf("UNINSTALL I2S\n");
i2s_driver_uninstall((i2s_port_t)portNo); //stop & destroy i2s driver
}
if (output_mode == INTERNAL_DAC || output_mode == INTERNAL_PDM)
return false; // Not allowed

i2s_pin_config_t pins = {
.bck_io_num = bclkPin,
.ws_io_num = wclkPin,
.data_out_num = doutPin,
.data_in_num = I2S_PIN_NO_CHANGE};
i2s_set_pin((i2s_port_t)portNo, &pins);
return true;
#else
if (i2sOn)
i2s_end();
(void)bclkPin;
(void)wclkPin;
(void)doutPin;
return false;
#endif
i2sOn = false;
}

bool AudioOutputI2S::SetPinout(int bclk, int wclk, int dout)
Expand All @@ -72,26 +82,33 @@ bool AudioOutputI2S::SetPinout(int bclk, int wclk, int dout)

return true;
}
#elif defined(ARDUINO_ARCH_RP2040)
AudioOutputI2S::AudioOutputI2S(long sampleRate, pin_size_t sck, pin_size_t data) {
i2sOn = false;
mono = false;
bps = 16;
channels = 2;
hertz = sampleRate;
bclkPin = sck;
doutPin = data;
SetGain(1.0);
}
#endif

bool AudioOutputI2S::SetPinout()
AudioOutputI2S::~AudioOutputI2S()
{
#ifdef ESP32
if (output_mode == INTERNAL_DAC || output_mode == INTERNAL_PDM)
return false; // Not allowed

i2s_pin_config_t pins = {
.bck_io_num = bclkPin,
.ws_io_num = wclkPin,
.data_out_num = doutPin,
.data_in_num = I2S_PIN_NO_CHANGE};
i2s_set_pin((i2s_port_t)portNo, &pins);
return true;
#else
(void)bclkPin;
(void)wclkPin;
(void)doutPin;
return false;
if (i2sOn) {
audioLogger->printf("UNINSTALL I2S\n");
i2s_driver_uninstall((i2s_port_t)portNo); //stop & destroy i2s driver
}
#elif defined(ESP8266)
if (i2sOn)
i2s_end();
#elif defined(ARDUINO_ARCH_RP2040)
stop();
#endif
i2sOn = false;
}

bool AudioOutputI2S::SetRate(int hz)
Expand All @@ -102,8 +119,10 @@ bool AudioOutputI2S::SetRate(int hz)
{
#ifdef ESP32
i2s_set_sample_rates((i2s_port_t)portNo, AdjustI2SRate(hz));
#else
#elif defined(ESP8266)
i2s_set_rate(AdjustI2SRate(hz));
#elif defined(ARDUINO_ARCH_RP2040)
I2S.setFrequency(hz);
#endif
}
return true;
Expand Down Expand Up @@ -189,7 +208,7 @@ bool AudioOutputI2S::begin(bool txDAC)
}
i2s_zero_dma_buffer((i2s_port_t)portNo);
}
#else
#elif defined(ESP8266)
(void)dma_buf_count;
(void)use_apll;
if (!i2sOn)
Expand All @@ -209,6 +228,11 @@ bool AudioOutputI2S::begin(bool txDAC)
}
#endif
}
#elif defined(ARDUINO_ARCH_RP2040)
(void)txDAC;
if (!i2sOn) {
I2S.begin(hertz, bclkPin, doutPin);
}
#endif
i2sOn = true;
SetRate(hertz); // Default
Expand Down Expand Up @@ -246,9 +270,11 @@ bool AudioOutputI2S::ConsumeSample(int16_t sample[2])
s32 = ((Amplify(ms[RIGHTCHANNEL])) << 16) | (Amplify(ms[LEFTCHANNEL]) & 0xffff);
}
return i2s_write_bytes((i2s_port_t)portNo, (const char *)&s32, sizeof(uint32_t), 0);
#else
#elif defined(ESP8266)
uint32_t s32 = ((Amplify(ms[RIGHTCHANNEL])) << 16) | (Amplify(ms[LEFTCHANNEL]) & 0xffff);
return i2s_write_sample_nb(s32); // If we can't store it, return false. OTW true
#elif defined(ARDUINO_ARCH_RP2040)
return !!I2S.write((void*)ms, 4);
#endif
}

Expand All @@ -265,6 +291,8 @@ void AudioOutputI2S::flush()
delay(10);
}
}
#elif defined(ARDUINO_ARCH_RP2040)
I2S.flush();
#endif
}

Expand All @@ -275,6 +303,9 @@ bool AudioOutputI2S::stop()

#ifdef ESP32
i2s_zero_dma_buffer((i2s_port_t)portNo);
#elif defined(ARDUINO_ARCH_RP2040)
I2S.end();
#endif
i2sOn = false;
return true;
}
19 changes: 9 additions & 10 deletions src/AudioOutputI2S.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,22 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _AUDIOOUTPUTI2S_H
#define _AUDIOOUTPUTI2S_H
#pragma once

#include "AudioOutput.h"

class AudioOutputI2S : public AudioOutput
{
public:
#if defined(ESP32) || defined(ESP8266)
AudioOutputI2S(int port=0, int output_mode=EXTERNAL_I2S, int dma_buf_count = 8, int use_apll=APLL_DISABLE);
virtual ~AudioOutputI2S() override;
bool SetPinout(int bclkPin, int wclkPin, int doutPin);
enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 };
enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 };
#elif defined(ARDUINO_ARCH_RP2040)
AudioOutputI2S(long sampleRate = 44100, pin_size_t sck = 26, pin_size_t data = 28);
#endif
virtual ~AudioOutputI2S() override;
virtual bool SetRate(int hz) override;
virtual bool SetBitsPerSample(int bits) override;
virtual bool SetChannels(int channels) override;
Expand All @@ -37,12 +42,9 @@ class AudioOutputI2S : public AudioOutput
virtual void flush() override;
virtual bool stop() override;

bool begin (bool txDAC);
bool begin(bool txDAC);
bool SetOutputModeMono(bool mono); // Force mono output no matter the input

enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 };
enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 };

protected:
bool SetPinout();
virtual int AdjustI2SRate(int hz) { return hz; }
Expand All @@ -60,6 +62,3 @@ class AudioOutputI2S : public AudioOutput
uint8_t wclkPin;
uint8_t doutPin;
};

#endif

13 changes: 10 additions & 3 deletions src/AudioOutputI2SNoDAC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
#include <Arduino.h>
#ifdef ESP32
#include "driver/i2s.h"
#else
#elif defined(ESP8266)
#include <i2s.h>
#elif defined(ARDUINO_ARCH_RP2040)
#include <I2S.h>
#endif
#include "AudioOutputI2SNoDAC.h"

Expand All @@ -32,7 +34,7 @@ AudioOutputI2SNoDAC::AudioOutputI2SNoDAC(int port) : AudioOutputI2S(port, false)
SetOversampling(32);
lastSamp = 0;
cumErr = 0;
#ifndef ESP32
#ifdef ESP8266
WRITE_PERI_REG(PERIPHS_IO_MUX_MTDO_U, orig_bck);
WRITE_PERI_REG(PERIPHS_IO_MUX_GPIO2_U, orig_ws);
#endif
Expand Down Expand Up @@ -97,12 +99,17 @@ bool AudioOutputI2SNoDAC::ConsumeSample(int16_t sample[2])
#ifdef ESP32
if (!i2s_write_bytes((i2s_port_t)portNo, (const char *)dsBuff, sizeof(uint32_t) * (oversample/32), 0))
return false;
#else
#elif defined(ESP8266)
if (!i2s_write_sample_nb(dsBuff[0])) return false; // No room at the inn
// At this point we've sent in first of possibly 8 32-bits, need to send
// remaining ones even if they block.
for (int i = 32; i < oversample; i+=32)
i2s_write_sample( dsBuff[i / 32]);
#elif defined(ARDUINO_ARCH_RP2040)
int16_t *p = (int16_t *) dsBuff;
for (int i = 0; i < oversample / 16; i++) {
I2S.write(*(p++));
}
#endif
return true;
}
Loading

0 comments on commit b37e9d2

Please sign in to comment.