Skip to content
Wolle edited this page Feb 3, 2021 · 45 revisions

audioI2S FAQ

What is this?

The library enables decoding of MP3 and AAC compression and plays 8bit or 16bit wav files. The audio data can come from the Internet, SD card or SPIFFS. Many radio stations can be heard. Playlists are unpacked and a connection to the (first) URL is established, formats are * .pls, * .m3u and * .asx. SSL connections are possible. Google's TTS can be used.

Examples:

connecttohost("http://online.rockarsenal.ru:8000/rockarsenal_aacplus");
connecttospeech("Wenn die Hunde schlafen, kann der Wolf gut Schafe stehlen.", "de");
connecttoSD("click.mp3");
connecttoFS(SD, "/wave_test/Wav_868kb.wav");
connecttoFS(SPIFFS, "wobble.mp3");

Stations can be received up to 320Kbit/s. A good connection is a prerequisite for this. Many, but not every, station that runs smoothly in the VLC player works on the ESP32 without dropouts. Shortly before the input buffer is empty, this message appears in the serial monitor slow stream, dropouts are possible If the connection is lost, the library tries to re-establish the connection.


Which external DACs can be used?

Basically all 16 bit DACs that have the pins DIN, BLCK and LRC. The PCM5102A delivers good results. Most GPIOs can be used

setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT); 

Also ESP32-A1S can be used; the library https://github.com/Yveaux/AC101 can be integrated for this purpose. See the examples folder https://github.com/schreibfaul1/ESP32-audioI2S/tree/master/examples/ESP32-A1S MCLK is required in certain cases (only support GPIO0/GPIO1/GPIO3).

i2s_mclk_pin_select(const uint8_t pin)

Is it also possible without an external DAC?

If the 8-bit sound is enough, you can do that.

...
Audio audio;
...
void setup() {
...
    audio.setInternalDAC(true);
...
}

The analog audio signal is then on GPIO25 and GPIO 26.


Can the Arduino IDE be used?

Yes, the library can be downloaded as a zip file. The installation in the Arduino IDE runs via the library manager

Arduino IDE Library

Tip: Use the partition scheme 'Huge App' so that there is enough memory for your own extensions

Arduino IDE Partition Scheme


What about PSRAM?

If available, PSRAM can be used. PSRAM is recognized automatically. The input buffer is then automatically relocated and enlarged.

without PSRAM, inputBufferSize is about 6.25KBytes

without PSRAM

with PSRAM, inputBufferSize is about 29KBytes

with PSRAM

8000 or 30000 bytes are allocated, part of which is used internally to avoid copying the audio data during operation


How to adjust the balance and volume?

Internally, the volume is divided into 64 steps. With setVolume () the volume is set in 22 steps. Internally, the 22 steps are assigned to the 64 volume steps via a table. This creates a logarithmic curve. The balance attenuates the left or right channel (values between -16 ...16).

setBalance(-16); // mutes the left channel
setVolume(21);  // max loudness

How To Change Bass And Treble?

Yes, that is possible. There are built-in IIR filters.

