ESP32 audio output

megabite
Posts: 11
Joined: Wed Nov 22, 2017 12:56 pm

ESP32 audio output

Postby megabite » Wed Nov 22, 2017 1:02 pm

Hello,

I'm new on this forum and would like some info on ESP32 boards that I haven't been able to gather conclusively from the specs of most boards.

For an in-car trip computer project, I would like to use an ESP32 board to display (animated) menus on a small TFT screen in the dashboard. Haven't bought one yet though.

Ideally, I would like to be able to play sounds as well, for example when my trip computer shows a "low fuel" or "high oil temperature" warning.

Do ESP32 WROOM boards or similar have the capability to connect a subminiature speaker straight to the board and play sounds? Or what other hardware is needed to make that happen?




Cheers,

megabite

ESP_Sprite
Posts: 1659
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP32 audio output

Postby ESP_Sprite » Thu Nov 23, 2017 9:08 am

The ESP32 has a built-in 8-bit DAC; if you don't mind the not-really-HiFi quality of that you should be able to get away with an audio amp connected to this. Alternatively, for better sound quality, you can use an external I2S codec and amp.

megabite
Posts: 11
Joined: Wed Nov 22, 2017 12:56 pm

Re: ESP32 audio output

Postby megabite » Fri Nov 24, 2017 2:31 pm

My initial idea was to just have a 0.6'' 0.8W subminiature speaker mounted to it. Which should be fine playing 8-bit sound samples.

Then again, as I've seen on the web, there are I2S amps that can drive a somewhat bigger speaker.

If I connect an amp like this one to my ESP32,

https://www.adafruit.com/product/3006

then what could I drive with it? Could I even hook it up to the car's own sound system speakers, or would that damage the amp as the speakers normally run on 12V coming from the radio?

megabite
Posts: 11
Joined: Wed Nov 22, 2017 12:56 pm

Re: ESP32 audio output

Postby megabite » Sat Mar 03, 2018 1:18 pm

Ok, so I've found some example code that should in theory enable me to output sound via the Adafruit I2S amp. Note that this is for the Arduino IDE:

Code: Select all

#include "driver/i2s.h"
#include "freertos/queue.h"
#include <pgmspace.h>

#include "soundsample.h"


//i2s configuration
int i2s_num = 0; // i2s port number
i2s_config_t i2s_config = {
  .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
  .sample_rate = 16000,
  .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
  .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
  .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
  .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority
  .dma_buf_count = 8,
  .dma_buf_len = 64   //Interrupt level 1
};

i2s_pin_config_t pin_config = {
  .bck_io_num = 26, //this is BCK pin
  .ws_io_num = 25, // this is LRCK pin
  .data_out_num = 22, // this is DATA output pin
  .data_in_num = -1   //Not used
};


int i2s_write_sample_nb(uint32_t sample) {
  return i2s_write_bytes((i2s_port_t)i2s_num, (const char *)&sample, sizeof(uint32_t), 100);
}

//Main function to play samples from PROGMEM
void playPROGMEMsample(const uint32_t* audioSample) {

  uint32_t sampleSize = sizeof(audioSample) * 4;

  uint32_t counter = 0;
  //initialize i2s with configurations above
  i2s_driver_install((i2s_port_t)i2s_num, &i2s_config, 0, NULL);
  i2s_set_pin((i2s_port_t)i2s_num, &pin_config);
  //set sample rates of i2s to sample rate of wav file
  i2s_set_sample_rates((i2s_port_t)i2s_num, 16000);


  uint32_t readData;

  while (audioSample)  {
    readData = pgm_read_dword(&audioSample[counter++]);
    if (readData == NULL) break;
    i2s_write_sample_nb(readData);
  }

  i2s_driver_uninstall((i2s_port_t)i2s_num); //stop & destroy i2s driver
}

void setup() {

  Serial.begin(115200);
  Serial.println("Serial connection OK");
  playPROGMEMsample(sample1);
}

void loop() {}


Basically, I want the code to read data from PROGMEM and then output it through my I2S amp. The goal of the project is to play all my sound samples from PROGMEM and not have to mess with an SD card, so that part is non-negotiable.

The sound sample is in PROGMEM as a series of hex double words. It's a 16-bit, 16 kHz wav sample, like this:

Code: Select all

const uint32_t sample1[] PROGMEM = {

0xd8040600, 0x37113f27, 0x0b2f343c, 0xfa3efe46, 0xfe3d9531, 0xa8236a05, 0x81f78beb, 0x97e852e6,
0x03e154e6, 0x8adc57e3, 0x87f29b05, 0xc508e208, 0xa30aae0f, 0xca06e2fb, 0x7becbcdb, 0xb9e1f5d9,
0x92deb3e7, 0x94f15803, 0x130b58fb, 0x1afe6d03, 0x8f0fa404, 0x73f379f8, 0x82f080eb, 0xbae1e9e9,
0x5a015e0d, 0xbd07b411, 0x60224c35, 0x9f30ba39, 0x8b4a2a4e, 0x05508851, 0xae39c831, 0x8e2c6520,
0xc6164910, 0x4d17061c, 0x2c18c514, 0xcb13090d, 0x011cf128, 0xe22c9825, 0x420b76eb, 0xc4e4fbdf,
0x77d9e6c7, 0x01c9ffd3, 0x7cddbcea, 0xf0f45bfe, 0x31fb1af3, 0x58f358fa, 0x6ffed901, 0x0b040bfe,
0xe1fbf3ea, 0xbfe080d9, 0x30dcbde0, 0x85d99cd2, 0x3ac608c7, 0x6fd58ef4, 0x9606e90d, 0xd611a61b,
0xdb30643c, 0x9745a946, 0x1445754d, 0x0149c244, 0x283c823b, 0x1e416b33, 0x942b5220, 0x2117a611,

// yadayadayada
//yadayadayada...

};


