Add pre-packaged dependency libraries (ESP32-audioI2S and PNGdec)
This commit is contained in:
352
libraries/ESP32-audioI2S/examples/AC101/AC101.cpp
Normal file
352
libraries/ESP32-audioI2S/examples/AC101/AC101.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
AC101 - An AC101 Codec driver library for Arduino
|
||||
Copyright (C) 2019, Ivo Pullens, Emmission
|
||||
|
||||
Inspired by:
|
||||
https://github.com/donny681/esp-adf/tree/master/components/audio_hal/driver/AC101
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Febr 2021 modified by schreibfaul1 - set correct pll values
|
||||
March 2021 modified by schreibfaul1 - can handle two i2c instances
|
||||
May 2021 modified by schreibfaul1 - constructor changed
|
||||
Oct 2021 modified by schreibfaul1 - I2C wrong ACK in ReadReg
|
||||
Jan 2022 modified by schreibfaul1 - left right channel swapped
|
||||
Jan 2022 modified by schreibfaul1 - suppress compiler warning: left shift of negative value
|
||||
|
||||
examples:
|
||||
|
||||
//one I2C bus: (default behaviour)
|
||||
AC101 ac;
|
||||
ac.begin(sda, scl);
|
||||
|
||||
//two I2C busses:
|
||||
TwoWire i2cBusOne = TwoWire(0);
|
||||
TwoWire i2cBusTwo = TwoWire(1);
|
||||
AC101 ac(&i2cBusOne);
|
||||
|
||||
i2cBusOne.begin(sda, scl, 400000);
|
||||
*/
|
||||
|
||||
#include "AC101.h"
|
||||
|
||||
#define BCLK // clock over BCLK comment out: clock over MCLK
|
||||
|
||||
#define AC101_ADDR 0x1A // Device address
|
||||
|
||||
#define CHIP_AUDIO_RS 0x00
|
||||
#define PLL_CTRL1 0x01
|
||||
#define PLL_CTRL2 0x02
|
||||
#define SYSCLK_CTRL 0x03
|
||||
#define MOD_CLK_ENA 0x04
|
||||
#define MOD_RST_CTRL 0x05
|
||||
#define I2S_SR_CTRL 0x06
|
||||
#define I2S1LCK_CTRL 0x10
|
||||
#define I2S1_SDOUT_CTRL 0x11
|
||||
#define I2S1_SDIN_CTRL 0x12
|
||||
#define I2S1_MXR_SRC 0x13
|
||||
#define I2S1_VOL_CTRL1 0x14
|
||||
#define I2S1_VOL_CTRL2 0x15
|
||||
#define I2S1_VOL_CTRL3 0x16
|
||||
#define I2S1_VOL_CTRL4 0x17
|
||||
#define I2S1_MXR_GAIN 0x18
|
||||
#define ADC_DIG_CTRL 0x40
|
||||
#define ADC_VOL_CTRL 0x41
|
||||
#define HMIC_CTRL1 0x44
|
||||
#define HMIC_CTRL2 0x45
|
||||
#define HMIC_STATUS 0x46
|
||||
#define DAC_DIG_CTRL 0x48
|
||||
#define DAC_VOL_CTRL 0x49
|
||||
#define DAC_MXR_SRC 0x4C
|
||||
#define DAC_MXR_GAIN 0x4D
|
||||
#define ADC_APC_CTRL 0x50
|
||||
#define ADC_SRC 0x51
|
||||
#define ADC_SRCBST_CTRL 0x52
|
||||
#define OMIXER_DACA_CTRL 0x53
|
||||
#define OMIXER_SR 0x54
|
||||
#define OMIXER_BST1_CTRL 0x55
|
||||
#define HPOUT_CTRL 0x56
|
||||
#define SPKOUT_CTRL 0x58
|
||||
#define AC_DAC_DAPCTRL 0xA0
|
||||
#define AC_DAC_DAPHHPFC 0xA1
|
||||
#define AC_DAC_DAPLHPFC 0xA2
|
||||
#define AC_DAC_DAPLHAVC 0xA3
|
||||
#define AC_DAC_DAPLLAVC 0xA4
|
||||
#define AC_DAC_DAPRHAVC 0xA5
|
||||
#define AC_DAC_DAPRLAVC 0xA6
|
||||
#define AC_DAC_DAPHGDEC 0xA7
|
||||
#define AC_DAC_DAPLGDEC 0xA8
|
||||
#define AC_DAC_DAPHGATC 0xA9
|
||||
#define AC_DAC_DAPLGATC 0xAA
|
||||
#define AC_DAC_DAPHETHD 0xAB
|
||||
#define AC_DAC_DAPLETHD 0xAC
|
||||
#define AC_DAC_DAPHGKPA 0xAD
|
||||
#define AC_DAC_DAPLGKPA 0xAE
|
||||
#define AC_DAC_DAPHGOPA 0xAF
|
||||
#define AC_DAC_DAPLGOPA 0xB0
|
||||
#define AC_DAC_DAPOPT 0xB1
|
||||
#define DAC_DAP_ENA 0xB5
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
const uint8_t regs[] = {
|
||||
CHIP_AUDIO_RS ,
|
||||
PLL_CTRL1 ,
|
||||
PLL_CTRL2 ,
|
||||
SYSCLK_CTRL ,
|
||||
MOD_CLK_ENA ,
|
||||
MOD_RST_CTRL ,
|
||||
I2S_SR_CTRL ,
|
||||
I2S1LCK_CTRL ,
|
||||
I2S1_SDOUT_CTRL ,
|
||||
I2S1_SDIN_CTRL ,
|
||||
I2S1_MXR_SRC ,
|
||||
I2S1_VOL_CTRL1 ,
|
||||
I2S1_VOL_CTRL2 ,
|
||||
I2S1_VOL_CTRL3 ,
|
||||
I2S1_VOL_CTRL4 ,
|
||||
I2S1_MXR_GAIN ,
|
||||
ADC_DIG_CTRL ,
|
||||
ADC_VOL_CTRL ,
|
||||
HMIC_CTRL1 ,
|
||||
HMIC_CTRL2 ,
|
||||
HMIC_STATUS ,
|
||||
DAC_DIG_CTRL ,
|
||||
DAC_VOL_CTRL ,
|
||||
DAC_MXR_SRC ,
|
||||
DAC_MXR_GAIN ,
|
||||
ADC_APC_CTRL ,
|
||||
ADC_SRC ,
|
||||
ADC_SRCBST_CTRL ,
|
||||
OMIXER_DACA_CTRL ,
|
||||
OMIXER_SR ,
|
||||
OMIXER_BST1_CTRL ,
|
||||
HPOUT_CTRL ,
|
||||
SPKOUT_CTRL ,
|
||||
AC_DAC_DAPCTRL ,
|
||||
AC_DAC_DAPHHPFC ,
|
||||
AC_DAC_DAPLHPFC ,
|
||||
AC_DAC_DAPLHAVC ,
|
||||
AC_DAC_DAPLLAVC ,
|
||||
AC_DAC_DAPRHAVC ,
|
||||
AC_DAC_DAPRLAVC ,
|
||||
AC_DAC_DAPHGDEC ,
|
||||
AC_DAC_DAPLGDEC ,
|
||||
AC_DAC_DAPHGATC ,
|
||||
AC_DAC_DAPLGATC ,
|
||||
AC_DAC_DAPHETHD ,
|
||||
AC_DAC_DAPLETHD ,
|
||||
AC_DAC_DAPHGKPA ,
|
||||
AC_DAC_DAPLGKPA ,
|
||||
AC_DAC_DAPHGOPA ,
|
||||
AC_DAC_DAPLGOPA ,
|
||||
AC_DAC_DAPOPT ,
|
||||
DAC_DAP_ENA
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool AC101::WriteReg(uint8_t reg, uint16_t val)
|
||||
{
|
||||
_TwoWireInstance->beginTransmission(AC101_ADDR);
|
||||
_TwoWireInstance->write(reg);
|
||||
_TwoWireInstance->write(uint8_t((val >> 8) & 0xff));
|
||||
_TwoWireInstance->write(uint8_t(val & 0xff));
|
||||
return 0 == _TwoWireInstance->endTransmission(true);
|
||||
}
|
||||
|
||||
uint16_t AC101::ReadReg(uint8_t reg)
|
||||
{
|
||||
_TwoWireInstance->beginTransmission(AC101_ADDR);
|
||||
_TwoWireInstance->write(reg);
|
||||
_TwoWireInstance->endTransmission(false);
|
||||
|
||||
uint16_t val = 0u;
|
||||
if (2 == _TwoWireInstance->requestFrom(uint16_t(AC101_ADDR), uint8_t(2)))
|
||||
{
|
||||
val = uint16_t(_TwoWireInstance->read() << 8) + uint16_t(_TwoWireInstance->read());
|
||||
}
|
||||
_TwoWireInstance->endTransmission(true);
|
||||
return val;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
AC101::AC101( TwoWire *TwoWireInstance ){
|
||||
_TwoWireInstance = TwoWireInstance;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool AC101::begin(int32_t sda, int32_t scl, uint32_t frequency) {
|
||||
bool ok;
|
||||
if((sda >= 0) && (scl >= 0)){
|
||||
ok = _TwoWireInstance->begin(sda, scl, frequency);
|
||||
}
|
||||
else {
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// Reset all registers, readback default as sanity check
|
||||
ok &= WriteReg(CHIP_AUDIO_RS, 0x123);
|
||||
delay(100);
|
||||
ok &= 0x0101 == ReadReg(CHIP_AUDIO_RS);
|
||||
ok &= WriteReg(SPKOUT_CTRL, 0xe880);
|
||||
// Enable the PLL from 256*44.1KHz MCLK source
|
||||
ok &= WriteReg(PLL_CTRL1, 0x0141);
|
||||
uint16_t N = 48 << 4; /* 512 / (M * (2*K+1)) / (CHANNELS * WORD_SIZE) -> 512 / 3 * (2 * 16) */
|
||||
uint16_t PLL_EN = 1 << 15;
|
||||
uint16_t N_f = 0<<0; /* 0.2 N */
|
||||
ok &= WriteReg(PLL_CTRL2, N | PLL_EN | N_f);
|
||||
|
||||
// Clocking system
|
||||
uint16_t PLLCLK_ENA = 1<<15; /* 0: Disable, 1: Enable */
|
||||
#ifdef BCLK
|
||||
uint16_t PLL_CLK = 0x2 << 12; /* bclk1 */
|
||||
uint16_t I2S1CLK_SRC = 0x3<<8; /* PLL */
|
||||
#else
|
||||
uint16_t PLL_CLK = 0x0 << 12; /* MCLK1 */
|
||||
uint16_t I2S1CLK_SRC = 0x0<<8; /* MLCK1 */
|
||||
#endif
|
||||
uint16_t I2S1CLK_ENA = 1<<11; /* 0: Disable, 1: Enable */
|
||||
|
||||
uint16_t SYSCLK_ENA = 1<<3;
|
||||
ok &= WriteReg(SYSCLK_CTRL, PLLCLK_ENA|PLL_CLK| I2S1CLK_ENA|I2S1CLK_SRC|SYSCLK_ENA/*0x8b08*/);
|
||||
|
||||
ok &= WriteReg(MOD_CLK_ENA, 0x800c);
|
||||
ok &= WriteReg(MOD_RST_CTRL, 0x800c);
|
||||
|
||||
// Set default at I2S, 44.1KHz, 16bit
|
||||
ok &= SetI2sSampleRate(SAMPLE_RATE_44100);
|
||||
ok &= SetI2sClock(BCLK_DIV_8, false, LRCK_DIV_32, false);
|
||||
ok &= SetI2sMode(MODE_SLAVE);
|
||||
ok &= SetI2sWordSize(WORD_SIZE_16_BITS);
|
||||
ok &= SetI2sFormat(DATA_FORMAT_I2S);
|
||||
|
||||
// AIF config
|
||||
ok &= WriteReg(I2S1_SDOUT_CTRL, 0xc000);
|
||||
ok &= WriteReg(I2S1_SDIN_CTRL, 0xc000);
|
||||
ok &= WriteReg(I2S1_MXR_SRC, 0x2200);
|
||||
|
||||
ok &= WriteReg(ADC_SRCBST_CTRL, 0xccc4);
|
||||
ok &= WriteReg(ADC_SRC, 0x1040);
|
||||
ok &= WriteReg(ADC_DIG_CTRL, 0x8000);
|
||||
ok &= WriteReg(ADC_APC_CTRL, 0xbbc3);
|
||||
|
||||
// Path Configuration
|
||||
ok &= WriteReg(DAC_MXR_SRC, 0xcc00);
|
||||
ok &= WriteReg(DAC_DIG_CTRL, 0x8000);
|
||||
ok &= WriteReg(OMIXER_SR, 0x0081);
|
||||
ok &= WriteReg(OMIXER_DACA_CTRL, 0xf080);
|
||||
|
||||
ok &= SetMode( MODE_DAC );
|
||||
|
||||
return ok;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
void AC101::DumpRegisters() {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(regs); ++i){
|
||||
Serial.print(regs[i], HEX);
|
||||
Serial.print(" = ");
|
||||
Serial.println(ReadReg(regs[i]), HEX);
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint8_t AC101::GetVolumeSpeaker() {
|
||||
// Times 2, to scale to same range as headphone volume
|
||||
return (ReadReg(SPKOUT_CTRL) & 31) * 2;
|
||||
}
|
||||
|
||||
bool AC101::SetVolumeSpeaker(uint8_t volume) {
|
||||
// Divide by 2, as it is scaled to same range as headphone volume
|
||||
volume /= 2;
|
||||
if(volume > 31) volume = 31;
|
||||
|
||||
uint16_t val = ReadReg(SPKOUT_CTRL);
|
||||
val &= ~31;
|
||||
val |= volume;
|
||||
return WriteReg(SPKOUT_CTRL, val);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
uint8_t AC101::GetVolumeHeadphone() {
|
||||
return (ReadReg(HPOUT_CTRL) >> 4) & 63;
|
||||
}
|
||||
|
||||
bool AC101::SetVolumeHeadphone(uint8_t volume) {
|
||||
if(volume > 63) volume = 63;
|
||||
|
||||
uint16_t val = ReadReg(HPOUT_CTRL);
|
||||
val &= ~63U << 4;
|
||||
val |= volume << 4;
|
||||
return WriteReg(HPOUT_CTRL, val);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool AC101::SetI2sSampleRate(I2sSampleRate_t rate) {
|
||||
return WriteReg(I2S_SR_CTRL, rate);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool AC101::SetI2sMode(I2sMode_t mode) {
|
||||
uint16_t val = ReadReg(I2S1LCK_CTRL);
|
||||
val &= ~0x8000;
|
||||
val |= uint16_t(mode) << 15;
|
||||
return WriteReg(I2S1LCK_CTRL, val);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool AC101::SetI2sWordSize(I2sWordSize_t size) {
|
||||
uint16_t val = ReadReg(I2S1LCK_CTRL);
|
||||
val &= ~0x0030;
|
||||
val |= uint16_t(size) << 4;
|
||||
return WriteReg(I2S1LCK_CTRL, val);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool AC101::SetI2sFormat(I2sFormat_t format) {
|
||||
uint16_t val = ReadReg(I2S1LCK_CTRL);
|
||||
val &= ~0x000C;
|
||||
val |= uint16_t(format) << 2;
|
||||
return WriteReg(I2S1LCK_CTRL, val);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool AC101::SetI2sClock(I2sBitClockDiv_t bitClockDiv, bool bitClockInv, I2sLrClockDiv_t lrClockDiv, bool lrClockInv) {
|
||||
uint16_t val = ReadReg(I2S1LCK_CTRL);
|
||||
val &= ~0x7FC0;
|
||||
val |= uint16_t(bitClockInv ? 1 : 0) << 14;
|
||||
val |= uint16_t(bitClockDiv) << 9;
|
||||
val |= uint16_t(lrClockInv ? 1 : 0) << 13;
|
||||
val |= uint16_t(lrClockDiv) << 6;
|
||||
return WriteReg(I2S1LCK_CTRL, val);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
bool AC101::SetMode(Mode_t mode) {
|
||||
bool ok = true;
|
||||
if(MODE_LINE == mode) {
|
||||
ok &= WriteReg(ADC_SRC, 0x0408);
|
||||
ok &= WriteReg(ADC_DIG_CTRL, 0x8000);
|
||||
ok &= WriteReg(ADC_APC_CTRL, 0x3bc0);
|
||||
}
|
||||
|
||||
if((MODE_ADC == mode) or (MODE_ADC_DAC == mode) or (MODE_LINE == mode)) {
|
||||
ok &= WriteReg(MOD_CLK_ENA, 0x800c);
|
||||
ok &= WriteReg(MOD_RST_CTRL, 0x800c);
|
||||
}
|
||||
|
||||
if((MODE_DAC == mode) or (MODE_ADC_DAC == mode) or (MODE_LINE == mode)) {
|
||||
// Enable Headphone output
|
||||
ok &= WriteReg(OMIXER_DACA_CTRL, 0xff80);
|
||||
ok &= WriteReg(HPOUT_CTRL, 0xc3c1);
|
||||
ok &= WriteReg(HPOUT_CTRL, 0xcb00);
|
||||
delay(100);
|
||||
ok &= WriteReg(HPOUT_CTRL, 0xfbc0);
|
||||
ok &= SetVolumeHeadphone(30);
|
||||
|
||||
// Enable Speaker output
|
||||
ok &= WriteReg(SPKOUT_CTRL, 0xeabd);
|
||||
delay(10);
|
||||
ok &= SetVolumeSpeaker(30);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
166
libraries/ESP32-audioI2S/examples/AC101/AC101.h
Normal file
166
libraries/ESP32-audioI2S/examples/AC101/AC101.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
AC101 - An AC101 Codec driver library for Arduino
|
||||
Copyright (C) 2019, Ivo Pullens, Emmission
|
||||
|
||||
Inspired by:
|
||||
https://github.com/donny681/esp-adf/tree/master/components/audio_hal/driver/AC101
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AC101_H
|
||||
#define AC101_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <inttypes.h>
|
||||
#include <Wire.h>
|
||||
|
||||
class AC101
|
||||
{
|
||||
public:
|
||||
typedef enum {
|
||||
SAMPLE_RATE_8000 = 0x0000,
|
||||
SAMPLE_RATE_11052 = 0x1000,
|
||||
SAMPLE_RATE_12000 = 0x2000,
|
||||
SAMPLE_RATE_16000 = 0x3000,
|
||||
SAMPLE_RATE_22050 = 0x4000,
|
||||
SAMPLE_RATE_24000 = 0x5000,
|
||||
SAMPLE_RATE_32000 = 0x6000,
|
||||
SAMPLE_RATE_44100 = 0x7000,
|
||||
SAMPLE_RATE_48000 = 0x8000,
|
||||
SAMPLE_RATE_96000 = 0x9000,
|
||||
SAMPLE_RATE_192000 = 0xa000,
|
||||
} I2sSampleRate_t;
|
||||
|
||||
typedef enum {
|
||||
MODE_MASTER = 0x00,
|
||||
MODE_SLAVE = 0x01,
|
||||
} I2sMode_t;
|
||||
|
||||
typedef enum {
|
||||
WORD_SIZE_8_BITS = 0x00,
|
||||
WORD_SIZE_16_BITS = 0x01,
|
||||
WORD_SIZE_20_BITS = 0x02,
|
||||
WORD_SIZE_24_BITS = 0x03,
|
||||
} I2sWordSize_t;
|
||||
|
||||
typedef enum {
|
||||
DATA_FORMAT_I2S = 0x00,
|
||||
DATA_FORMAT_LEFT = 0x01,
|
||||
DATA_FORMAT_RIGHT = 0x02,
|
||||
DATA_FORMAT_DSP = 0x03,
|
||||
} I2sFormat_t;
|
||||
|
||||
typedef enum {
|
||||
BCLK_DIV_1 = 0x0,
|
||||
BCLK_DIV_2 = 0x1,
|
||||
BCLK_DIV_4 = 0x2,
|
||||
BCLK_DIV_6 = 0x3,
|
||||
BCLK_DIV_8 = 0x4,
|
||||
BCLK_DIV_12 = 0x5,
|
||||
BCLK_DIV_16 = 0x6,
|
||||
BCLK_DIV_24 = 0x7,
|
||||
BCLK_DIV_32 = 0x8,
|
||||
BCLK_DIV_48 = 0x9,
|
||||
BCLK_DIV_64 = 0xa,
|
||||
BCLK_DIV_96 = 0xb,
|
||||
BCLK_DIV_128 = 0xc,
|
||||
BCLK_DIV_192 = 0xd,
|
||||
} I2sBitClockDiv_t;
|
||||
|
||||
typedef enum {
|
||||
LRCK_DIV_16 = 0x0,
|
||||
LRCK_DIV_32 = 0x1,
|
||||
LRCK_DIV_64 = 0x2,
|
||||
LRCK_DIV_128 = 0x3,
|
||||
LRCK_DIV_256 = 0x4,
|
||||
} I2sLrClockDiv_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
MODE_ADC,
|
||||
MODE_DAC,
|
||||
MODE_ADC_DAC,
|
||||
MODE_LINE
|
||||
} Mode_t;
|
||||
|
||||
// Constructor.
|
||||
AC101(TwoWire *TwoWireInstance = &Wire);
|
||||
|
||||
// Initialize codec, using provided I2C pins and bus frequency.
|
||||
// @return True on success, false on failure.
|
||||
bool begin(int32_t sda = -1, int32_t scl = -1, uint32_t frequency = 400000);
|
||||
|
||||
// Get speaker volume.
|
||||
// @return Speaker volume, [63..0] for [0..-43.5] [dB], in increments of 2.
|
||||
uint8_t GetVolumeSpeaker();
|
||||
|
||||
// Set speaker volume.
|
||||
// @param volume Target volume, [63..0] for [0..-43.5] [dB], in increments of 2.
|
||||
// @return True on success, false on failure.
|
||||
bool SetVolumeSpeaker(uint8_t volume);
|
||||
|
||||
// Get headphone volume.
|
||||
// @return Headphone volume, [63..0] for [0..-62] [dB]
|
||||
uint8_t GetVolumeHeadphone();
|
||||
|
||||
// Set headphone volume
|
||||
// @param volume Target volume, [63..0] for [0..-62] [dB]
|
||||
// @return True on success, false on failure.
|
||||
bool SetVolumeHeadphone(uint8_t volume);
|
||||
|
||||
// Configure I2S samplerate.
|
||||
// @param rate Samplerate.
|
||||
// @return True on success, false on failure.
|
||||
bool SetI2sSampleRate(I2sSampleRate_t rate);
|
||||
|
||||
// Configure I2S mode (master/slave).
|
||||
// @param mode Mode.
|
||||
// @return True on success, false on failure.
|
||||
bool SetI2sMode(I2sMode_t mode);
|
||||
|
||||
// Configure I2S word size (8/16/20/24 bits).
|
||||
// @param size Word size.
|
||||
// @return True on success, false on failure.
|
||||
bool SetI2sWordSize(I2sWordSize_t size);
|
||||
|
||||
// Configure I2S format (I2S/Left/Right/Dsp).
|
||||
// @param format I2S format.
|
||||
// @return True on success, false on failure.
|
||||
bool SetI2sFormat(I2sFormat_t format);
|
||||
|
||||
// Configure I2S clock.
|
||||
// @param bitClockDiv I2S1CLK/BCLK1 ratio.
|
||||
// @param bitClockInv I2S1 BCLK Polarity.
|
||||
// @param lrClockDiv BCLK1/LRCK ratio.
|
||||
// @param lrClockInv I2S1 LRCK Polarity.
|
||||
// @return True on success, false on failure.
|
||||
bool SetI2sClock(I2sBitClockDiv_t bitClockDiv, bool bitClockInv, I2sLrClockDiv_t lrClockDiv, bool lrClockInv);
|
||||
|
||||
// Configure the mode (Adc/Dac/Adc+Dac/Line)
|
||||
// @param mode Operating mode.
|
||||
// @return True on success, false on failure.
|
||||
bool SetMode(Mode_t mode);
|
||||
|
||||
// Dumpt the current register configuration to serial.
|
||||
void DumpRegisters();
|
||||
|
||||
protected:
|
||||
bool WriteReg(uint8_t reg, uint16_t val);
|
||||
uint16_t ReadReg(uint8_t reg);
|
||||
private:
|
||||
TwoWire *_TwoWireInstance = NULL; // TwoWire Instance
|
||||
};
|
||||
|
||||
#endif
|
||||
85
libraries/ESP32-audioI2S/examples/AC101/main.cpp
Normal file
85
libraries/ESP32-audioI2S/examples/AC101/main.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "Arduino.h"
|
||||
#include "WiFi.h"
|
||||
#include "SPI.h"
|
||||
#include "SD.h"
|
||||
#include "FS.h"
|
||||
#include "Wire.h"
|
||||
#include "AC101.h"
|
||||
#include "Audio.h"
|
||||
|
||||
// I2S GPIOs, the names refer on AC101, AS1 Audio Kit V2.2 2379
|
||||
#define I2S_DSIN 35 // pin not used
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
#define I2S_MCLK 0
|
||||
#define I2S_DOUT 25
|
||||
|
||||
// I2C GPIOs
|
||||
#define IIC_CLK 32
|
||||
#define IIC_DATA 33
|
||||
|
||||
// amplifier enable
|
||||
#define GPIO_PA_EN 21
|
||||
|
||||
//Switch S1: 1-OFF, 2-ON, 3-ON, 4-OFF, 5-OFF
|
||||
|
||||
String ssid = "*****";
|
||||
String password = "*****";
|
||||
|
||||
static AC101 dac; // AC101
|
||||
int volume = 40; // 0...100
|
||||
|
||||
Audio audio;
|
||||
|
||||
//#####################################################################
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
Serial.begin(115200);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED){
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.printf_P(PSTR("Connected\r\nRSSI: "));
|
||||
Serial.print(WiFi.RSSI());
|
||||
Serial.print(" IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
Serial.printf("Connect to DAC codec... ");
|
||||
while (not dac.begin(IIC_DATA, IIC_CLK)){
|
||||
Serial.printf("Failed!\n");
|
||||
delay(1000);
|
||||
}
|
||||
Serial.printf("OK\n");
|
||||
|
||||
dac.SetVolumeSpeaker(volume);
|
||||
dac.SetVolumeHeadphone(volume);
|
||||
// ac.DumpRegisters();
|
||||
|
||||
// Enable amplifier
|
||||
pinMode(GPIO_PA_EN, OUTPUT);
|
||||
digitalWrite(GPIO_PA_EN, HIGH);
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT, I2S_MCLK);
|
||||
audio.setVolume(10); // 0...21
|
||||
|
||||
audio.connecttohost("http://mp3channels.webradio.antenne.de:80/oldies-but-goldies");
|
||||
// audio.connecttohost("http://dg-rbb-http-dus-dtag-cdn.cast.addradio.de/rbb/antennebrandenburg/live/mp3/128/stream.mp3");
|
||||
// audio.connecttospeech("Wenn die Hunde schlafen, kann der Wolf gut Schafe stehlen.", "de");
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void loop(){
|
||||
vTaskDelay(1);
|
||||
audio.loop();
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
|
||||
// example AUDIO RECORDER
|
||||
// starts an audio stream and records 10 seconds of it on the SD card.
|
||||
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Audio.h"
|
||||
#include "WiFiMulti.h"
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
|
||||
Audio audio;
|
||||
WiFiMulti wifiMulti;
|
||||
|
||||
//______________________________________________________________________________________________________________________________________________________________________________________________________
|
||||
// A U D I O R E C O R D E R
|
||||
//______________________________________________________________________________________________________________________________________________________________________________________________________
|
||||
|
||||
struct WAVHeader {
|
||||
char riff[4] = {'R', 'I', 'F', 'F'};
|
||||
uint32_t size;
|
||||
char wave[4] = {'W', 'A', 'V', 'E'};
|
||||
char fmt[4] = {'f', 'm', 't', ' '};
|
||||
uint32_t fmtSize = 16;
|
||||
uint16_t format = 1;
|
||||
uint16_t channels = 2;
|
||||
uint32_t sampleRate;
|
||||
uint32_t byteRate;
|
||||
uint16_t blockAlign;
|
||||
uint16_t bits;
|
||||
char data[4] = {'d', 'a', 't', 'a'};
|
||||
uint32_t dataSize;
|
||||
};
|
||||
|
||||
constexpr size_t REC_BUFFER_SIZE = 512 * 1024; // 512KB für 2-3 Sekunden Puffer
|
||||
constexpr size_t WRITE_CHUNK_SIZE = 10242; // not too big!
|
||||
constexpr size_t SD_FLUSH_INTERVAL = 65536; // Alle 64KB flush
|
||||
ps_ptr<uint8_t> rec_buffer;
|
||||
ps_ptr<uint8_t> writeBuffer;
|
||||
|
||||
class AudioRecorder {
|
||||
public:
|
||||
std::atomic<size_t> writePos{0};
|
||||
std::atomic<size_t> readPos{0};
|
||||
uint32_t totalBytes = 0;
|
||||
uint16_t sampleRate = 44100;
|
||||
uint32_t overflowCount = 0;
|
||||
|
||||
volatile bool startRequested = false;
|
||||
volatile bool stopRequested = false;
|
||||
volatile bool running = false;
|
||||
|
||||
bool push16(const int32_t* data, size_t frames) {
|
||||
// frames = Stereo-Frames
|
||||
size_t bytes16 = frames * 2 * sizeof(int16_t);
|
||||
|
||||
size_t currentWrite = writePos.load(std::memory_order_relaxed);
|
||||
size_t currentRead = readPos.load(std::memory_order_acquire);
|
||||
|
||||
size_t free = (currentRead + REC_BUFFER_SIZE - currentWrite - 1) % REC_BUFFER_SIZE;
|
||||
|
||||
if (bytes16 > free) {
|
||||
overflowCount++;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < frames * 2; i++) {
|
||||
// 32 → 16 Bit (High word)
|
||||
int32_t v = data[i] >> 16;
|
||||
|
||||
// Optional Clipping (save)
|
||||
if (v > 32767) v = 32767;
|
||||
if (v < -32768) v = -32768;
|
||||
|
||||
int16_t s = (int16_t)v;
|
||||
|
||||
// Write byte by byte (LE)
|
||||
rec_buffer[currentWrite] = s & 0xFF;
|
||||
currentWrite = (currentWrite + 1) % REC_BUFFER_SIZE;
|
||||
rec_buffer[currentWrite] = (s >> 8) & 0xFF;
|
||||
currentWrite = (currentWrite + 1) % REC_BUFFER_SIZE;
|
||||
}
|
||||
writePos.store(currentWrite, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copies data to dest, returns bytes actually read
|
||||
size_t pop(uint8_t* dest, size_t maxLen) {
|
||||
size_t currentRead = readPos.load(std::memory_order_relaxed);
|
||||
size_t currentWrite = writePos.load(std::memory_order_acquire);
|
||||
|
||||
if (currentRead == currentWrite) return 0;
|
||||
|
||||
size_t avail = (currentWrite > currentRead) ? (currentWrite - currentRead) : (REC_BUFFER_SIZE - currentRead);
|
||||
|
||||
size_t toRead = std::min(avail, maxLen);
|
||||
|
||||
// Wrap-around handling
|
||||
size_t firstChunk = std::min(toRead, REC_BUFFER_SIZE - currentRead);
|
||||
memcpy(dest, &rec_buffer[currentRead], firstChunk);
|
||||
if (toRead > firstChunk) { memcpy(dest + firstChunk, &rec_buffer[0], toRead - firstChunk); }
|
||||
|
||||
readPos.store((currentRead + toRead) % REC_BUFFER_SIZE, std::memory_order_release);
|
||||
return toRead;
|
||||
}
|
||||
|
||||
// For external access to buffers (e.g. for pop with pointer math, but not recommended)
|
||||
size_t available() {
|
||||
size_t w = writePos.load(std::memory_order_acquire);
|
||||
size_t r = readPos.load(std::memory_order_acquire);
|
||||
return (w >= r) ? (w - r) : (REC_BUFFER_SIZE - r + w);
|
||||
}
|
||||
};
|
||||
|
||||
AudioRecorder recorder;
|
||||
|
||||
void wavWriterTask(void*) {
|
||||
File file;
|
||||
WAVHeader hdr;
|
||||
bool fileOpen = false;
|
||||
|
||||
size_t writeBufferFill = 0;
|
||||
uint32_t bytesSinceFlush = 0;
|
||||
|
||||
while (true) {
|
||||
// --- START REQUEST ---
|
||||
if (recorder.startRequested && !fileOpen) {
|
||||
recorder.startRequested = false;
|
||||
|
||||
// Datei mit Zeitstempel erstellen
|
||||
char filename[64];
|
||||
snprintf(filename, sizeof(filename), "/recording.wav");
|
||||
|
||||
file = SD_MMC.open(filename, FILE_WRITE);
|
||||
if (!file) {
|
||||
Serial.println("Failed to open file! \"/recording.wav\"");
|
||||
continue;
|
||||
}
|
||||
// prepeare header
|
||||
hdr.sampleRate = recorder.sampleRate;
|
||||
hdr.byteRate = recorder.sampleRate * 2 * 2; // Stereo, 16-bit
|
||||
hdr.blockAlign = 2 * 2; // 8 bytes per frame
|
||||
hdr.bits = 16;
|
||||
hdr.dataSize = 0;
|
||||
hdr.size = 36; // 44 - 8 (RIFF header)
|
||||
|
||||
file.write((uint8_t*)&hdr, sizeof(hdr));
|
||||
recorder.totalBytes = 0;
|
||||
writeBufferFill = 0;
|
||||
bytesSinceFlush = 0;
|
||||
fileOpen = true;
|
||||
recorder.running = true;
|
||||
|
||||
Serial.printf("Recording started: %s\n", filename);
|
||||
}
|
||||
|
||||
// --- WRITE DATA ---
|
||||
if (fileOpen) {
|
||||
// fill local buffer
|
||||
while (writeBufferFill < WRITE_CHUNK_SIZE) {
|
||||
size_t spaceInLocalBuffer = WRITE_CHUNK_SIZE - writeBufferFill;
|
||||
size_t bytesRead = recorder.pop(writeBuffer + writeBufferFill, spaceInLocalBuffer);
|
||||
|
||||
if (bytesRead == 0) break; // ringbuffer is empty
|
||||
|
||||
writeBufferFill += bytesRead;
|
||||
}
|
||||
|
||||
// write full block to SD
|
||||
if (writeBufferFill >= WRITE_CHUNK_SIZE) {
|
||||
size_t written = file.write(writeBuffer.get(), WRITE_CHUNK_SIZE);
|
||||
if (written != WRITE_CHUNK_SIZE) {
|
||||
Serial.println("SD write error!");
|
||||
// Optional: error handling, hold buffer?
|
||||
}
|
||||
|
||||
recorder.totalBytes += written;
|
||||
bytesSinceFlush += written;
|
||||
writeBufferFill = 0; // buffer is empty (or move remaining data)
|
||||
|
||||
// Periodic flush for data integrity
|
||||
if (bytesSinceFlush >= SD_FLUSH_INTERVAL) {
|
||||
file.flush();
|
||||
bytesSinceFlush = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- STOP REQUEST ---
|
||||
if (recorder.stopRequested && fileOpen) {
|
||||
recorder.stopRequested = false;
|
||||
|
||||
// Write remaining data to local buffer
|
||||
if (writeBufferFill > 0) {
|
||||
file.write(writeBuffer.get(), writeBufferFill);
|
||||
recorder.totalBytes += writeBufferFill;
|
||||
}
|
||||
|
||||
// Update header
|
||||
hdr.dataSize = recorder.totalBytes;
|
||||
hdr.size = recorder.totalBytes + 36;
|
||||
|
||||
file.seek(0);
|
||||
file.write((uint8_t*)&hdr, sizeof(hdr));
|
||||
file.flush();
|
||||
file.close();
|
||||
|
||||
fileOpen = false;
|
||||
writeBufferFill = 0;
|
||||
recorder.running = false;
|
||||
Serial.printf("Recording stopped. Total bytes: %u, Overflows: %u\n", recorder.totalBytes, recorder.overflowCount);
|
||||
}
|
||||
|
||||
// Small delay to feed watchdog and release CPU
|
||||
// But not too long, so that the ring buffer does not overflow!
|
||||
vTaskDelay(pdMS_TO_TICKS(1)); // 1ms = ~176 Bytes bei 44.1kHz Stereo 32-bit
|
||||
}
|
||||
}
|
||||
|
||||
//______________________________________________________________________________________________________________________________________________________________________________________________________
|
||||
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
#define SD_MMC_D0 2
|
||||
#define SD_MMC_CLK 14
|
||||
#define SD_MMC_CMD 15
|
||||
|
||||
String ssid = "*****";
|
||||
String password = "*****";
|
||||
|
||||
uint32_t t;
|
||||
enum {IDLE, RECORDING, FINISH, PLAYBACK};
|
||||
uint8_t state = IDLE;
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
Serial.print("\n\n");
|
||||
|
||||
wifiMulti.addAP(ssid.c_str(), password.c_str());
|
||||
wifiMulti.run(); // if there are multiple access points, use the strongest one
|
||||
while (WiFi.status() != WL_CONNECTED) delay(1500);
|
||||
pinMode(SD_MMC_D0, INPUT_PULLUP);
|
||||
SD_MMC.setPins(SD_MMC_CLK, SD_MMC_CMD, SD_MMC_D0);
|
||||
SD_MMC.begin("/sdcard", true);
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(20); // default 0...21
|
||||
audio.connecttohost("http://stream.antennethueringen.de/live/aac-64/stream.antennethueringen.de/"); // aac
|
||||
|
||||
rec_buffer.alloc_array(REC_BUFFER_SIZE, "rec_buffer"); // allocate in PSRAM
|
||||
writeBuffer.alloc_array(WRITE_CHUNK_SIZE, "writeBuffer"); // allocate in PSRAM
|
||||
xTaskCreatePinnedToCore(wavWriterTask, "wavWriter", 4096, nullptr, 1, nullptr, 0); // start recorder task
|
||||
Serial.printf("recorder task started, Free heap: %u\n", ESP.getFreeHeap());
|
||||
t = millis();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
audio.loop();
|
||||
vTaskDelay(1);
|
||||
if (t + 5000 < millis() && state == IDLE && audio.isRunning()) {
|
||||
Serial.println("start recording");
|
||||
state = RECORDING;
|
||||
recorder.sampleRate = audio.getSampleRate();
|
||||
recorder.startRequested = true;
|
||||
}
|
||||
if (t + 15000 < millis() && state == RECORDING) {
|
||||
Serial.println("stop recording");
|
||||
state = FINISH;
|
||||
recorder.stopRequested = true;
|
||||
audio.stopSong();
|
||||
}
|
||||
if (t + 17000 < millis() && state == FINISH) {
|
||||
state = PLAYBACK;
|
||||
audio.connecttoFS(SD_MMC, "/recording.wav");
|
||||
}
|
||||
if (t + 29000 < millis() && state == PLAYBACK && !audio.isRunning()) {
|
||||
state = IDLE;
|
||||
audio.connecttohost("http://stream.antennethueringen.de/live/aac-64/stream.antennethueringen.de/");
|
||||
t = millis();
|
||||
}
|
||||
}
|
||||
//______________________________________________________________________________________________________________________________________________________________________________________________________
|
||||
void audio_process_raw_samples(int32_t* outBuff, int16_t validSamples) { // samples are available, write in wav file
|
||||
if (recorder.running == true) {
|
||||
recorder.push16(outBuff, validSamples);
|
||||
}
|
||||
}
|
||||
BIN
libraries/ESP32-audioI2S/examples/ES8311/ES8311_DS.pdf
Normal file
BIN
libraries/ESP32-audioI2S/examples/ES8311/ES8311_DS.pdf
Normal file
Binary file not shown.
316
libraries/ESP32-audioI2S/examples/ES8311/es8311.cpp
Normal file
316
libraries/ESP32-audioI2S/examples/ES8311/es8311.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
ES8311 - An ES8311 Codec driver library for Arduino
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
examples:
|
||||
|
||||
//one I2C bus: (default behaviour)
|
||||
ES8311 es;
|
||||
es.begin(sda, scl);
|
||||
|
||||
//two I2C busses:
|
||||
TwoWire i2cBusOne = TwoWire(0);
|
||||
TwoWire i2cBusTwo = TwoWire(1);
|
||||
ES8311 es(&i2cBusOne);
|
||||
|
||||
i2cBusOne.begin(sda, scl, 400000);
|
||||
*/
|
||||
|
||||
#include "es8311.h"
|
||||
|
||||
/* codec hifi mclk clock divider coefficients */
|
||||
static const struct _coeff_div coeff_div[] = {
|
||||
/*!<mclk rate pre_div mult adc_div dac_div fs_mode lrch lrcl bckdiv osr */
|
||||
/* 8k */
|
||||
{12288000, 8000, 0x06, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 8000, 0x03, 0x01, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x10},
|
||||
{16384000, 8000, 0x08, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 8000, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 8000, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{4096000, 8000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 8000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2048000, 8000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 8000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1024000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 11.025k */
|
||||
{11289600, 11025, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 11025, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 11025, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 12k */
|
||||
{12288000, 12000, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 12000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 12000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 16k */
|
||||
{12288000, 16000, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 16000, 0x03, 0x01, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
|
||||
{16384000, 16000, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 16000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{4096000, 16000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2048000, 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 16000, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1024000, 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 22.05k */
|
||||
{11289600, 22050, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 22050, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{705600, 22050, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 24k */
|
||||
{12288000, 24000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 24000, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 24000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 32k */
|
||||
{12288000, 32000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 32000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
|
||||
{16384000, 32000, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 32000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{4096000, 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 32000, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2048000, 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 32000, 0x03, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
{1024000, 32000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 44.1k */
|
||||
{11289600, 44100, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 44100, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 48k */
|
||||
{12288000, 48000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 48000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 48000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
|
||||
/* 64k */
|
||||
{12288000, 64000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 64000, 0x03, 0x02, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{16384000, 64000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{8192000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 64000, 0x01, 0x02, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{4096000, 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 64000, 0x01, 0x03, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
||||
{2048000, 64000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 64000, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18},
|
||||
{1024000, 64000, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
|
||||
/* 88.2k */
|
||||
{11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{5644800, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{2822400, 88200, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1411200, 88200, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
|
||||
/* 96k */
|
||||
{12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{18432000, 96000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{6144000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{3072000, 96000, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||
{1536000, 96000, 0x01, 0x03, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
||||
};
|
||||
|
||||
ES8311::ES8311(TwoWire *TwoWireInstance){
|
||||
_TwoWireInstance = TwoWireInstance;
|
||||
}
|
||||
|
||||
ES8311::~ES8311(){
|
||||
if (_TwoWireInstance != NULL) {
|
||||
_TwoWireInstance->end();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* look for the coefficient in coeff_div[] table
|
||||
*/
|
||||
int ES8311::get_coeff(uint32_t mclk, uint32_t rate){
|
||||
for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) {
|
||||
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ES8311::begin(int32_t sda, int32_t scl, uint32_t frequency) {
|
||||
bool ok = true;
|
||||
uint8_t reg = 0;
|
||||
|
||||
if((sda >= 0) && (scl >= 0)){
|
||||
ok = _TwoWireInstance->begin(sda, scl, frequency);
|
||||
_TwoWireInstance->beginTransmission(ES8311_ADDR);
|
||||
ok = (Wire.endTransmission() == 0);
|
||||
if(!ok) {
|
||||
_TwoWireInstance->end();
|
||||
log_e("ES8311 not found"); return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_e("Invalid SDA/SCL pins");
|
||||
return false;
|
||||
}
|
||||
|
||||
ok |= WriteReg(0x00, 0x1F); // Reset
|
||||
vTaskDelay(20 / portTICK_PERIOD_MS);
|
||||
ok |= WriteReg(0x00, 0x00); // Release reset
|
||||
ok |= WriteReg(0x00, 0x80); // Power on
|
||||
|
||||
ok |= WriteReg(0x01, 0x3F); // Enable all clocks
|
||||
|
||||
reg = ReadReg(0x06);
|
||||
reg &= ~BIT(5); // SCLK (BCLK) pin not inverted
|
||||
ok |= WriteReg(0x06, reg); //
|
||||
|
||||
ok |= setSampleRate(ES8311_SAMPLE_RATE48); // default
|
||||
ok |= setBitsPerSample(ES8311_BITS_PER_SAMPLE16); // default
|
||||
|
||||
ok |= WriteReg(0x0D, 0x01); // Power up analog circuitry
|
||||
ok |= WriteReg(0x0E, 0x02); // Enable analog PGA, enable ADC modulator
|
||||
ok |= WriteReg(0x12, 0x00); // Power-up DAC
|
||||
ok |= WriteReg(0x13, 0x10); // Enable output to HP drive
|
||||
ok |= WriteReg(0x1C, 0x6A); // ADC Equalizer bypass, cancel DC offset in digital domain
|
||||
ok |= WriteReg(0x37, 0x08); // Bypass DAC equalizer
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ES8311::setVolume(uint8_t volume){ // 0...100
|
||||
if (volume > 100) {volume = 100;}
|
||||
int reg32;
|
||||
if (volume == 0) {reg32 = 0;}
|
||||
else { reg32 = ((volume) * 256 / 100) - 1;}
|
||||
return WriteReg(0x32, reg32);
|
||||
}
|
||||
|
||||
uint8_t ES8311::getVolume(){
|
||||
uint8_t reg32 = ReadReg(0x32);
|
||||
uint8_t volume;
|
||||
if (reg32 == 0) {
|
||||
volume = 0;
|
||||
} else {
|
||||
volume = ((reg32 * 100) / 256) + 1;
|
||||
}
|
||||
return volume;
|
||||
}
|
||||
|
||||
bool ES8311::setSampleRate(uint32_t sample_rate){
|
||||
uint8_t reg = 0;
|
||||
bool ok = true;
|
||||
_mclk_hz = sample_rate * 256; // default MCLK frequency
|
||||
if(sample_rate > 64000) _mclk_hz /= 2;
|
||||
int coeff = get_coeff(_mclk_hz, sample_rate);
|
||||
if (coeff < 0) {log_e("Invalid sample rate %i", sample_rate); return false;}
|
||||
const struct _coeff_div *const selected_coeff = &coeff_div[coeff];
|
||||
reg = ReadReg(0x02);
|
||||
reg |= (selected_coeff->pre_div - 1) << 5;
|
||||
reg |= selected_coeff->pre_multi << 3;
|
||||
ok |= WriteReg(0x02, reg); // Set pre_div and pre_multi
|
||||
const uint8_t reg03 = (selected_coeff->fs_mode << 6) | selected_coeff->adc_osr;
|
||||
ok |= WriteReg(0x03, reg03); // Set fs_mode and adc_osr
|
||||
ok |= WriteReg(0x04, selected_coeff->dac_osr); // Set dac_osr
|
||||
const uint8_t reg05 = ((selected_coeff->adc_div - 1) << 4) | (selected_coeff->dac_div - 1);
|
||||
ok |= WriteReg(0x05, reg05); // Set adc_div and dac_div
|
||||
reg = ReadReg(0x06);
|
||||
reg &= 0xE0;
|
||||
if (selected_coeff->bclk_div < 19) {reg |= (selected_coeff->bclk_div - 1) << 0;}
|
||||
else { reg |= (selected_coeff->bclk_div) << 0;}
|
||||
ok |= WriteReg(0x06, reg); // Set bclk_div
|
||||
reg = ReadReg(0x07);
|
||||
reg &= 0xC0;
|
||||
reg |= selected_coeff->lrck_h << 0;
|
||||
ok |= WriteReg(0x07, reg); // Set lrck_h
|
||||
ok |= WriteReg(0x08, selected_coeff->lrck_l); // Set lrck_l
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ES8311::setBitsPerSample(uint8_t bps){
|
||||
uint8_t reg09 = ReadReg(0x09);
|
||||
uint8_t reg0A = ReadReg(0x0A);
|
||||
switch (bps) {
|
||||
case 16: reg09 |= (3 << 2); reg0A |= (3 << 2); break;
|
||||
case 18: reg09 |= (2 << 2); reg0A |= (2 << 2); break;
|
||||
case 20: reg09 |= (1 << 2); reg0A |= (1 << 2); break;
|
||||
case 24: reg09 |= (0 << 2); reg0A |= (0 << 2); break;
|
||||
case 32: reg09 |= (4 << 2); reg0A |= (4 << 2); break;
|
||||
default: return false; // Invalid bits per sample
|
||||
}
|
||||
bool ok = WriteReg(0x09, reg09);
|
||||
ok |= WriteReg(0x0A, reg0A);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ES8311::enableMicrophone(bool enable){
|
||||
uint8_t reg = 0x1A; // enable analog MIC and max PGA gain
|
||||
if (enable) {
|
||||
reg |= BIT(6);
|
||||
}
|
||||
bool ok = WriteReg(0x17, 0xC8); // ADC_VOLUME
|
||||
ok |= WriteReg(0x14, reg); // Enable MIC
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ES8311::setMicrophoneGain(uint8_t gain){ // 0...7
|
||||
uint8_t reg = ReadReg(0x16);
|
||||
reg &= 0xF8; // Clear gain bits
|
||||
if (gain > 7) {gain = 7;}
|
||||
reg |= gain; // Set gain bits
|
||||
bool ok = WriteReg(0x16, gain); // ADC_VOLUME
|
||||
return ok;
|
||||
}
|
||||
|
||||
uint8_t ES8311::getMicrophoneGain(){
|
||||
uint8_t reg = ReadReg(0x16);
|
||||
return (reg & 0x07); // Get gain bits
|
||||
}
|
||||
|
||||
bool ES8311::WriteReg(uint8_t reg, uint8_t val){
|
||||
_TwoWireInstance->beginTransmission(ES8311_ADDR);
|
||||
_TwoWireInstance->write(reg);
|
||||
_TwoWireInstance->write(val);
|
||||
return _TwoWireInstance->endTransmission() == 0;
|
||||
}
|
||||
|
||||
uint8_t ES8311::ReadReg(uint8_t reg){
|
||||
_TwoWireInstance->beginTransmission(ES8311_ADDR);
|
||||
_TwoWireInstance->write(reg);
|
||||
_TwoWireInstance->endTransmission(false);
|
||||
|
||||
uint8_t val = 0u;
|
||||
_TwoWireInstance->requestFrom(uint16_t(ES8311_ADDR), (uint8_t)1, true);
|
||||
if(_TwoWireInstance->available() >= 1){
|
||||
val = _TwoWireInstance->read();
|
||||
}
|
||||
_TwoWireInstance->endTransmission();
|
||||
return val;
|
||||
}
|
||||
|
||||
void ES8311::read_all(){
|
||||
for (uint8_t i = 0; i < 0x4A; i++) {
|
||||
Serial.printf("0x%02X: 0x%02X\n", i, ReadReg(i));
|
||||
}
|
||||
}
|
||||
|
||||
48
libraries/ESP32-audioI2S/examples/ES8311/es8311.h
Normal file
48
libraries/ESP32-audioI2S/examples/ES8311/es8311.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
|
||||
|
||||
#define ES8311_ADDR 0x18
|
||||
#define ES8311_SAMPLE_RATE48 48000
|
||||
#define ES8311_BITS_PER_SAMPLE16 16
|
||||
|
||||
struct _coeff_div { /* Clock coefficient structure */
|
||||
uint32_t mclk; /* mclk frequency */
|
||||
uint32_t rate; /* sample rate */
|
||||
uint8_t pre_div; /* the pre divider with range from 1 to 8 */
|
||||
uint8_t pre_multi; /* the pre multiplier with 0: 1x, 1: 2x, 2: 4x, 3: 8x selection */
|
||||
uint8_t adc_div; /* adcclk divider */
|
||||
uint8_t dac_div; /* dacclk divider */
|
||||
uint8_t fs_mode; /* double speed or single speed, =0, ss, =1, ds */
|
||||
uint8_t lrck_h; /* adclrck divider and daclrck divider */
|
||||
uint8_t lrck_l;
|
||||
uint8_t bclk_div; /* sclk divider */
|
||||
uint8_t adc_osr; /* adc osr */
|
||||
uint8_t dac_osr; /* dac osr */
|
||||
};
|
||||
|
||||
class ES8311{
|
||||
|
||||
private:
|
||||
TwoWire *_TwoWireInstance = NULL; // TwoWire Instance
|
||||
uint32_t _mclk_hz = 48000 * 256; // default MCLK frequency
|
||||
public:
|
||||
// Constructor.
|
||||
ES8311(TwoWire *TwoWireInstance = &Wire);
|
||||
~ES8311();
|
||||
bool begin(int32_t sda, int32_t scl, uint32_t frequency);
|
||||
bool setVolume(uint8_t volume);
|
||||
uint8_t getVolume();
|
||||
bool setSampleRate(uint32_t sample_rate);
|
||||
bool setBitsPerSample(uint8_t bps);
|
||||
bool enableMicrophone(bool enable);
|
||||
bool setMicrophoneGain(uint8_t gain);
|
||||
uint8_t getMicrophoneGain();
|
||||
void read_all();
|
||||
protected:
|
||||
int get_coeff(uint32_t mclk, uint32_t rate);
|
||||
bool WriteReg(uint8_t reg, uint8_t val);
|
||||
uint8_t ReadReg(uint8_t reg);
|
||||
};
|
||||
|
||||
62
libraries/ESP32-audioI2S/examples/ES8311/main.cpp
Normal file
62
libraries/ESP32-audioI2S/examples/ES8311/main.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "Arduino.h"
|
||||
#include "Audio.h"
|
||||
#include "WiFi.h"
|
||||
#include "es8311.h"
|
||||
#include "Wire.h"
|
||||
|
||||
// Pins: Waveshare ESP32-P4-Module-DEV-KIT and ESP32-P4-NANO High-performance Development Board
|
||||
#define I2S_DOUT 9 // DSDIN pin(es8311)
|
||||
#define I2S_BCLK 12 // SCLK
|
||||
#define I2S_MCLK 13
|
||||
#define I2S_LRC 10 // LRCK
|
||||
#define I2C_SCL 8
|
||||
#define I2C_SDA 7
|
||||
#define PA_ENABLE 53
|
||||
|
||||
Audio audio;
|
||||
ES8311 es;
|
||||
|
||||
String ssid = "*****";
|
||||
String password = "*****";
|
||||
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
Serial.begin(115200);
|
||||
Serial.print("\n\n");
|
||||
Serial.println("----------------------------------");
|
||||
Serial.printf("ESP32 Chip: %s\n", ESP.getChipModel());
|
||||
Serial.printf("Arduino Version: %d.%d.%d\n", ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH);
|
||||
Serial.printf("ESP-IDF Version: %d.%d.%d\n", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR, ESP_IDF_VERSION_PATCH);
|
||||
Serial.printf("ARDUINO_LOOP_STACK_SIZE %d words (32 bit)\n", CONFIG_ARDUINO_LOOP_STACK_SIZE);
|
||||
Serial.println("----------------------------------");
|
||||
Serial.print("\n\n");
|
||||
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
while (WiFi.status() != WL_CONNECTED) {delay(1500); Serial.print(".");}
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT, I2S_MCLK);
|
||||
audio.setVolume(21); // default 0...21
|
||||
|
||||
pinMode(PA_ENABLE, OUTPUT);
|
||||
digitalWrite(PA_ENABLE, HIGH);
|
||||
|
||||
if(!es.begin(I2C_SDA, I2C_SCL, 400000)) log_e("ES8311 begin failed");
|
||||
es.setVolume(50);
|
||||
es.setBitsPerSample(16);
|
||||
|
||||
// es.setSampleRate(22050);
|
||||
// es.read_all();
|
||||
// audio.connecttohost("http://www.wdr.de/wdrlive/media/einslive.m3u");
|
||||
audio.connecttohost("http://stream.antennethueringen.de/live/aac-64/stream.antennethueringen.de/"); // aac
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
audio.loop();
|
||||
vTaskDelay(1);
|
||||
}
|
||||
270
libraries/ESP32-audioI2S/examples/ES8388/ES8388.cpp
Normal file
270
libraries/ESP32-audioI2S/examples/ES8388/ES8388.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#include <Arduino.h>
|
||||
#include "ES8388.h"
|
||||
#include <Wire.h>
|
||||
|
||||
#define ES8388_ADDR 0x10
|
||||
|
||||
/* ES8388 register */
|
||||
#define ES8388_CONTROL1 0x00
|
||||
#define ES8388_CONTROL2 0x01
|
||||
#define ES8388_CHIPPOWER 0x02
|
||||
#define ES8388_ADCPOWER 0x03
|
||||
#define ES8388_DACPOWER 0x04
|
||||
#define ES8388_CHIPLOPOW1 0x05
|
||||
#define ES8388_CHIPLOPOW2 0x06
|
||||
#define ES8388_ANAVOLMANAG 0x07
|
||||
#define ES8388_MASTERMODE 0x08
|
||||
|
||||
/* ADC */
|
||||
#define ES8388_ADCCONTROL1 0x09
|
||||
#define ES8388_ADCCONTROL2 0x0a
|
||||
#define ES8388_ADCCONTROL3 0x0b
|
||||
#define ES8388_ADCCONTROL4 0x0c
|
||||
#define ES8388_ADCCONTROL5 0x0d
|
||||
#define ES8388_ADCCONTROL6 0x0e
|
||||
#define ES8388_ADCCONTROL7 0x0f
|
||||
#define ES8388_ADCCONTROL8 0x10
|
||||
#define ES8388_ADCCONTROL9 0x11
|
||||
#define ES8388_ADCCONTROL10 0x12
|
||||
#define ES8388_ADCCONTROL11 0x13
|
||||
#define ES8388_ADCCONTROL12 0x14
|
||||
#define ES8388_ADCCONTROL13 0x15
|
||||
#define ES8388_ADCCONTROL14 0x16
|
||||
|
||||
/* DAC */
|
||||
#define ES8388_DACCONTROL1 0x17
|
||||
#define ES8388_DACCONTROL2 0x18
|
||||
#define ES8388_DACCONTROL3 0x19
|
||||
#define ES8388_DACCONTROL4 0x1a
|
||||
#define ES8388_DACCONTROL5 0x1b
|
||||
#define ES8388_DACCONTROL6 0x1c
|
||||
#define ES8388_DACCONTROL7 0x1d
|
||||
#define ES8388_DACCONTROL8 0x1e
|
||||
#define ES8388_DACCONTROL9 0x1f
|
||||
#define ES8388_DACCONTROL10 0x20
|
||||
#define ES8388_DACCONTROL11 0x21
|
||||
#define ES8388_DACCONTROL12 0x22
|
||||
#define ES8388_DACCONTROL13 0x23
|
||||
#define ES8388_DACCONTROL14 0x24
|
||||
#define ES8388_DACCONTROL15 0x25
|
||||
#define ES8388_DACCONTROL16 0x26
|
||||
#define ES8388_DACCONTROL17 0x27
|
||||
#define ES8388_DACCONTROL18 0x28
|
||||
#define ES8388_DACCONTROL19 0x29
|
||||
#define ES8388_DACCONTROL20 0x2a
|
||||
#define ES8388_DACCONTROL21 0x2b
|
||||
#define ES8388_DACCONTROL22 0x2c
|
||||
#define ES8388_DACCONTROL23 0x2d
|
||||
#define ES8388_DACCONTROL24 0x2e
|
||||
#define ES8388_DACCONTROL25 0x2f
|
||||
#define ES8388_DACCONTROL26 0x30
|
||||
#define ES8388_DACCONTROL27 0x31
|
||||
#define ES8388_DACCONTROL28 0x32
|
||||
#define ES8388_DACCONTROL29 0x33
|
||||
#define ES8388_DACCONTROL30 0x34
|
||||
|
||||
bool ES8388::write_reg(uint8_t slave_add, uint8_t reg_add, uint8_t data)
|
||||
{
|
||||
Wire.beginTransmission(slave_add);
|
||||
Wire.write(reg_add);
|
||||
Wire.write(data);
|
||||
return Wire.endTransmission() == 0;
|
||||
}
|
||||
|
||||
bool ES8388::read_reg(uint8_t slave_add, uint8_t reg_add, uint8_t &data)
|
||||
{
|
||||
bool retval = false;
|
||||
Wire.beginTransmission(slave_add);
|
||||
Wire.write(reg_add);
|
||||
Wire.endTransmission(false);
|
||||
Wire.requestFrom((uint16_t)slave_add, (uint8_t)1, true);
|
||||
if (Wire.available() >= 1)
|
||||
{
|
||||
data = Wire.read();
|
||||
retval = true;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool ES8388::begin(int32_t sda, int32_t scl, uint32_t frequency)
|
||||
{
|
||||
bool res = identify(sda, scl, frequency);
|
||||
|
||||
if (res == true)
|
||||
{
|
||||
|
||||
/* mute DAC during setup, power up all systems, slave mode */
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL3, 0x04);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_CONTROL2, 0x50);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_CHIPPOWER, 0x00);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_MASTERMODE, 0x00);
|
||||
|
||||
/* power up DAC and enable LOUT1+2 / ROUT1+2, ADC sample rate = DAC sample rate */
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACPOWER, 0x3e);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_CONTROL1, 0x12);
|
||||
|
||||
/* DAC I2S setup: 16 bit word length, I2S format; MCLK / Fs = 256*/
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL1, 0x18);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL2, 0x02);
|
||||
|
||||
/* DAC to output route mixer configuration: ADC MIX TO OUTPUT */
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL16, 0x1B);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL17, 0x90);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL20, 0x90);
|
||||
|
||||
/* DAC and ADC use same LRCK, enable MCLK input; output resistance setup */
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL21, 0x80);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL23, 0x00);
|
||||
|
||||
/* DAC volume control: 0dB (maximum, unattenuated) */
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL5, 0x00);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL4, 0x00);
|
||||
|
||||
/* power down ADC while configuring; volume: +9dB for both channels */
|
||||
res &= write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0xff);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL1, 0x88); // +24db
|
||||
|
||||
/* select LINPUT2 / RINPUT2 as ADC input; stereo; 16 bit word length, format right-justified, MCLK / Fs = 256 */
|
||||
res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL2, 0xf0); // 50
|
||||
res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL3, 0x80); // 00
|
||||
res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL4, 0x0e);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL5, 0x02);
|
||||
|
||||
/* set ADC volume */
|
||||
res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL8, 0x20);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL9, 0x20);
|
||||
|
||||
/* set LOUT1 / ROUT1 volume: 0dB (unattenuated) */
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL24, 0x1e);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL25, 0x1e);
|
||||
|
||||
/* set LOUT2 / ROUT2 volume: 0dB (unattenuated) */
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL26, 0x1e);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL27, 0x1e);
|
||||
|
||||
/* power up and enable DAC; power up ADC (no MIC bias) */
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACPOWER, 0x3c);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL3, 0x00);
|
||||
res &= write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0x00);
|
||||
|
||||
/* set up MCLK) */
|
||||
#ifdef FUNC_GPIO0_CLK_OUT1
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
|
||||
#endif
|
||||
WRITE_PERI_REG(PIN_CTRL, 0xFFF0);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief (un)mute one of the two outputs or main dac output of the ES8388 by switching of the output register bits. Does not really mute the selected output, causes an attenuation.
|
||||
* hence should be used in conjunction with appropriate volume setting. Main dac output mute does mute both outputs
|
||||
*
|
||||
* @param out
|
||||
* @param muted
|
||||
*/
|
||||
void ES8388::mute(const ES8388_OUT out, const bool muted)
|
||||
{
|
||||
uint8_t reg_addr;
|
||||
uint8_t mask_mute;
|
||||
uint8_t mask_val;
|
||||
|
||||
switch (out)
|
||||
{
|
||||
case ES_OUT1:
|
||||
reg_addr = ES8388_DACPOWER;
|
||||
mask_mute = (3 << 4);
|
||||
mask_val = muted ? 0 : mask_mute;
|
||||
break;
|
||||
case ES_OUT2:
|
||||
reg_addr = ES8388_DACPOWER;
|
||||
mask_mute = (3 << 2);
|
||||
mask_val = muted ? 0 : mask_mute;
|
||||
break;
|
||||
case ES_MAIN:
|
||||
default:
|
||||
reg_addr = ES8388_DACCONTROL3;
|
||||
mask_mute = 1 << 2;
|
||||
mask_val = muted ? mask_mute : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t reg;
|
||||
if (read_reg(ES8388_ADDR, reg_addr, reg))
|
||||
{
|
||||
reg = (reg & ~mask_mute) | (mask_val & mask_mute);
|
||||
write_reg(ES8388_ADDR, reg_addr, reg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set volume gain for the main dac, or for one of the two output channels. Final gain = main gain + out channel gain
|
||||
*
|
||||
* @param out which gain setting to control
|
||||
* @param vol 0-100 (100 is max)
|
||||
*/
|
||||
void ES8388::volume(const ES8388_OUT out, const uint8_t vol)
|
||||
{
|
||||
const uint32_t max_vol = 100; // max input volume value
|
||||
|
||||
const int32_t max_vol_val = out == ES8388_OUT::ES_MAIN ? 96 : 0x21; // max register value for ES8388 out volume
|
||||
|
||||
uint8_t lreg = 0, rreg = 0;
|
||||
|
||||
switch (out)
|
||||
{
|
||||
case ES_MAIN:
|
||||
lreg = ES8388_DACCONTROL4;
|
||||
rreg = ES8388_DACCONTROL5;
|
||||
break;
|
||||
case ES_OUT1:
|
||||
lreg = ES8388_DACCONTROL24;
|
||||
rreg = ES8388_DACCONTROL25;
|
||||
break;
|
||||
case ES_OUT2:
|
||||
lreg = ES8388_DACCONTROL26;
|
||||
rreg = ES8388_DACCONTROL27;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t vol_val = vol > max_vol ? max_vol_val : (max_vol_val * vol) / max_vol;
|
||||
|
||||
// main dac volume control is reverse scale (lowest value is loudest)
|
||||
// hence we reverse the calculated value
|
||||
if (out == ES_MAIN)
|
||||
{
|
||||
vol_val = max_vol_val - vol_val;
|
||||
}
|
||||
|
||||
write_reg(ES8388_ADDR, lreg, vol_val);
|
||||
write_reg(ES8388_ADDR, rreg, vol_val);
|
||||
}
|
||||
|
||||
void ES8388::SetVolumeSpeaker(uint8_t vol) {
|
||||
vol = vol * 1.6;
|
||||
volume(ES_OUT1, vol);
|
||||
volume(ES_MAIN, 100);
|
||||
}
|
||||
|
||||
void ES8388::SetVolumeHeadphone(uint8_t vol){
|
||||
vol = vol * 1.6;
|
||||
volume(ES_OUT2, vol);
|
||||
volume(ES_MAIN, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test if device with I2C address for ES8388 is connected to the I2C bus
|
||||
*
|
||||
* @param sda which pin to use for I2C SDA
|
||||
* @param scl which pin to use for I2C SCL
|
||||
* @param frequency which frequency to use as I2C bus frequency
|
||||
* @return true device was found
|
||||
* @return false device was not found
|
||||
*/
|
||||
bool ES8388::identify(int32_t sda, int32_t scl, uint32_t frequency)
|
||||
{
|
||||
Wire.begin(sda, scl, frequency);
|
||||
Wire.beginTransmission(ES8388_ADDR);
|
||||
return Wire.endTransmission() == 0;
|
||||
}
|
||||
|
||||
27
libraries/ESP32-audioI2S/examples/ES8388/ES8388.h
Normal file
27
libraries/ESP32-audioI2S/examples/ES8388/ES8388.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
class ES8388
|
||||
{
|
||||
|
||||
bool write_reg(uint8_t slave_add, uint8_t reg_add, uint8_t data);
|
||||
bool read_reg(uint8_t slave_add, uint8_t reg_add, uint8_t &data);
|
||||
bool identify(int32_t sda, int32_t scl, uint32_t frequency);
|
||||
|
||||
public:
|
||||
bool begin(int32_t sda = -1, int32_t scl = -1, uint32_t frequency = 400000U);
|
||||
|
||||
enum ES8388_OUT
|
||||
{
|
||||
ES_MAIN, // this is the DAC output volume (both outputs)
|
||||
ES_OUT1, // this is the additional gain for OUT1
|
||||
ES_OUT2 // this is the additional gain for OUT2
|
||||
};
|
||||
|
||||
void SetVolumeSpeaker(uint8_t vol);
|
||||
void SetVolumeHeadphone(uint8_t vol);
|
||||
|
||||
void mute(const ES8388_OUT out, const bool muted);
|
||||
void volume(const ES8388_OUT out, const uint8_t vol);
|
||||
};
|
||||
|
||||
109
libraries/ESP32-audioI2S/examples/ES8388/main.cpp
Normal file
109
libraries/ESP32-audioI2S/examples/ES8388/main.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "Arduino.h"
|
||||
#include "WiFi.h"
|
||||
#include "SPI.h"
|
||||
#include "SD.h"
|
||||
#include "FS.h"
|
||||
#include "Wire.h"
|
||||
#include "ES8388.h"
|
||||
#include "Audio.h"
|
||||
|
||||
|
||||
// SPI GPIOs
|
||||
#define SD_CS 13
|
||||
#define SPI_MOSI 15
|
||||
#define SPI_MISO 2
|
||||
#define SPI_SCK 14
|
||||
|
||||
// I2S GPIOs, the names refer on ES8388, AS1 Audio Kit V2.2 3378
|
||||
#define I2S_DSIN 35 // pin not used
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 25
|
||||
#define I2S_MCLK 0
|
||||
#define I2S_DOUT 26
|
||||
|
||||
// I2C GPIOs
|
||||
#define IIC_CLK 32
|
||||
#define IIC_DATA 33
|
||||
|
||||
// buttons
|
||||
// #define BUTTON_2_PIN 13 // shared mit SPI_CS
|
||||
#define BUTTON_3_PIN 19
|
||||
#define BUTTON_4_PIN 23
|
||||
#define BUTTON_5_PIN 18 // Stop
|
||||
#define BUTTON_6_PIN 5 // Play
|
||||
|
||||
// amplifier enable
|
||||
#define GPIO_PA_EN 21
|
||||
|
||||
//Switch S1: 1-OFF, 2-ON, 3-ON, 4-OFF, 5-OFF
|
||||
|
||||
String ssid = "*****";
|
||||
String password = "*****";
|
||||
|
||||
ES8388 dac; // ES8388 (new board)
|
||||
int volume = 40; // 0...100
|
||||
|
||||
Audio audio;
|
||||
|
||||
//#####################################################################
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
Serial.begin(115200);
|
||||
Serial.println("\r\nReset");
|
||||
Serial.printf_P(PSTR("Free mem=%l\n"), ESP.getFreeHeap());
|
||||
|
||||
pinMode(SD_CS, OUTPUT);
|
||||
digitalWrite(SD_CS, HIGH);
|
||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
SPI.setFrequency(1000000);
|
||||
|
||||
SD.begin(SD_CS);
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED){
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.printf_P(PSTR("Connected\r\nRSSI: "));
|
||||
Serial.print(WiFi.RSSI());
|
||||
Serial.print(" IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
Serial.printf("Connect to DAC codec... ");
|
||||
while (not dac.begin(IIC_DATA, IIC_CLK)){
|
||||
Serial.printf("Failed!\n");
|
||||
delay(1000);
|
||||
}
|
||||
Serial.printf("OK\n");
|
||||
|
||||
dac.SetVolumeSpeaker(volume);
|
||||
dac.SetVolumeHeadphone(volume);
|
||||
// ac.DumpRegisters();
|
||||
|
||||
// Enable amplifier
|
||||
pinMode(GPIO_PA_EN, OUTPUT);
|
||||
digitalWrite(GPIO_PA_EN, HIGH);
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT, I2S_MCLK);
|
||||
audio.setVolume(10); // 0...21
|
||||
|
||||
audio.connecttohost("http://mp3channels.webradio.antenne.de:80/oldies-but-goldies");
|
||||
// audio.connecttohost("http://dg-rbb-http-dus-dtag-cdn.cast.addradio.de/rbb/antennebrandenburg/live/mp3/128/stream.mp3");
|
||||
// audio.connecttospeech("Wenn die Hunde schlafen, kann der Wolf gut Schafe stehlen.", "de");
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
void loop(){
|
||||
vTaskDelay(1);
|
||||
audio.loop();
|
||||
}
|
||||
19
libraries/ESP32-audioI2S/examples/ES9038/Readme.md
Normal file
19
libraries/ESP32-audioI2S/examples/ES9038/Readme.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# 32 bit ES9038
|
||||
|
||||
For those who have a board with a 32bit DAC
|
||||
[DJ202](https://github.com/dj202) sent us a solution:
|
||||
|
||||
|
||||
There are some great sounding and cheap 32 bit decoders available like boards with the ES9038Q2M that don't work because the library outputs 16 bits data.
|
||||
You only need to change 3 lines lines of code to change it to 32bit:
|
||||
## Audio.cpp
|
||||
in Audio::Audio
|
||||
````c++
|
||||
m_i2s_std_cfg.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO);
|
||||
in Audio::resampleTo48kStereo:
|
||||
|
||||
m_samplesBuff48K[outputIndex * 2] = clipToInt16(outLeft);
|
||||
m_samplesBuff48K[outputIndex * 2 + 1] = 0x00;
|
||||
m_samplesBuff48K[outputIndex * 2 + 2] = clipToInt16(outRight);
|
||||
m_samplesBuff48K[outputIndex * 2 + 3] = 0x00;
|
||||
````
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,59 @@
|
||||
// Copied from https://github.com/LilyGO/TTGO-TAudio/issues/12
|
||||
|
||||
|
||||
// Required Libraries (Download zips and add to the Arduino IDE library).
|
||||
#include "Arduino.h"
|
||||
#include <WM8978.h> // https://github.com/CelliesProjects/wm8978-esp32
|
||||
#include <Audio.h> // https://github.com/schreibfaul1/ESP32-audioI2S
|
||||
|
||||
// T-Audio 1.6 WM8978 I2C pins.
|
||||
#define I2C_SDA 19
|
||||
#define I2C_SCL 18
|
||||
|
||||
// T-Audio 1.6 WM8978 I2S pins.
|
||||
#define I2S_BCK 33
|
||||
#define I2S_WS 25
|
||||
#define I2S_DOUT 26
|
||||
|
||||
// T-Audio 1.6 WM8978 MCLK gpio number
|
||||
#define I2S_MCLKPIN 0
|
||||
|
||||
Audio audio;
|
||||
WM8978 dac;
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
Serial.begin(115200);
|
||||
|
||||
// Setup wm8978 I2C interface.
|
||||
if (!dac.begin(I2C_SDA, I2C_SCL)) {
|
||||
ESP_LOGE(TAG, "Error setting up dac: System halted.");
|
||||
while (1) delay(100);
|
||||
}
|
||||
|
||||
// Select I2S pins
|
||||
audio.setPinout(I2S_BCK, I2S_WS, I2S_DOUT);
|
||||
audio.i2s_mclk_pin_select(I2S_MCLKPIN);
|
||||
|
||||
// WiFi Settings here.
|
||||
WiFi.begin("EnterSSIDHere", "EnterPasswordHere");
|
||||
while (!WiFi.isConnected()) {
|
||||
delay(10);
|
||||
}
|
||||
ESP_LOGI(TAG, "Connected. Starting MP3...");
|
||||
// Enter your Icecast station URL here.
|
||||
audio.setVolume(21);
|
||||
audio.connecttohost("http://hestia2.cdnstream.com/1458_128");
|
||||
// Volume control.
|
||||
dac.setSPKvol(63); // Change volume here for board speaker output (Max 63).
|
||||
dac.setHPvol(63, 63); // Change volume here for headphone jack left, right channel.
|
||||
}
|
||||
|
||||
void loop() {
|
||||
vTaskDelay(1);
|
||||
audio.loop();
|
||||
}
|
||||
BIN
libraries/ESP32-audioI2S/examples/ESP32_TTGO-TAudio/T9V1.5.jpg
Normal file
BIN
libraries/ESP32-audioI2S/examples/ESP32_TTGO-TAudio/T9V1.5.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 271 KiB |
Binary file not shown.
92
libraries/ESP32-audioI2S/examples/Ethernet/ETH_IP101.ino
Normal file
92
libraries/ESP32-audioI2S/examples/Ethernet/ETH_IP101.ino
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "Arduino.h"
|
||||
#include "Audio.h"
|
||||
#include "ETH.h"
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
#define ETH_PHY_TYPE ETH_PHY_TLK110
|
||||
#define ETH_PHY_MDC 23
|
||||
#define ETH_PHY_MDIO 18
|
||||
#define ETH_PHY_POWER -1
|
||||
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||
#define I2S_DOUT 22
|
||||
#define I2S_BCLK 20
|
||||
#define I2S_LRC 21
|
||||
#define ETH_PHY_TYPE ETH_PHY_TLK110
|
||||
#define ETH_PHY_MDC 31
|
||||
#define ETH_PHY_MDIO 52
|
||||
#define ETH_PHY_POWER 51
|
||||
#define ETH_CLK_MODE EMAC_CLK_EXT_IN
|
||||
#endif
|
||||
|
||||
Audio audio;
|
||||
|
||||
static bool eth_connected = false;
|
||||
|
||||
void onEvent(arduino_event_id_t event) {
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_ETH_START:
|
||||
Serial.println("ETH Started");
|
||||
// The hostname must be set after the interface is started, but needs
|
||||
// to be set before DHCP, so set it from the event handler thread.
|
||||
ETH.setHostname("esp32-ethernet");
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_CONNECTED: Serial.println("ETH Connected"); break;
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
Serial.println("ETH Got IP");
|
||||
Serial.println(ETH);
|
||||
eth_connected = true;
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_LOST_IP:
|
||||
Serial.println("ETH Lost IP");
|
||||
eth_connected = false;
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||
Serial.println("ETH Disconnected");
|
||||
eth_connected = false;
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_STOP:
|
||||
Serial.println("ETH Stopped");
|
||||
eth_connected = false;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
Serial.begin(115200);
|
||||
Serial.print("A\n\n");
|
||||
Serial.println("----------------------------------");
|
||||
Serial.printf("ESP32 Chip: %s\n", ESP.getChipModel());
|
||||
Serial.printf("Arduino Version: %d.%d.%d\n", ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH);
|
||||
Serial.printf("ESP-IDF Version: %d.%d.%d\n", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR, ESP_IDF_VERSION_PATCH);
|
||||
Serial.printf("ARDUINO_LOOP_STACK_SIZE %d words (32 bit)\n", CONFIG_ARDUINO_LOOP_STACK_SIZE);
|
||||
Serial.println("----------------------------------");
|
||||
Serial.print("\n\n");
|
||||
Network.onEvent(onEvent);
|
||||
ETH.begin();
|
||||
while (!eth_connected) delay(100);
|
||||
Serial.println("ETH Connected");
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(21); // default 0...21
|
||||
audio.connecttohost("http://stream.antennethueringen.de/live/aac-64/stream.antennethueringen.de/"); // aac
|
||||
pinMode(53, OUTPUT);
|
||||
digitalWrite(53, HIGH);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
audio.loop();
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
74
libraries/ESP32-audioI2S/examples/Ethernet/ETH_W5500.ino
Normal file
74
libraries/ESP32-audioI2S/examples/Ethernet/ETH_W5500.ino
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "Arduino.h" // >= Arduino V3
|
||||
#include <ETH.h>
|
||||
#include <SPI.h>
|
||||
#include "Audio.h"
|
||||
|
||||
Audio audio;
|
||||
|
||||
#define USE_TWO_ETH_PORTS 0
|
||||
#define ETH_PHY_TYPE ETH_PHY_W5500
|
||||
|
||||
// GPIOs
|
||||
#define ETH_PHY_ADDR 1
|
||||
#define ETH_PHY_CS 3
|
||||
#define ETH_PHY_IRQ 8
|
||||
#define ETH_PHY_RST 4
|
||||
#define ETH_SPI_SCK 7
|
||||
#define ETH_SPI_MISO 6
|
||||
#define ETH_SPI_MOSI 5
|
||||
#define I2S_DOUT 12
|
||||
#define I2S_BCLK 13
|
||||
#define I2S_LRC 14
|
||||
|
||||
static bool eth_connected = false;
|
||||
|
||||
void onEvent(arduino_event_id_t event, arduino_event_info_t info) {
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_ETH_START:
|
||||
Serial.println("ETH Started");
|
||||
ETH.setHostname("esp32-eth0"); //set eth hostname here
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_CONNECTED: Serial.println("ETH Connected"); break;
|
||||
case ARDUINO_EVENT_ETH_GOT_IP: Serial.printf("ETH Got IP: '%s'\n", esp_netif_get_desc(info.got_ip.esp_netif)); Serial.println(ETH);
|
||||
eth_connected = true;
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_LOST_IP:
|
||||
Serial.println("ETH Lost IP");
|
||||
eth_connected = false;
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||
Serial.println("ETH Disconnected");
|
||||
eth_connected = false;
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_STOP:
|
||||
Serial.println("ETH Stopped");
|
||||
eth_connected = false;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
Serial.begin(115200);
|
||||
Serial.print("\n\n");
|
||||
|
||||
Network.onEvent(onEvent);
|
||||
|
||||
SPI.begin(ETH_SPI_SCK, ETH_SPI_MISO, ETH_SPI_MOSI);
|
||||
ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_CS, ETH_PHY_IRQ, ETH_PHY_RST, SPI);
|
||||
while (!eth_connected) delay(100);
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(21); // default 0...21
|
||||
audio.connecttohost("https://wdr-wdr2-ruhrgebiet.icecastssl.wdr.de/wdr/wdr2/ruhrgebiet/mp3/128/stream.mp3"); // mp3
|
||||
}
|
||||
|
||||
void loop(){
|
||||
audio.loop();
|
||||
vTaskDelay(5 /portTICK_PERIOD_MS);
|
||||
}
|
||||
88
libraries/ESP32-audioI2S/examples/Ethernet/WT32-ETH01.ino
Normal file
88
libraries/ESP32-audioI2S/examples/Ethernet/WT32-ETH01.ino
Normal file
@@ -0,0 +1,88 @@
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Audio.h"
|
||||
#include "SD.h"
|
||||
#include "FS.h"
|
||||
|
||||
// Digital I/O used
|
||||
#define SD_CS 5
|
||||
#define SPI_MOSI 2
|
||||
#define SPI_MISO 4
|
||||
#define SPI_SCK 17
|
||||
#define I2S_DOUT 12
|
||||
#define I2S_BCLK 14
|
||||
#define I2S_LRC 15
|
||||
|
||||
#define ETH_PHY_TYPE ETH_PHY_LAN8720
|
||||
#define ETH_PHY_MDC 23
|
||||
#define ETH_PHY_MDIO 18
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32P4
|
||||
#define ETH_CLK_MODE EMAC_CLK_EXT_IN
|
||||
#endif
|
||||
|
||||
#include "ETH.h"
|
||||
|
||||
Audio audio;
|
||||
|
||||
static bool eth_connected = false;
|
||||
|
||||
void onEvent(arduino_event_id_t event) {
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_ETH_START:
|
||||
Serial.println("ETH Started");
|
||||
// The hostname must be set after the interface is started, but needs
|
||||
// to be set before DHCP, so set it from the event handler thread.
|
||||
ETH.setHostname("esp32-ethernet");
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_CONNECTED: Serial.println("ETH Connected"); break;
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
Serial.println("ETH Got IP");
|
||||
Serial.println(ETH);
|
||||
eth_connected = true;
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_LOST_IP:
|
||||
Serial.println("ETH Lost IP");
|
||||
eth_connected = false;
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||
Serial.println("ETH Disconnected");
|
||||
eth_connected = false;
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_STOP:
|
||||
Serial.println("ETH Stopped");
|
||||
eth_connected = false;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
pinMode(SD_CS, OUTPUT); digitalWrite(SD_CS, HIGH);
|
||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
Serial.begin(115200);
|
||||
SD.begin(SD_CS);
|
||||
|
||||
Network.onEvent(onEvent);
|
||||
ETH.begin();
|
||||
while (!eth_connected) delay(100);
|
||||
// Eth Connected,
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(21); // default 0...21
|
||||
audio.connecttohost("https://wdr-wdr2-ruhrgebiet.icecastssl.wdr.de/wdr/wdr2/ruhrgebiet/mp3/128/stream.mp3"); // mp3
|
||||
}
|
||||
|
||||
void loop(){
|
||||
vTaskDelay(1);
|
||||
audio.loop();
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <driver/i2s_std.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
|
||||
class AudioResampleBuffer {
|
||||
public:
|
||||
static constexpr size_t FIFO_SIZE_BYTES = 16384; // Muss Vielfaches von 4 sein (Stereo, 16 Bit)
|
||||
static constexpr size_t I2S_BLOCK_SIZE = 512; // DMA Blockgröße
|
||||
|
||||
|
||||
AudioResampleBuffer()
|
||||
:fifoWrite(0), fifoRead(0), m_resampleCursor(0.0f) {
|
||||
memset(fifo, 0, sizeof(fifo));
|
||||
memset(m_inputHistory, 0, sizeof(m_inputHistory));
|
||||
}
|
||||
void setChannelHandle(i2s_chan_handle_t i2sHandle){
|
||||
m_i2s = i2sHandle;
|
||||
}
|
||||
|
||||
// Set the input samplerates (updated by the LRCK monitoring)
|
||||
void setInputSamplerate(uint32_t samplerate) {
|
||||
if (samplerate == 8000 || samplerate == 22050 || samplerate == 44100 || samplerate == 48000) {
|
||||
m_sampleRate = samplerate;
|
||||
ESP_LOGI("ResampleBuffer", "Input samplerate set to %u Hz", samplerate);
|
||||
} else {
|
||||
ESP_LOGW("ResampleBuffer", "Invalid samplerate %u Hz, defaulting to 44100 Hz", samplerate);
|
||||
m_sampleRate = 44100;
|
||||
}
|
||||
}
|
||||
|
||||
// Muss zyklisch aufgerufen werden (z. B. aus Task)
|
||||
void loopResample() {
|
||||
alignas(4) uint8_t i2sBuf[I2S_BLOCK_SIZE];
|
||||
size_t bytesRead = 0;
|
||||
if (i2s_channel_read(m_i2s, i2sBuf, I2S_BLOCK_SIZE, &bytesRead, 50) != ESP_OK || bytesRead == 0)
|
||||
return;
|
||||
|
||||
size_t inSamples = bytesRead / 4; // Stereo, 16 Bit
|
||||
int16_t* inData = reinterpret_cast<int16_t*>(i2sBuf);
|
||||
|
||||
int16_t resampled[1024];
|
||||
size_t outSamples = resampleTo441Stereo(inData, inSamples, resampled);
|
||||
size_t outBytes = outSamples * 4;
|
||||
|
||||
if (fifoFree() >= outBytes) {
|
||||
fifoWriteBytes(reinterpret_cast<uint8_t*>(resampled), outBytes);
|
||||
} else {
|
||||
vTaskDelay(100);
|
||||
// ESP_LOGW("ResampleBuffer", "FIFO voll, Daten verworfen %i Bytes", outBytes - fifoFree());
|
||||
}
|
||||
}
|
||||
|
||||
// Bluetooth Callback: muss exakt "bytes" liefern
|
||||
int32_t getData(uint8_t* data, int32_t bytes) {
|
||||
while (fifoAvailable() < static_cast<size_t>(bytes)) {
|
||||
vTaskDelay(1);
|
||||
}
|
||||
fifoReadBytes(data, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
i2s_chan_handle_t m_i2s;
|
||||
uint8_t fifo[FIFO_SIZE_BYTES];
|
||||
size_t fifoWrite;
|
||||
size_t fifoRead;
|
||||
float m_sampleRate = 44100.0f;
|
||||
|
||||
float m_resampleCursor;
|
||||
int16_t m_inputHistory[6]; // 3 Stereo-Samples
|
||||
|
||||
size_t fifoAvailable() const {
|
||||
return (fifoWrite + FIFO_SIZE_BYTES - fifoRead) % FIFO_SIZE_BYTES;
|
||||
}
|
||||
|
||||
size_t fifoFree() const {
|
||||
return FIFO_SIZE_BYTES - fifoAvailable() - 1;
|
||||
}
|
||||
|
||||
void fifoWriteBytes(const uint8_t* data, size_t len) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
fifo[fifoWrite] = data[i];
|
||||
fifoWrite = (fifoWrite + 1) % FIFO_SIZE_BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
void fifoReadBytes(uint8_t* out, size_t len) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
out[i] = fifo[fifoRead];
|
||||
fifoRead = (fifoRead + 1) % FIFO_SIZE_BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
// Catmull-Rom Spline Resampling von 48 kHz auf 44,1 kHz
|
||||
size_t resampleTo441Stereo(const int16_t* input, size_t inputSamples, int16_t* output) {
|
||||
float ratio = m_sampleRate / 44100.0f;
|
||||
float cursor = m_resampleCursor;
|
||||
|
||||
size_t extendedSamples = inputSamples + 3;
|
||||
std::vector<int16_t> extendedInput(extendedSamples * 2);
|
||||
|
||||
memcpy(&extendedInput[0], m_inputHistory, 6 * sizeof(int16_t));
|
||||
memcpy(&extendedInput[6], input, inputSamples * 2 * sizeof(int16_t));
|
||||
|
||||
size_t outputIndex = 0;
|
||||
|
||||
auto catmullRom = [](float t, float xm1, float x0, float x1, float x2) {
|
||||
return 0.5f * (
|
||||
(2.0f * x0) +
|
||||
(-xm1 + x1) * t +
|
||||
(2.0f * xm1 - 5.0f * x0 + 4.0f * x1 - x2) * t * t +
|
||||
(-xm1 + 3.0f * x0 - 3.0f * x1 + x2) * t * t * t
|
||||
);
|
||||
};
|
||||
|
||||
auto clip = [](float v) -> int16_t {
|
||||
return v > 32767.0f ? 32767 : (v < -32768.0f ? -32768 : static_cast<int16_t>(v));
|
||||
};
|
||||
|
||||
for (size_t inIdx = 1; inIdx < extendedSamples - 2; ++inIdx) {
|
||||
int16_t xm1_l = extendedInput[(inIdx - 1) * 2];
|
||||
int16_t x0_l = extendedInput[(inIdx + 0) * 2];
|
||||
int16_t x1_l = extendedInput[(inIdx + 1) * 2];
|
||||
int16_t x2_l = extendedInput[(inIdx + 2) * 2];
|
||||
|
||||
int16_t xm1_r = extendedInput[(inIdx - 1) * 2 + 1];
|
||||
int16_t x0_r = extendedInput[(inIdx + 0) * 2 + 1];
|
||||
int16_t x1_r = extendedInput[(inIdx + 1) * 2 + 1];
|
||||
int16_t x2_r = extendedInput[(inIdx + 2) * 2 + 1];
|
||||
|
||||
while (cursor < 1.0f) {
|
||||
float t = cursor;
|
||||
output[outputIndex * 2] = clip(catmullRom(t, xm1_l, x0_l, x1_l, x2_l));
|
||||
output[outputIndex * 2 + 1] = clip(catmullRom(t, xm1_r, x0_r, x1_r, x2_r));
|
||||
++outputIndex;
|
||||
cursor += ratio;
|
||||
}
|
||||
cursor -= 1.0f;
|
||||
}
|
||||
|
||||
// Historie sichern
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
size_t idx = inputSamples - 3 + i;
|
||||
m_inputHistory[i * 2] = input[idx * 2];
|
||||
m_inputHistory[i * 2 + 1] = input[idx * 2 + 1];
|
||||
}
|
||||
|
||||
m_resampleCursor = cursor;
|
||||
return outputIndex;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
**I2S Bluetooth Transmitter**
|
||||
|
||||
Such boards can be connected directly to the I2S output: <br>
|
||||
But to do this, `#define SR_48K` must be activated in Audio.h so that the I2S frequency is always 48KHz.
|
||||
|
||||

|
||||
|
||||
If you still have an old ESP32 in your box, you can use it to simulate this board. PSRAM is not required.
|
||||
|
||||
The BT transmitter is the slave and is connected in this way, the DAC serves as an analogue output, but is not necessary.
|
||||
|
||||

|
||||
|
||||
|
||||
The ESP32-A2DP library is used by P. Schatzmann, https://github.com/pschatzmann/ESP32-A2DP.git
|
||||
|
||||
As the I2S output of the audioI2S library not always outputs 44.1KHz, it is scaled internally to 44.1KHz for compatibility. 8000Hz, 22050Hz, 44100Hz and 48000Hz are possible.
|
||||
This is necessary because the ESP32 BT library expects this sample rate. This means that old BT devices can also be used.
|
||||
It doesn't matter what sample rate the audio source has.
|
||||
|
||||

|
||||
|
||||
Test circuit: the audioI2S library is running on the left, the BT transmitter on the right
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
#include "Arduino.h"
|
||||
/*
|
||||
I2S Bluetooth Transmitter
|
||||
Can be connected to an I2S Master.Sampling rate must be 48KHz
|
||||
Include PSchatzmann /ESP32-A2DP https://github.com/pschatzmann/ESP32-A2DP.git
|
||||
*/
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "esp_bt.h"
|
||||
#include "BluetoothA2DPCommon.h"
|
||||
#include "BluetoothA2DPSource.h"
|
||||
#include <driver/i2s_std.h>
|
||||
#include "AudioResampler.hpp"
|
||||
|
||||
#define RX_I2S_DIN 25 // connect with I2S Master (signal dout)
|
||||
#define RX_I2S_BCLK 27 // connect with I2S Master (bit clock)
|
||||
#define RX_I2S_LRC 26 // connect with I2S Master (word select)
|
||||
|
||||
#define MEASUREMENT_INTERVAL_MS 100 // Messintervall in ms
|
||||
#define TOLERANCE_PERCENT 5 // Toleranz für Schwankungen in %
|
||||
static volatile uint32_t lrck_count = 0;
|
||||
static uint32_t current_samplerate = 0;
|
||||
|
||||
BluetoothA2DPSource a2dp_source;
|
||||
AudioResampleBuffer resampler;
|
||||
|
||||
char BT_SINK_NAME[] = " Pebble V3\r\n"; // set your sink devicename here
|
||||
//char BT_SINK_NAME[] = "Manhattan-165327";
|
||||
|
||||
i2s_chan_handle_t i2s_rx_handle = {};
|
||||
i2s_chan_config_t i2s_chan_cfg = {}; // stores I2S channel values
|
||||
i2s_std_config_t i2s_std_cfg = {}; // stores I2S driver values
|
||||
|
||||
const i2s_port_t i2s_num = I2S_NUM_0;
|
||||
|
||||
//--------------------------------Recognise Host Samplerate-------------------------------------------------------------------------------------
|
||||
// Interrupt-Handler for LRCK-Pulse
|
||||
void IRAM_ATTR lrck_interrupt_handler(void *arg) {
|
||||
lrck_count+=1;
|
||||
}
|
||||
|
||||
// Initialize GPIO interrupt for LRCK
|
||||
void init_lrck_monitor() {
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = (1ULL << RX_I2S_LRC),
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_POSEDGE // Interrupt bei steigender Flanke
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_install_isr_service(0);
|
||||
gpio_isr_handler_add((gpio_num_t)RX_I2S_LRC, lrck_interrupt_handler, NULL);
|
||||
}
|
||||
|
||||
// calculate samplerates based on LRCK counter
|
||||
uint32_t measure_samplerate() {
|
||||
lrck_count = 0;
|
||||
vTaskDelay(MEASUREMENT_INTERVAL_MS / portTICK_PERIOD_MS);
|
||||
uint32_t samplerate = (lrck_count * 1000) / MEASUREMENT_INTERVAL_MS; // Hz
|
||||
return samplerate;
|
||||
}
|
||||
|
||||
// check whether samplerates are one of the expected values
|
||||
uint32_t map_to_valid_samplerate(uint32_t measured) {
|
||||
const uint32_t valid_rates[] = {8000, 22050, 44100, 48000};
|
||||
const uint32_t num_rates = sizeof(valid_rates) / sizeof(valid_rates[0]);
|
||||
uint32_t closest_rate = valid_rates[0];
|
||||
int min_diff = abs((int)measured - (int)valid_rates[0]);
|
||||
|
||||
for (int i = 1; i < num_rates; i++) {
|
||||
int diff = abs((int)measured - (int)valid_rates[i]);
|
||||
if (diff < min_diff) {
|
||||
min_diff = diff;
|
||||
closest_rate = valid_rates[i];
|
||||
}
|
||||
}
|
||||
|
||||
// check tolerance
|
||||
if (min_diff <= (closest_rate * TOLERANCE_PERCENT / 100)) {
|
||||
return closest_rate;
|
||||
}
|
||||
return 0; // Invalid samplerates
|
||||
}
|
||||
|
||||
// task to monitor the samplerates
|
||||
void samplerate_monitor_task(void *pvParameters) {
|
||||
init_lrck_monitor();
|
||||
|
||||
while (1) {
|
||||
uint32_t new_samplerate = map_to_valid_samplerate(measure_samplerate());
|
||||
|
||||
if (new_samplerate != 0 && new_samplerate != current_samplerate) {
|
||||
log_w("Samplerate changed to %u Hz", new_samplerate);
|
||||
resampler.setInputSamplerate(new_samplerate);
|
||||
// response to samplerates change
|
||||
if (new_samplerate == 48000) {
|
||||
log_w("Activating resampling to 44100 Hz");
|
||||
// activate resampling logic here
|
||||
} else if (new_samplerate == 44100) {
|
||||
log_w("No resampling needed");
|
||||
// deactivate Resampling, direct transmission
|
||||
} else {
|
||||
log_w("Unsupported samplerate: %u Hz", new_samplerate);
|
||||
// treat 8000 or 22050 Hz, if necessary
|
||||
}
|
||||
current_samplerate = new_samplerate;
|
||||
}
|
||||
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS); // Prüfe alle 500 ms
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
void i2s_install(){
|
||||
|
||||
i2s_chan_cfg.id = (i2s_port_t)i2s_num; // I2S_NUM_AUTO, I2S_NUM_0, I2S_NUM_1
|
||||
i2s_chan_cfg.role = I2S_ROLE_SLAVE; // I2S controller slave role, bclk and lrc signal will be set to input
|
||||
i2s_chan_cfg.dma_desc_num = 8; // number of DMA buffer
|
||||
i2s_chan_cfg.dma_frame_num = 512; // I2S frame number in one DMA buffer.
|
||||
i2s_chan_cfg.auto_clear = true; // i2s will always send zero automatically if no data to send
|
||||
i2s_new_channel(&i2s_chan_cfg, NULL, &i2s_rx_handle);
|
||||
|
||||
i2s_std_cfg.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO); // Set to enable bit shift in Philips mode
|
||||
i2s_std_cfg.gpio_cfg.bclk = (gpio_num_t)RX_I2S_BCLK; // BCLK Assignment
|
||||
i2s_std_cfg.gpio_cfg.din = (gpio_num_t)RX_I2S_DIN; // DIN Assignment
|
||||
i2s_std_cfg.gpio_cfg.dout = I2S_GPIO_UNUSED; //
|
||||
i2s_std_cfg.gpio_cfg.mclk = I2S_GPIO_UNUSED; //
|
||||
i2s_std_cfg.gpio_cfg.ws = (gpio_num_t)RX_I2S_LRC; // LRC Assignment
|
||||
i2s_std_cfg.gpio_cfg.invert_flags.mclk_inv = false;
|
||||
i2s_std_cfg.gpio_cfg.invert_flags.bclk_inv = false;
|
||||
i2s_std_cfg.gpio_cfg.invert_flags.ws_inv = false;
|
||||
i2s_std_cfg.clk_cfg.sample_rate_hz = 44800;
|
||||
i2s_std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_DEFAULT; // Select PLL_F160M as the default source clock
|
||||
i2s_std_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_128; //
|
||||
i2s_channel_init_std_mode(i2s_rx_handle, &i2s_std_cfg);
|
||||
i2s_channel_enable(i2s_rx_handle);
|
||||
}
|
||||
|
||||
//---------------------------------------------CallBacks--------------------------------------------------------------------
|
||||
|
||||
int32_t get_data(uint8_t *data, int32_t bytes) {
|
||||
return resampler.getData(data, bytes); // Holt exakt die benötigten Daten
|
||||
}
|
||||
|
||||
// gets called when button on bluetooth speaker is pressed
|
||||
void button_handler(uint8_t id, bool isReleased){
|
||||
if (isReleased) {
|
||||
Serial.print("button id ");
|
||||
Serial.print(id);
|
||||
Serial.println(" released");
|
||||
}
|
||||
}
|
||||
//---------------------------------------------SETUP--------------------------------------------------------------------
|
||||
void setup(){
|
||||
Serial.begin(115200);
|
||||
i2s_install();
|
||||
a2dp_source.set_data_callback(get_data);
|
||||
a2dp_source.set_avrc_passthru_command_callback(button_handler);
|
||||
a2dp_source.start(BT_SINK_NAME);
|
||||
resampler.setChannelHandle(i2s_rx_handle);
|
||||
xTaskCreate(samplerate_monitor_task, "samplerate_monitor", 2048, NULL, 5, NULL);
|
||||
}
|
||||
//----------------------------------------------LOOP--------------------------------------------------------------------
|
||||
void loop() {
|
||||
vTaskDelay(1);
|
||||
resampler.loopResample();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "Arduino.h"
|
||||
#include "Audio.h"
|
||||
#include "WiFi.h"
|
||||
|
||||
#define I2S_DOUT 9
|
||||
#define I2S_BCLK 3
|
||||
#define I2S_LRC 1
|
||||
|
||||
Audio audio;
|
||||
|
||||
String ssid = "*****";
|
||||
String password = "*****";
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
while (WiFi.status() != WL_CONNECTED) delay(1500);
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(12); // default 0...21
|
||||
audio.connecttospeech("Wenn die Hunde schlafen, kann der Wolf gut Schafe stehlen.", "de"); // Google TTS
|
||||
}
|
||||
|
||||
void loop() {
|
||||
audio.loop();
|
||||
vTaskDelay(1);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "Arduino.h"
|
||||
#include "Audio.h"
|
||||
#include "WiFi.h"
|
||||
|
||||
#define I2S_DOUT 9
|
||||
#define I2S_BCLK 3
|
||||
#define I2S_LRC 1
|
||||
|
||||
Audio audio;
|
||||
|
||||
String ssid = "*****";
|
||||
String password = "*****";
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
Serial.begin(115200);
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
while (WiFi.status() != WL_CONNECTED) delay(1500);
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(12); // default 0...21
|
||||
audio.openai_speech("openAI-key", "tts-1", "Today is a wonderful day to build something people love!", "", "shimer", "mp3", "1");}
|
||||
|
||||
void loop() {
|
||||
audio.loop();
|
||||
vTaskDelay(1);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//**********************************************************************************************************
|
||||
//* audioI2S-- I2S audiodecoder for ESP32, *
|
||||
//**********************************************************************************************************
|
||||
//
|
||||
// first release on 11/2018
|
||||
// Version 4 , Aug.22/2025
|
||||
//
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT.
|
||||
// FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR
|
||||
// OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||
//
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Audio.h"
|
||||
#include "SPI.h"
|
||||
#include "SD.h"
|
||||
#include "FS.h"
|
||||
|
||||
// Digital I/O used
|
||||
#define SD_CS 5
|
||||
#define SPI_MOSI 23
|
||||
#define SPI_MISO 19
|
||||
#define SPI_SCK 18
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
|
||||
Audio audio;
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
pinMode(SD_CS, OUTPUT);
|
||||
digitalWrite(SD_CS, HIGH);
|
||||
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
SPI.setFrequency(1000000);
|
||||
Serial.begin(115200);
|
||||
SD.begin(SD_CS);
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(12); // 0...21
|
||||
audio.connecttoFS(SD, "test.wav");
|
||||
}
|
||||
|
||||
void loop(){
|
||||
vTaskDelay(1);
|
||||
audio.loop();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "Arduino.h"
|
||||
#include "Audio.h"
|
||||
#include "SD_MMC.h"
|
||||
|
||||
#define I2S_DOUT 9
|
||||
#define I2S_BCLK 3
|
||||
#define I2S_LRC 1
|
||||
#define SD_MMC_D0 11
|
||||
#define SD_MMC_CLK 13
|
||||
#define SD_MMC_CMD 14
|
||||
|
||||
Audio audio;
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
Serial.begin(115200);
|
||||
pinMode(SD_MMC_D0, INPUT_PULLUP);
|
||||
SD_MMC.setPins(SD_MMC_CLK, SD_MMC_CMD, SD_MMC_D0);
|
||||
SD_MMC.begin("/sdcard", true);
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(12); // default 0...21
|
||||
audio.connecttoFS(SD_MMC, "/test.wav");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
audio.loop();
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
// optional
|
||||
void audio_info(const char *info){
|
||||
Serial.print("info "); Serial.println(info);
|
||||
}
|
||||
77
libraries/ESP32-audioI2S/examples/M5Core2/M5Core2.ino
Normal file
77
libraries/ESP32-audioI2S/examples/M5Core2/M5Core2.ino
Normal file
@@ -0,0 +1,77 @@
|
||||
//**********************************************************************************************************
|
||||
//* audioI2S-- I2S audiodecoder for M5Stack Core2 *
|
||||
//**********************************************************************************************************
|
||||
//
|
||||
// first release on May.12/2021
|
||||
//
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT.
|
||||
// FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR
|
||||
// OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||
//
|
||||
|
||||
#include <M5Core2.h>
|
||||
#include "Audio.h"
|
||||
|
||||
// Digital I/O used
|
||||
#define SD_CS 4
|
||||
#define SD_MOSI 23
|
||||
#define SD_MISO 38
|
||||
#define SD_SCK 18
|
||||
#define I2S_DOUT 2
|
||||
#define I2S_BCLK 12
|
||||
#define I2S_LRC 0
|
||||
|
||||
Audio audio;
|
||||
String ssid = "xxxxxx";
|
||||
String password = "xxxxxx";
|
||||
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
M5.begin(true, true, true, true);
|
||||
M5.Axp.SetSpkEnable(true);
|
||||
M5.Lcd.fillScreen(BLACK);
|
||||
M5.Lcd.setTextColor(WHITE);
|
||||
M5.Lcd.setTextSize(2);
|
||||
|
||||
pinMode(SD_CS, OUTPUT);
|
||||
digitalWrite(SD_CS, HIGH);
|
||||
SPI.begin(SD_SCK, SD_MISO, SD_MOSI);
|
||||
SPI.setFrequency(1000000);
|
||||
SD.begin(SD_CS);
|
||||
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(15); // 0...21
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
while (!WiFi.isConnected()) {
|
||||
delay(10);
|
||||
}
|
||||
ESP_LOGI(TAG, "Connected");
|
||||
ESP_LOGI(TAG, "Starting MP3...\n");
|
||||
|
||||
// audio.connecttoFS(SD, "/320k_test.mp3");
|
||||
// audio.connecttoFS(SD, "test.wav");
|
||||
audio.connecttohost("http://air.ofr.fm:8008/jazz/mp3/128");
|
||||
// audio.connecttospeech("Миска вареників з картоплею та шкварками, змащених салом!", "uk-UA");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
vTaskDelay(1);
|
||||
audio.loop();
|
||||
if(Serial.available()){ // put streamURL in serial monitor
|
||||
audio.stopSong();
|
||||
String r=Serial.readString();
|
||||
r.trim();
|
||||
if(r.length()>5) audio.connecttohost(r.c_str());
|
||||
log_i("free heap=%i", ESP.getFreeHeap());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
// M5Stack Node support
|
||||
// thanks to Cellie - issue #35 25.Apr.2020
|
||||
// M5Stack board with Node base also need a MCLK signal on GPIO0.
|
||||
|
||||
#include <WM8978.h> /* https://github.com/CelliesProjects/wm8978-esp32 */
|
||||
#include <Audio.h> /* https://github.com/schreibfaul1/ESP32-audioI2S */
|
||||
|
||||
/* M5Stack Node WM8978 I2C pins */
|
||||
#define I2C_SDA 21
|
||||
#define I2C_SCL 22
|
||||
|
||||
/* M5Stack Node I2S pins */
|
||||
#define I2S_BCK 5
|
||||
#define I2S_WS 13
|
||||
#define I2S_DOUT 2
|
||||
#define I2S_DIN 34
|
||||
|
||||
/* M5Stack WM8978 MCLK gpio number */
|
||||
#define I2S_MCLKPIN 0
|
||||
|
||||
WM8978 dac;
|
||||
Audio audio;
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
/* Setup wm8978 I2C interface */
|
||||
if (!dac.begin(I2C_SDA, I2C_SCL)) {
|
||||
log_e("Error setting up dac. System halted");
|
||||
while (1) delay(100);
|
||||
}
|
||||
|
||||
dac.setSPKvol(40); /* max 63 */
|
||||
dac.setHPvol(32, 32);
|
||||
|
||||
/* Setup wm8978 I2S interface */
|
||||
audio.setPinout(I2S_BCK, I2S_WS, I2S_DOUT, I2S_MCLKPIN);
|
||||
|
||||
WiFi.begin("xxx", "xxx");
|
||||
while (!WiFi.isConnected()) { delay(10); }
|
||||
|
||||
log_i("Connected\nStarting MP3...\n");
|
||||
audio.connecttohost("http://icecast.omroep.nl/3fm-bb-mp3");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
vTaskDelay(1);
|
||||
audio.loop();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//**********************************************************************************************************
|
||||
//* audioI2S-- I2S audiodecoder for M5StickC Plus and SPK HAT *
|
||||
//**********************************************************************************************************
|
||||
//
|
||||
// first release on May.12/2021
|
||||
//
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT.
|
||||
// FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR
|
||||
// OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
||||
//
|
||||
|
||||
#include <M5StickCPlus.h>
|
||||
#include "Audio.h"
|
||||
|
||||
Audio audio = Audio(true);
|
||||
|
||||
String ssid = "xxxxxxxx";
|
||||
String password = "xxxxxxxx";
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
M5.begin(false); // Lcd disabled to reduce noise
|
||||
M5.Axp.ScreenBreath(1); // Lower Lcd backlight
|
||||
pinMode(36, INPUT);
|
||||
gpio_pulldown_dis(GPIO_NUM_25);
|
||||
gpio_pullup_dis(GPIO_NUM_25);
|
||||
M5.Beep.tone(44100); // Built-in buzzer tone
|
||||
M5.Beep.end(); // disabled
|
||||
|
||||
audio.setVolume(15); // 0...21
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
while (!WiFi.isConnected()) { delay(10); }
|
||||
ESP_LOGI(TAG, "Connected");
|
||||
ESP_LOGI(TAG, "Starting MP3...\n");
|
||||
|
||||
audio.connecttohost("http://air.ofr.fm:8008/jazz/mp3/128");
|
||||
// audio.connecttospeech("Миска вареників з картоплею та шкварками, змащених салом!", "uk-UA");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
vTaskDelay(1);
|
||||
audio.loop();
|
||||
if(Serial.available()){ // put streamURL in serial monitor
|
||||
audio.stopSong();
|
||||
String r=Serial.readString();
|
||||
r.trim();
|
||||
if(r.length()>5) audio.connecttohost(r.c_str());
|
||||
log_i("free heap=%i", ESP.getFreeHeap());
|
||||
}
|
||||
}
|
||||
44
libraries/ESP32-audioI2S/examples/biquad/biquad.py
Normal file
44
libraries/ESP32-audioI2S/examples/biquad/biquad.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import numpy as np
|
||||
# import matplotlib
|
||||
# matplotlib.use("TkAgg") # oder "QtAgg", falls Qt installiert ist
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.signal import freqz
|
||||
|
||||
# =========================
|
||||
# PARAMETER
|
||||
# =========================
|
||||
fs = 44100 # Samplerate
|
||||
N = 8192 # FFT-Auflösung
|
||||
|
||||
# Liste von Biquads (Reihenfolge = Signalfluss)
|
||||
biquads = [
|
||||
([1.026721, -1.919612, 0.901093], [1.0, -1.922258, 0.925168]), # LOWSHELF
|
||||
([0.802626, -1.424665, 0.670208 ], [1.0, -1.424665, 0.472834 ]), # PEAKINGEQ
|
||||
([2.092489, -2.262849, 0.808592], [1.0, -0.597000, 0.235232]), # HIGHSHELF
|
||||
]
|
||||
|
||||
w = np.linspace(0, np.pi, N)
|
||||
H = np.ones_like(w, dtype=complex)
|
||||
|
||||
for b, a in biquads:
|
||||
_, h = freqz(b, a, worN=w)
|
||||
H *= h
|
||||
|
||||
f = w * fs / (2*np.pi)
|
||||
mag_db = 20 * np.log10(np.abs(H) + 1e-12)
|
||||
|
||||
# =========================
|
||||
# PLOT
|
||||
# =========================
|
||||
plt.figure(figsize=(9,5))
|
||||
plt.semilogx(f, mag_db)
|
||||
plt.xlim(20, fs/2)
|
||||
plt.ylim(-15, 15)
|
||||
plt.grid(True, which='both')
|
||||
plt.xlabel("Frequency (Hz)")
|
||||
plt.ylabel("Amplitude (dB)")
|
||||
plt.title("Biquad frequency response")
|
||||
plt.tight_layout()
|
||||
# plt.show()
|
||||
plt.savefig("biquad_response.png", dpi=150)
|
||||
print("Plot gespeichert als biquad_response.png")
|
||||
BIN
libraries/ESP32-audioI2S/examples/biquad/biquad_response.png
Normal file
BIN
libraries/ESP32-audioI2S/examples/biquad/biquad_response.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -0,0 +1,98 @@
|
||||
#include "Arduino.h"
|
||||
#include "Audio.h"
|
||||
#include "SD_MMC.h"
|
||||
#include "FS.h"
|
||||
#include <vector>
|
||||
|
||||
#define I2S_LRC 26
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
|
||||
#define SD_MMC_D0 2
|
||||
#define SD_MMC_CLK 14
|
||||
#define SD_MMC_CMD 15
|
||||
|
||||
void listDir(fs::FS &fs, const char * dirname, uint8_t levels); //proto
|
||||
std::vector<char*> v_audioContent;
|
||||
int pirPin = 4;
|
||||
Audio audio;
|
||||
|
||||
File dir;
|
||||
const char audioDir[] = "/mp3";
|
||||
|
||||
void my_audio_info(Audio::msg_t m) {
|
||||
Serial.printf("%s: %s\n", m.s, m.msg);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Audio::audio_info_callback = my_audio_info;
|
||||
Serial.begin(115200);
|
||||
pinMode(SD_MMC_D0, INPUT_PULLUP);
|
||||
SD_MMC.setPins(SD_MMC_CLK,SD_MMC_CMD, SD_MMC_D0);
|
||||
if(!SD_MMC.begin( "/sdmmc", true, false, 20000)){
|
||||
Serial.println("Card Mount Failed");
|
||||
return;
|
||||
}
|
||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
||||
audio.setVolume(17); // 0...21 Will need to add a volume setting in the app
|
||||
dir = SD_MMC.open(audioDir);
|
||||
listDir(SD_MMC, audioDir, 1);
|
||||
if(v_audioContent.size() > 0){
|
||||
const char* s = (const char*)v_audioContent[v_audioContent.size() -1];
|
||||
Serial.printf("playing %s\n", s);
|
||||
audio.connecttoFS(SD_MMC, s);
|
||||
v_audioContent.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void loop(){
|
||||
audio.loop();
|
||||
vTaskDelay(1); // Audio is distoreted without this
|
||||
}
|
||||
|
||||
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
|
||||
Serial.printf("Listing directory: %s\n", dirname);
|
||||
|
||||
File root = fs.open(dirname);
|
||||
if(!root){
|
||||
Serial.println("Failed to open directory");
|
||||
return;
|
||||
}
|
||||
if(!root.isDirectory()){
|
||||
Serial.println("Not a directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while(file){
|
||||
if(file.isDirectory()){
|
||||
Serial.print(" DIR : ");
|
||||
Serial.println(file.name());
|
||||
if(levels){
|
||||
listDir(fs, file.path(), levels -1);
|
||||
}
|
||||
} else {
|
||||
Serial.print(" FILE: ");
|
||||
Serial.print(file.name());
|
||||
Serial.print(" SIZE: ");
|
||||
Serial.println(file.size());
|
||||
v_audioContent.insert(v_audioContent.begin(), strdup(file.path()));
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
Serial.printf("num files %i", v_audioContent.size());
|
||||
root.close();
|
||||
file.close();
|
||||
}
|
||||
|
||||
void vector_clear_and_shrink(vector<char*>&vec){
|
||||
uint size = vec.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if(vec[i]){
|
||||
free(vec[i]);
|
||||
vec[i] = NULL;
|
||||
}
|
||||
}
|
||||
vec.clear();
|
||||
vec.shrink_to_fit();
|
||||
}
|
||||
Reference in New Issue
Block a user