Skip to content

Audio Output

Phil Schatzmann edited this page Oct 19, 2023 · 5 revisions

Audio Output

We use the AudioTools library to define the output devices. This logic is used when A2DP_I2S_AUDIOTOOLS is set to true in config.h

A Simple I2S Example (A2DP Sink)

#include "BluetoothA2DPSink.h"

I2SStream out;
BluetoothA2DPSink a2dp_sink(out);

void setup() {
    auto cfg = out.defaultConfig();
    config.pin_bck = 26;
    config.pin_ws = 25;
    config.pin_data = 22;

    out.begin(cfg); 
    a2dp_sink.start("MyMusic");
}

void loop() {
    delay(100);
}

Here is the detailed information about the I2S config API

Output to the Internal DAC

#include "BluetoothA2DPSink.h"

AnalogAudioStream out;
BluetoothA2DPSink a2dp_sink(out);

void setup() {
    auto cfg = out.defaultConfig();
    out.begin(cfg); 
    a2dp_sink.start("MyMusic");
}

void loop() {
    delay(100);
}

The output goes now to the DAC pins GPIO25 (Channel 1) and GPIO26 (Channel 2).

Legacy Audio Output

This logic is used when A2DP_I2S_AUDIOTOOLS is set to false in config.h

A Simple I2S Example (A2DS Sink) using default Pins

Here is the simplest example which just uses the proper default settings:

#include "BluetoothA2DPSink.h"

BluetoothA2DPSink a2dp_sink;

void setup() {
    a2dp_sink.start("MyMusic");
}

void loop() {
}

This creates a new Bluetooth device with the name “MyMusic” and the output will be sent to the following default I2S pins which need to be conected to an external DAC:

  • bck_io_num = 26
  • ws_io_num = 25
  • data_out_num = 22

Defining Pins

You can define your own pins easily by calling the set_pin_config method in the setup before the start.

#include "BluetoothA2DPSink.h"

BluetoothA2DPSink a2dp_sink;

void setup() {
    i2s_pin_config_t my_pin_config = {
        .mck_io_num = I2S_PIN_NO_CHANGE,
        .bck_io_num = 26,
        .ws_io_num = 25,
        .data_out_num = 22,
        .data_in_num = I2S_PIN_NO_CHANGE
    };
    a2dp_sink.set_pin_config(my_pin_config);
    a2dp_sink.start("MyMusic");
}

void loop() {
}

Using your specific i2s_config

In some cases you might want to use your specific i2s_config settings. E.g. to request a different bits_per_sample (e.g. 32) or to use the use_apll or to optimize the dma buffer...

#include "BluetoothA2DPSink.h"

BluetoothA2DPSink a2dp_sink;

void setup() {

    static i2s_config_t i2s_config = {
      .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX),
      .sample_rate = 44100, // updated automatically by A2DP
      .bits_per_sample = (i2s_bits_per_sample_t)32,
      .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
      .communication_format = (i2s_comm_format_t) (I2S_COMM_FORMAT_STAND_I2S),
      .intr_alloc_flags = 0, // default interrupt priority
      .dma_buf_count = 8,
      .dma_buf_len = 64,
      .use_apll = true,
      .tx_desc_auto_clear = true // avoiding noise in case of data unavailability
  };
  a2dp_sink.set_i2s_config(i2s_config);
  a2dp_sink.start("MyMusic");
}

void loop() {
}

Output to the Internal DAC

You can also send the output directly to the internal DAC of the ESP32 by providing the corresponding i2s_config:

#include "BluetoothA2DPSink.h"

BluetoothA2DPSink a2dp_sink;

void setup() {
    static const i2s_config_t i2s_config = {
        .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
        .sample_rate = 44100, // corrected by info from bluetooth
        .bits_per_sample = (i2s_bits_per_sample_t) 16, /* the DAC module will only take the 8bits from MSB */
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
        .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_MSB,
        .intr_alloc_flags = 0, // default interrupt priority
        .dma_buf_count = 8,
        .dma_buf_len = 64,
        .use_apll = false
    };

    a2dp_sink.set_i2s_config(i2s_config);
    a2dp_sink.start("MyMusic");

}

void loop() {
}

The output goes now to the DAC pins GPIO25 (Channel 1) and GPIO26 (Channel 2).