The problem is, I'm not 100 percent sure what I am doing. How do I customize this code so that it will play my sound sample through the Adafruit mono amp? Right now, this code compiles without a problem, but all I get is noise on the speaker that is connected to the Adafruit amp. The amp has its own 3V3 external power supply.

I've attached the Arduino code and the sound sample file.
Attachments
---esp32_audio.zip
(472.51 KiB) Downloaded 37 times

Archibald
Posts: 82
Joined: Mon Mar 05, 2018 12:44 am

Re: ESP32 audio output

Postby Archibald » Mon Mar 05, 2018 2:27 am

Using I²S should give you really good sound quality so it's probably well worth trying to get it to work. If you find you can't make progress, I suggest you use one of the digital-to-analogue converter (DAC) outputs of the ESP32.

For speech messages, I am using 8-bit samples at 8kHz stored in ESP32 Flash memory. Initially I was concerned that 8-bit may be inadequate. To get maximum dynamic range, I am exporting audio clips from Audacity, having amplified them within Audacity to be on the edge of clipping. The quality is very adequate and certainly would be more than adequate within the noisy environment of a car. By sampling at only 8 kHz, I was concerned that I may need a good low-pass audio filter to cut off signal components above 4kHz. I'm finding the quality of sibilant speech to be questionable but still readily understandable. As I may need to store quite a lot of speech, I would prefer not to increase sampling rate to 16kHz to improve sibilants, but that would be preferable to adding an audio filter.

This makes the software very simple. The output part of my code, using Arduino IDE, is just:

Code: Select all

for (z=0 ; z<size ; z++)
 {
      dac_output_voltage (DAC_CHANNEL_1,buffer[z]); 
      delayMicroseconds(125);        // 8kbit/s period
 }

(I'm aware processing time will slightly lower the output sample rate)

For amplification, I purchased from China a very tiny board with the XPT8871 chip and am running it off 5 volt supply. It's output capabilities are similar to your Adafruit board but it works Class AB instead of Class D. The board has quite high amplification so I'm reducing the input signal considerably by a potential divider of 10kΩ and 470Ω (connected to ground). I guess it would go into its overheat protection mode if driven with loud continuous music, but for short speech clips it's fine. My loudspeaker is 65mm which gives good volume but certainly needs to be in a box, or at least mounted on a panel. I am sceptical whether your 0.6" loudspeaker will be adequate.

Archibald
Posts: 82
Joined: Mon Mar 05, 2018 12:44 am

Re: ESP32 audio output

Postby Archibald » Mon Mar 05, 2018 7:53 am

all I get is noise on the speaker that is connected to the Adafruit amp.


For I²S your data needs to be signed in two's complement form. If not you will get extreme distortion that sounds rather like noise.

(If you use one of the ESP32's digital-to-analogue converters, the data needs to be unsigned.)

megabite
Posts: 11
Joined: Wed Nov 22, 2017 12:56 pm

Re: ESP32 audio output

Postby megabite » Mon Mar 05, 2018 3:51 pm

I have sort of half managed to make it work now. It turns out that I had the byte order wrong with my sound sample. It needs to be little endian double words, which it wasn't before.

Now, however, the sample sounds like it's sped up by a few orders of magnitude.

Here's my code:

Code: Select all

#include "driver/i2s.h"
#include "freertos/queue.h"
#include <pgmspace.h>

#include "soundsample.h"


//i2s configuration
int i2s_num = 0; // i2s port number
i2s_config_t i2s_config = {
     .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
     .sample_rate = 16000,
     .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
     .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
     .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
     .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority
     .dma_buf_count = 8,
     .dma_buf_len = 64   //Interrupt level 1
    };
   
i2s_pin_config_t pin_config = {
    .bck_io_num = 26, //this is BCK pin
    .ws_io_num = 25, // this is LRCK pin
    .data_out_num = 22, // this is DATA output pin
    .data_in_num = -1   //Not used
};

int i2s_write_sample_nb(uint32_t sample) {
  return i2s_write_bytes((i2s_port_t)i2s_num, (const char *)&sample, sizeof(uint32_t), 100);
}

//Main function to play samples from PROGMEM
void playPROGMEMsample(const uint32_t* audioSample) {

 
  uint32_t counter = 0;
  //initialize i2s with configurations above
  i2s_driver_install((i2s_port_t)i2s_num, &i2s_config, 0, NULL);
  i2s_set_pin((i2s_port_t)i2s_num, &pin_config);
  //set sample rates of i2s to sample rate of wav file
  i2s_set_sample_rates((i2s_port_t)i2s_num, 16000);


  uint32_t readData;

  while (audioSample)  {
    readData = pgm_read_dword(&audioSample[counter++]);
    if (readData == NULL) break;
    i2s_write_sample_nb(readData);
  }

  i2s_driver_uninstall((i2s_port_t)i2s_num); //stop & destroy i2s driver
}

void setup() {

  Serial.begin(115200);
  Serial.println("Serial connection OK");
  playPROGMEMsample(sample1);
}

void loop() {


}


How do I slow the playback down?

Who is online

Users browsing this forum: kolban and 8 guests