setTone(uint8_t l_type, uint16_t l_freq, uint8_t r_type, uint16_t r_freq){
    // see https://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/
    // l_ left channel, r_ right channel
    // type: 0- lowpass, 1- highpass, 2 - lowshelf, 3 - highshelf
    // freq: frequency in Hz (between 10....samlerate/2), freq =0 - no filter
    // low/highpass have Qfactor = 0.707, low/highshelf have Gain -5dB

The filter type and the nominal frequency can be selected for each channel. High and low pass filters have a quality factor of 0.707, high and lowshelf a gain of -5dB. The filter effect can be evaluated graphically here:

lowpass

Example code, changes every 10s between lowpass (2KHz), highpass (2KHz) and no filter

Ticker ticker;

void setup(){
    ...
  ticker.attach(10, tcr10s);
    ...
}


void tcr10s(){  // Timer all 10 seconds
    static uint8_t i = 0;
    if(i==0) {
       log_i("lowpass");
       audio.setTone(0, 2000, 0, 2000);
       i++; return;
    }
    if(i==1) {
        log_i("highpass");
        audio.setTone(1, 2000, 1, 2000);
        i++; return;
    }
    if(i==2) {
        log_i("no filter");
        audio.setTone(0, 0, 0, 0);
        i = 0;
    }
    return;
}

The server requires access data

audio_info: authentification failed, wrong credentials?

The name and password can be transferred when the destination is called, use:

connecttohost("http://xxxx", "name", "password");

What audio events are there?

The events are weakly integrated by the compiler. This means they can, but do not have to be used.

audio_info outputs the current status, suitable for debugging and troubleshooting

void audio_info(const char *info)

audio_id3data many mp3 files contain information about artists, albums or bands. The data is read from the file and can be used further via this event

void audio_id3data(const char *info)

audio_eof_mp3 is called after the end of an audio file. * info contains the file name. With this event it is possible to create playlists

void audio_eof_mp3(const char *info)

Playlist example:

void audio_eof_mp3(const char *info){  //end of file
    Serial.print("audio_info: "); Serial.println(info);
    static int i=0;
    if(i==0) audio.connecttoSD("/wave_test/If_I_Had_a_Chicken.mp3");
    if(i==1) audio.connecttoSD("/wave_test/test_8bit_stereo.wav");
    if(i==2) audio.connecttoSD("/wave_test/test_16bit_mono.wav");
    i++;
    if(i==3) i=0;
}

audio_eof_stream the same as audio_eof_mp3 for podcasts or files transferred from the server

void audio_eof_stream(const char *info)

audio_showstation many radio stations provide their names at the beginning of the connection. This can be used in your own applications

void audio_showstation(const char *info)

audio_showstreamtitle if the radio station transmits information about the artist, music track ... in its metadata, this event is called

void audio_showstreamtitle(const char *info)

audio_bitrate returns the current bit rate as text

void audio_bitrate(const char *info)

audio_commercial commercials are often played at the beginning of the broadcast (and during the program). Info contains the expected duration of the advertisement. So the sound can be switched off for the time being

void audio_commercial(const char *info)

audio_icyurl if the station has a homepage this is called here

void audio_icyurl(const char *info)

audio_lasthost the URL that is called via connecttohost does not have to be the current URL. Sometimes it is redirected to another URL that can be read out here.

void audio_lasthost(const char *info)

audio_end_of_speech is called after the voice transmission ends [using connecttospeech(...)]

void audio_eof_speech(const char *info)

audio_id3image mp3 files can contain pictures. Here the pointer to the current mp3 file, the position of the beginning of the picture and the size is transmitted

void audio_id3image(File& file, const size_t pos, const size_t size)

What else is there?

There are other useful functions for building MP3 players, for example

getAudioFileDuration() Indicates the expected length of an audio file in seconds. With a constant bit rate, CBR, the value is exact, with a variable bit rate, VBR, the duration is estimated based on the first 100 mp3 frames and can therefore deviate slightly from the actual playback time

uint32_t getAudioFileDuration()

getAudioCurrentTime() returns the current playing time in seconds

uint32_t getAudioCurrentTime()

An example program could look like this:

Ticker ticker;
void setup()  {
	...
	ticker.attach(1, tcr1s);
	...
}

void tcr1s(){
    uint32_t act=audio.getAudioCurrentTime();
    uint32_t afd=audio.getAudioFileDuration();
    uint32_t pos =audio.getFilePos();
    log_i("pos =%i", pos);
    log_i("audioTime: %i:%02d - duration: %i:%02d", (act/60), (act%60) , (afd/60), (afd%60));
}

The output in the serial monitor

Audio Duration

Sometimes you want to play an audio file in a loop.
setFileLoop() the position is determined internally after the audio header. At the end of the file there is a jump to the audio start position

bool setFileLoop(true);

In some projects there is only one audio amplifier or speaker. Then it makes sense to convert the stereo signal into a mono signal. With forceMono(true); the mean value is calculated from the signal of both channels and placed on the left and right channel.

void forceMono(true);  // change stereo to mono
void forceMono(false); // default stereo will be played

A simple project to receive a webstream

Here is a simple program example, you need an ESP32 developer board and an external DAC (e.g. PCM5102A)

#include "Arduino.h"
#include "WiFi.h"
#include "Audio.h"

#define I2S_DOUT      26  // connect to DAC pin DIN
#define I2S_BCLK      27  // connect to DAC pin BCK
#define I2S_LRC       25  // connect to DAC pin LCK

Audio audio;

const char* ssid =     "SSID";
const char* password = "password";

void setup() {
    Serial.begin(115200);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) delay(1500);
    audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
    audio.connecttohost("http://s1.knixx.fm/dein_webradio_64.aac"); // 64 kbp/s aac+
}

void loop() {
    audio.loop();
}

void audio_info(const char *info){
    Serial.print("info        "); Serial.println(info);
}

The output in the serial monitor:

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:10944
load:0x40080400,len:6388
entry 0x400806b4
info        PSRAM not found, inputBufferSize = 6399 bytes
info        buffers freed, free Heap: 228148 bytes
info        Connect to new host: "http://s1.knixx.fm/dein_webradio_64.aac"
info        Connect to "s1.knixx.fm" on port 80, extension "/dein_webradio_64.aac"
info        Connected to server
info        Server: nginx/1.14.2
info        audio/aac seen.
info        format is aac
info        AACDecoder has been initialized, free Heap: 199916 bytes
info        chunked data transfer
info        Connection: close
info        ice-audio-info: channels=2;samplerate=44100;bitrate=64
info        icy-description: Wir spielen Musik von den 60ern bis Heute! Und immer um halb aktuelle Country-Music.
info        icy-genre: variety,pop,oldies,country
info        icy-name: knixx.fm - Dein Webradio. / 64 kbp/s aac+
info        icy-pub: 1
info        icy-url: https://knixx.fm
info        Cache-Control: no-cache, no-store
info        Access-Control-Allow-Origin: *
info        Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type
info        Access-Control-Allow-Methods: GET, OPTIONS, HEAD
info        Expires: Mon, 26 Jul 1997 05:00:00 GMT
info        X-Frame-Options: SAMEORIGIN
info        X-Content-Type-Options: nosniff
info        Switch to DATA, bitrate is 64000, metaint is 4096
info        inputbuffer is being filled
info        StreamTitle="Michael Bolton - Soul Provider -- 1989"
info        stream ready
info        buffer filled in 7 ms
info        syncword found at pos 0
info        AAC Channels=1
info        AAC SampleRate=22050
info        AAC BitsPerSample=16
info        AAC Bitrate=64000
info        StreamTitle="Symbol - The Most Beautiful Girl In The World -- 1994"

building it on a breadboard:

Simple_Project

the schematic:

Simple_Project Schematic


Who wants to build a simple internet radio

There are displays for the Raspberry Pi with a resolution of 480x320 pixels and an SPI bus. These are particularly suitable, see the radio folder

Simple_WiFi_Radio

Clone this wiki locally