3 Star 19 Fork 7

mingyee/ArduinoADFSimplify

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
LGPL-3.0

本库对开发环境的版本要求

1. Arduino2.0.0+ESP32-2.0.5或以上;

2. VSCODE1.72.2+PlatformIO_v2.5.4+Espressif 32 5.0.0

注:其他开发环境版本本库功能是否正常不做保证。

Bluetooth Audio for Arduino

The code exposes the A2DP profile (Bluetooth Audio) available in ESP32 boards using the Arduino interface. It assumes you have installed the ESP32 core for Arduino and have an ESP32 board. I quite like the TinyPico because it's so powerful and so tiny! I also like the ESP32-PICO-KIT because it's so powerful and cheap. Both have the same chips (i think) but the TinyPico is way smaller.

Table of contents

  1. Installation
  2. Advertising the Connection
  3. Simple Audio
    1. Hardware: Components
    2. Hardware: Setup
    3. I2S
  4. Changing Volume
  5. Serial Control
  6. High-Pass Filtering
  7. Low-Pass Filtering
  8. Dynamic Range Compression
    1. DRC: Partial Control
    2. DRC: Full Control
    3. DRC: Approximation
  9. WiFi Interface

Appendix I: Not so simple audio

Installation

  1. Install the Arduino IDE
  2. Install the ESP32 core for Arduino
  3. Download this repository

4. Within the Arduino IDE, select "Sketch" -> "Include Library" -> "Add .ZIP library", then select the downloaded zip file.

This should add the library. To use the library, you'll have to include the relevant header in the Arduino sketch. You'll see this in the following sketches.

Advertising the Connection

This section covers the advertiseBluetooth example. The first step to getting some Bluetooth audio up and running is to advertise your ESP32 board. You will need to include the btAudio header and declare a btAudio object.

#include <btAudio.h>

// Sets the name of the audio device
btAudio audio = btAudio("ESP_Speaker");

The string that you supply to the btAudio object becomes the name of the ESP32 Bluetooth connection. However, this only initialises the object. It doesn't actually start the Bluetooth. For that you'll need to use the btAudio::begin method.

void setup() {
 
 // Streams audio data to the ESP32   
 audio.begin();

}

void loop() {

}

Yay, now you can connect to your ESP32 board and stream audio to it. You can connect with your phone, laptop, MP3 player, whatever you want. Sadly, this data is stuck on the ESP32 unless you have a DAC (Digital to Analogue Converter) that can actually send the audio somewhere (speaker, Hi-Fi system). I'll cover that in the next section.

btAudio can remember and attempt to automatically connect to the last connected source device. Just call the audio.reconnect() function, and btAudio will connect to the last remembered device. You can also manually disconnect from the current source device using the audio.disconnect() function.

void setup() {
 
 // Streams audio data to the ESP32   
 audio.begin();
 
 // Re-connects to last connected device
 audio.reconnect();

}

void loop() {

}

The whole script is below:

#include <btAudio.h>

// Sets the name of the audio device
btAudio audio = btAudio("ESP_Speaker");

void setup() {
 
 // Streams audio data to the ESP32   
 audio.begin();
 
 // Re-connects to last connected device
 audio.reconnect();

}

void loop() {

}

Simple Audio

This section covers the minimalAudio example. Now that we have mastered the Bluetooth component of "Bluetooth Audio", let's turn to the audio part. This requires some extra hardware. I like the Adafruit I2S Stereo decoder. It takes data from the ESP32 and converts it to a line out signal which can be plugged into a stereo or Hi-Fi system (instantly adding wireless audio to your audio system). But what is I2S and what extra hardware do you need?

Hardware: Components

You will need:

  • a Breadboard
  • An ESP32 board. I like the TinyPico and the ESP32-PICO-KIT
  • Adafruit I2S Stereo decoder.
  • Micro USB cable
  • 5 Jumper Wires (3 for I2S, 1 for power and 1 for ground)
  • Aux cable/3.5mm headphones
  • Speaker with Line in connection (if you're not using headphones).

Hardware: Setup

  1. Set up the breadboard. There's a wiring guide for the I2S DAC over at adafruit. The connections to the DAC are the same. Just swap the outputs from the microcontroller in the adafruit example to the pins you selected for the ESP32 in the minimalAudio example. Mine looks like this. While making this project I didn't have access to jumper wires or a soldering iron (they were at my locked-down workplace). For the jumper wires I just cut up one long blue wire into as many wires as I needed. To attach pins to the DAC I used Pimoroni push headers. This is not ideal but cheaper than buying a temporary soldering iron.

  1. Upload the minimalAudio example using the micro USB cable.

  2. Disconnect USB from computer and power the ESP32 board from a USB power supply near the speaker. Don't worry about it losing power it will remember the code you uploaded previously.

  3. Connect the Line out from the DAC to the the Line in on the stereo/Hi-Fi using your Aux cable. You could also used wired headphones instead of an Aux cable to a speaker. My setup looks like this.

  1. On your laptop/phone connect to the ESP32 like you would any other bluetooth device.

  2. Play some audio!

I2S

I2S is method of digitally transferring audio data between devices. It uses just 3 wires. This is particularly useful in transferring data to an external high performance Digital to Analog Converter (DAC). For instance most ESP32s have 2 8-bit DACs whereas music is usually played over 16-bit DACs (or better). However, you can get cheap 16 bit DACs that you can plug into your speakers/Hi-Fi systems. These DACs receive data from your microcontroller using I2S. The one I have been using is the Adafruit I2S Stereo decoder. Not all microcontrollers have I2S communication and Bluetooth Classic/ WIFI. This is why the ESP32 boards are key here. They have both. A simple bit of code to combine both the bluetooth and the I2S is given below. The only difference between this code and the advertising code is calling the btAudio::I2S method and specifying the 3 pins that you want to use. A cool feature about ESP32s is that you usually pick whatever pins to do whatever action you want. So feel free to change the pins.

The full contents of the minimalAudio example are below.

#include <btAudio.h>

// Sets the name of the audio device
btAudio audio = btAudio("ESP_Speaker");

void setup() {
 
 // Streams audio data to the ESP32   
 audio.begin();
 
 // Re-connects to last connected device
 audio.reconnect();
 
 // Outputs the received data to an I2S DAC, e.g. https://www.adafruit.com/product/3678
 int bck = 26; 
 int ws = 27;
 int dout = 25;
 audio.I2S(bck, dout, ws);
}

void loop() {

}

Changing Volume

This section covers the changeVolume example. Volume is a tricky issue. Ideally, the sender should issue a request for volume to be changed at the receiver (using something like AVRCP). The sender should not change the data before it is transmitted. My MP3 player does not change the data before transmission. My laptop and phone do. One option is to digitally alter the data before it goes to the DAC but after it has been received by the ESP32. We can do this by using the btAudio::volume method. It accepts one argument: a floating point number between 0 and 1. It then scales the data by that number.

#include <btAudio.h>

// Sets the name of the audio device
btAudio audio = btAudio("ESP_Speaker");

void setup() {

 // Streams audio data to the ESP32   
 audio.begin();

 // Re-connects to last connected device
 audio.reconnect();

 // Outputs the received data to an I2S DAC, e.g. https://www.adafruit.com/product/3678
 int bck = 26; 
 int ws = 27;
 int dout = 25;
 audio.I2S(bck, dout, ws);
}

void loop() {
  delay(3000);
  audio.volume(0.1);
  delay(3000);
  audio.volume(1.0);
}

Serial Control

This section covers the serialControl example. If you want to control any of the features proposed here you may need to create a hardware interface (buttons, potentiometers, etc). In the meantime I've created a serial interface for you to play with. It's pretty simple and not necessarily the best method but it works. The idea is to send a string to over the comm port (e.g. vol). Follow this string with a terminator(e.g. #) and then follow this with a number (e.g. 0.42). So the full command to cut the volume by half would be vol#0.5. In the image below you can see waht this looks like in hte serial monitor.

Once the code in the next section is uploaded try and experiment by opening the serial monitor and changing the volume a bit. This general approach can be adapted for any method.

#include <btAudio.h>

btAudio audio = btAudio("ESP_Speaker");
String command;

void setup() { 
 // Streams audio data to the ESP32   
 audio.begin();
 // Re-connects to last connected device
 audio.reconnect();
 // Outputs the received data to an I2S DAC, e.g. https://www.adafruit.com/product/3678
 int bck = 26; 
 int ws = 27;
 int dout = 25;
 audio.I2S(bck, dout, ws);
 // Opens the serial port
 Serial.begin(115200);
}


void loop() {
 delay(300);
 // check if data is available 
 if(Serial.available()){
  //read until a terminator. after this point there should only be numbers
  command = Serial.readStringUntil('#');
  if(command.equals("vol")){
   //read and set volume	  
   float vol =Serial.parseFloat();
   Serial.println("Changing Volume");
   audio.volume(vol);
  }
 }
}

High-Pass Filtering

This section covers the highpassFilter example. High-Pass filters remove low frequency content from your data. You can either set the parameters in the void setup() section or you can interactively edit the parameters via the serial interface. The method is btAudio::createFilter. The implementation uses a cascade of biquad filters. The method takes three arguments. The first specifies the number of filter cascades. A higher number makes the filter sharper but increases the run-time of the method. For me a value of 3 is a good compromise between computation time and filter efficacy. The second argument is the filter cutoff. Below this frequency the signal starts to get suppressed. The third argument is the filter type. Setting the value to highpass makes the filter a high-pass filter. Calling the stopFilter() method stops the effects of the filter(send command stopFilt#). Experiment with a few values for your high pass cut off. Most speakers struggle to produce sound below 100Hz so sending the command hp#100 should only make a very small difference to your listening. Send the command hp#600 and you will notice a very big difference! The sound will sound like it's coming through a very old phone...

#include <btAudio.h>

btAudio audio = btAudio("ESP_Speaker");
String command;

void setup() { 
 // Streams audio data to the ESP32   
 audio.begin();
 // Re-connects to last connected device
 audio.reconnect();
 // Outputs the received data to an I2S DAC, e.g. https://www.adafruit.com/product/3678
 int bck = 26; 
 int ws = 27;
 int dout = 25;
 audio.I2S(bck, dout, ws);
 // Opens the serial port
 Serial.begin(115200);
}

int fo=3;
float fc=30;

void loop() {
 delay(300);
 if(Serial.available()){
  command = Serial.readStringUntil('#');
  if(command.equals("hp")){
   Serial.println("Setting a High-Pass Filter...");
   fc =Serial.parseFloat();
   audio.createFilter(fo, fc, highpass);
  }
  else if(command.equals("stopFilt")){
   audio.stopFilter();
   Serial.println("Stopping Filter...");
  }   
 }
}

Low-Pass Filtering

This section covers the lowpassFilter example. Low-Pass filters suppress high frequency signals in your audio. Creating one is very similar to the High-Pass filter example. It only requires you to change the third argument from highpass to lowpass in the btAudio::createFilter method. We can similarly use the serial interface to change the frequency cutoff (lp#10000). Sets a 10000Hz Low-Pass Filter. Lowering this setting creates a more muffled sound (like listening to audio through a wall or under water). As a side note, if you send the hp#100 and lp#14000 commands you will create a Band-Pass Filter with cutoffs at 100Hz & 14000Hz. stopFilt# will once again turn off the filters.

#include <btAudio.h>

btAudio audio = btAudio("ESP_Speaker");
String command;

void setup() { 
 // Streams audio data to the ESP32   
 audio.begin();
 // Re-connects to last connected device
 audio.reconnect();
 // Outputs the received data to an I2S DAC, e.g. https://www.adafruit.com/product/3678
 int bck = 26; 
 int ws = 27;
 int dout = 25;
 audio.I2S(bck, dout, ws);
 // Opens the serial port
 Serial.begin(115200);
}

int fo=3;
float fc;

void loop() {
 delay(300);
 if(Serial.available()){
  command = Serial.readStringUntil('#');
  if(command.equals("hp")){
   Serial.println("Setting a High-Pass Filter...");
   fc =Serial.parseFloat();
   audio.createFilter(fo, fc, highpass);
  }
  else if(command.equals("stopFilt")){
   audio.stopFilter();
   Serial.println("Stopping Filter...");
  } 
  else if(command.equals("lp")){
   Serial.println("Setting a Low-Pass Filter...");
   fc =Serial.parseFloat();
   audio.createFilter(fo, fc, lowpass);
  }  
 }
}

Dynamic Range Compression

I'll highlight terms throughout this section that will be parameters for the Dynamic range compression(DRC). DRC is a method that evens out the sound of audio. It compresses loud sounds to the level of quiet ones and then, optionally, amplifies(gain) both. The result is a more even audio experience. It is nonlinear and has many, many settings. It is both complicated to implement and to use. Consider this feature experimental. There are two main types of compression: Hard Knee and Soft Knee. Basically, once the audio reaches a certain threshold it starts to be compressed by a certain Ratio. Hard knee compressors start compressing immediately at the threshold but Soft knee compressors start compressing sooner and in a more smooth fashion. Supposedly, this leads to a more natural sound but it is far more computationaly intensive to implement.
Compression knee

You can also specify the width of this soft knee (i.e. How gradual you want the compression to be). Setting this width to zero implements a hard knee. You can also set attack and release times. These are time constants that determine how quickly the compressor starts working. For instance if there was a sudden change in volume you might want to have a very quick attack time so as to compress that signal quickly. Having a long release time will leave the compressor running for a brief time after it has started compressing. I've got two examples on how to use the compressor. One has extreme parameters that you can just turn on or off and another with full serial control over all the parameters.

DRC: Partial Control

This section covers the partialDRC example. For turning the compressor on and off just send the compress# and decompress# commands over the serial port. This will call the btAudio::compress method and the btAudio::decompress method. Hopefully, you should hear a clear difference between compressed and uncompressed data. It can be quite useful for making sure audio will never clip a set of speakers and for watching TV shows that have very loud background music and very low vocals. I'm looking at you xxxxxxx!

#include <btAudio.h>

btAudio audio = btAudio("ESP_Speaker");
String command;

void setup() { 
 // Streams audio data to the ESP32   
 audio.begin();
 // Re-connects to last connected device
 audio.reconnect();
 // Outputs the received data to an I2S DAC, e.g. https://www.adafruit.com/product/3678
 int bck = 26; 
 int ws = 27;
 int dout = 25;
 audio.I2S(bck, dout, ws);
 // Opens the serial port
 Serial.begin(115200);
}

float thresh=30;
float attack=0.1;
float release= 0.2;
float ratio = 10;
float width= 10;
float gain=0;

void loop() {
 delay(300);
 if(Serial.available()){
  command = Serial.readStringUntil('#');
   if(command.equals("compress")){
    Serial.println("Applying Compression");
    audio.compress(thresh,attack,release,ratio,width,gain);
   }
   else if(command.equals("decompress")){
    Serial.println("Releasing Compression");
    audio.decompress();
   }
  }
}

DRC: Full Control

This section covers the fullDRC example. Examples of the commands to control the individual parameters are as follows:

  • compress#
  • decompress#
  • gain#0
  • thresh#25
  • attack#0.1
  • release#0.1
  • ratio#10
  • width#10

The actual code that does the compression is the same but the serial interface is more verbose.

#include <btAudio.h>

btAudio audio = btAudio("ESP_Speaker");
String command;

void setup() { 
 // Streams audio data to the ESP32   
 audio.begin();
 // Re-connects to last connected device
 audio.reconnect();
 // Outputs the received data to an I2S DAC, e.g. https://www.adafruit.com/product/3678
 int bck = 26; 
 int ws = 27;
 int dout = 25;
 audio.I2S(bck, dout, ws);
 // Opens the serial port
 Serial.begin(115200);
}

float thresh=30;
float attack=0.1;
float release= 0.2;
float ratio = 10;
float kneeWidth= 1;
float gain=0;

void loop() {
 delay(300);
 if(Serial.available()){
  command = Serial.readStringUntil('#');
   if(command.equals("compress")){
    Serial.println("Applying Compression");
    audio.compress(thresh,attack,release,ratio,kneeWidth,gain);
   }
   else if(command.equals("decompress")){
    Serial.println("Releasing Compression");
    audio.decompress();
   }
   else if(command.equals("gain")){
    gain =Serial.parseInt();
    Serial.println((String)"Compressing with a gain of "+gain);
    audio.compress(thresh,attack,release,ratio,kneeWidth,gain);
   }
   else if(command.equals("thresh")){
    thresh =Serial.parseFloat();
    Serial.println(thresh);
    Serial.println((String)"Compressing with a threshold of " + thresh + "dB");
    audio.compress(thresh,attack,release,ratio,kneeWidth,gain);
   }
   else if(command.equals("attack")){
    attack =Serial.parseFloat();
    Serial.println((String)"Compressing with an attack time of " + attack*1000 + "ms");
   audio.compress(thresh,attack,release,ratio,kneeWidth,gain); 
   }
   else if(command.equals("release")){
    release =Serial.parseFloat();
    Serial.println((String)"Compressing with an release time of " + release*1000 + "ms");
    audio.compress(thresh,attack,release,ratio,kneeWidth,gain);
   }
   else if(command.equals("ratio")){
    ratio =Serial.parseFloat();
    Serial.println((String)"Compressing with a Ratio of " + ratio);
    audio.compress(thresh,attack,release,ratio,kneeWidth,gain);
   }
   else if(command.equals("width")){
    kneeWidth =Serial.parseInt();
    Serial.println((String)"Compressing with a knee width of " + kneeWidth + "dB");
    audio.compress(thresh,attack,release,ratio,kneeWidth,gain);
   }
  }
}

Approximate Dynamic Range Compression

Dynamic Range Compression is very computationally expensive. I found for my ESP32 the big stress was converting the computed gain (dB) back to a 16 bit integer. This required computing pow10f(x/20) which became a severe bottleneck. This operation took takes about 5 microseconds to compute. For a stereo system that's 10 microseconds. Considering there is only 11.3 microseconds between stereo samples using 10 microseconds for one line of code is abhorrent.
I thought about precomputing the values and creating a lookup table but that would require > 130KB of memory. That would not be the polite thing to do. Program storage space is valuable, particularly as the ESP32 uses a lot of memory for WIFI and Bluetooth. The compromise was to use a lookup table for integral part of x and a polynomial approximation for the fractional part of x. As x only ranges between -90dB and 90dB for signed 16 bit integers we can create a lookup table for the integral part using less than 400 bytes. For the fractional part we use Newton's Divided Difference method for polynomial interpolation between the integer parts. Long story short this method has an accuracy of 0.01% and runs in 0.2 microseconds. If you just use the integer lookup method the error is greater than 10% and takes 0.1 microseconds. In my opinion the extra 0.1 microseconds is worth the 1000 fold increase in accuracy. The code below is covered in the benchLookup example. This example isn't crucial but useful if you want to see the difference in methodologies.

void setup() {
 Serial.begin(115200);
 
// Create lookup table
float lu[199];
 for(int i = -99; i <100;i++){
  lu[i+99]=pow10f(i/20.0);
}

// test data ranges between -10 and 10 dB
float temp=-10.0;
float val[200];
for(int i = 0; i <200;i++){
  temp+=.1;
  val[i]= temp;
}


// exact solution 
float outvalEx[200];
unsigned long t1 =micros();
for(int i = 0; i <200;i++){
  outvalEx[i]= pow10f(val[i]/20.0f);
}
unsigned long t2 =micros();  
Serial.print("Exact Method: ");
Serial.print((t2-t1)/200.0);
Serial.println(" Microseconds per transform");


// approximate method 

float outvalAppi[200];

t1 =micros();
for(int i = 0; i <200;i++){
    outvalAppi[i]= lu[(int)val[i]+99];
}
t2=micros();
Serial.print("Approximate Method(lookup): ");
Serial.print((t2-t1)/200.0);
Serial.println(" Microseconds per transform");


// approximate method 
float frac;
float outvalApp[200];

t1 =micros();
for(int i = 0; i <200;i++){
  float input =val[i];
  if(input<0){
    int integ=  (int)input;
    integ-=1;
    frac= input-integ;
    outvalApp[i]= 1.0f+ (0.1146f+0.0074f*frac)*frac;
    outvalApp[i]*=lu[integ+99];
  }else{
    int integ=  (int)input;
    frac= input-integ;
    outvalApp[i]= 1.0f+ (0.1146f+0.0074f*frac)*frac;
    outvalApp[i]*=lu[integ+99];
  }
}
t2=micros();
Serial.print("Approximate Method: ");
Serial.print((t2-t1)/200.0);
Serial.println(" Microseconds per transform");

float err=0.0;
float maxerr=0.0;


// Approximation error(lookup)
for(int i = 0; i <200;i++){
  err= abs((outvalAppi[i]-outvalEx[i])/outvalEx[i]*100);
  if(err>maxerr){
    maxerr=err;
  }
}
Serial.print("Approximate method(lookup) max error is: ");
Serial.print(maxerr);
Serial.println("%");

maxerr=0;
// Approximation error(polynomial)
for(int i = 0; i <200;i++){
  err= abs((outvalApp[i]-outvalEx[i])/outvalEx[i]*100);
  if(err>maxerr){
    maxerr=err;
  }
}
Serial.print("Approximate method (polynomial) max error is: ");
Serial.print(maxerr);
Serial.println("%");

}

void loop() {
  // put your main code here, to run repeatedly:
delay(1000);
}

If you run the above code you should get results like this on the serial monitor.

WiFi interface

This section covers the webInterface example The serial interface is useful for debugging but not very useful if you have the ESP32 hooked up behind a speaker. To edit the DSP parameters wirelessly I've created a very simple web server that you can access via any browser connected to the same network as the ESP32. It's pretty straightforward to use. Simply create a webDSP object and pass it your SSID, password, and the btAudio object.

#include<webDSP.h>
#include<btAudio.h>

// create audio object 
btAudio audio = btAudio("ESP_Speaker");

// create webserver object
webDSP web;

void setup() {
 // Streams audio data to the ESP32   
 audio.begin();
 // Re-connects to last connected device
 audio.reconnect();
 // Outputs the received data to an I2S DAC, e.g. https://www.adafruit.com/product/3678
 int bck = 26; 
 int ws = 27;
 int dout = 25;
 audio.I2S(bck, dout, ws);
 // Opens the serial port
 Serial.begin(115200);
  
 // Replace ssid and password with your details
 const char* ssid = "";
 const char* password = "";
 web.begin(ssid, password, &audio); 
}

void loop() {
  // continually check on client 
  web._server.handleClient();
}

The first time you run this bit of code, you should have the serial monitor open so that you can see what the IP address assigned to your ESP32 is. Once you know this number, simply enter it in to your browser and you'll be greeted by this webpage!

You can expand each of the three sections and edit any of the parameters of the filters, volume or dynamic range compression.

Not so simple audio

What if you hate classes/Object Oriented Programming and don't want to use my code but still want Bluetooth audio. Well this section covers just how to do that with the underTheHood example. It uses the minimum amount of ESP32 code to get audio output on I2S. I ain't gonna explain this. The reason I wrote the library is so that I wouldn't have to explain low level ESP32 code. However, for developers it may be easier to work with the raw ESP32 code as you can see the all the moving parts and dependencies more easily. However, for an end user(not a developer) I think a class based approach that hides these details is the way to go. Whatever your thoughts, I think I'm unlikely to shift from the object-oriented programming to more functional programming for this library.

On a technical note this code adapts the ESP-IDF A2DP Sink example but uses the arduino-esp32 library . There are very subtle differences between these libraries. So this example will only work with the Arduino library, not the ESP-IDF library. You'll need to use the much more complicated ESP-IDF example if you work outside the Arduino environment.

// cricial audio bits taken from https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/bluedroid/classic_bt/a2dp_sink/main/main.c

// bluetooth, config, discover and audio
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_a2dp_api.h"


// the audio DAC and amp configuration. 
#include "driver/i2s.h"

//globals


// the callback(processes bluetooth data).
// this is the most important function. 
void bt_data_cb(const uint8_t *data, uint32_t len){
   // number of 16 bit samples
   int n = len/2;
   
   // point to a 16bit sample 
   int16_t* data16=(int16_t*)data;
   
   // create a variable (potentially processed) that we'll pass via I2S 
   int16_t fy; 
   
   // Records number of bytes written via I2S
   size_t i2s_bytes_write = 0;

   for(int i=0;i<n;i++){
    // put the current sample in fy
    fy=*data16;
    
    //making this value larger will decrease the volume(Very simple DSP!). 
    fy/=1;
    
    // write data to I2S buffer 
    i2s_write(I2S_NUM_0, &fy, 2, &i2s_bytes_write,  10 );
    
    //move to next memory address housing 16 bit data 
    data16++;
   }
}


void setup() {

  // i2s configuration
  static const i2s_config_t i2s_config = {
    .mode = static_cast<i2s_mode_t>(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = 44100,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = static_cast<i2s_comm_format_t>(I2S_COMM_FORMAT_I2S|I2S_COMM_FORMAT_I2S_MSB),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // default interrupt priority
    .dma_buf_count = 8,
    .dma_buf_len = 1000,
    .use_apll = false,
    .tx_desc_auto_clear = true
  };
  
  // i2s pinout
  static const i2s_pin_config_t pin_config = {
    .bck_io_num = 26,//26
    .ws_io_num = 27,
    .data_out_num = 25, //
    .data_in_num = I2S_PIN_NO_CHANGE
  };
  
  // now configure i2s with constructed pinout and config
  i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_NUM_0, &pin_config);
  i2s_set_clk(I2S_NUM_0, 44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);
  i2s_set_sample_rates(I2S_NUM_0, 44100);
  
 // set up bluetooth classic via bluedroid
  btStart();
  esp_bluedroid_init();
  esp_bluedroid_enable();

 
  // set up device name
  const char *dev_name = "ESP_SPEAKER";
  esp_bt_dev_set_device_name(dev_name);

  // initialize A2DP sink and set the data callback(A2DP is bluetooth audio)
  esp_a2d_sink_register_data_callback(bt_data_cb);
  esp_a2d_sink_init();
  
  // set discoverable and connectable mode, wait to be connected
  esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); 

}
void loop() {
  // put your main code here, to run repeatedly:
  delay(1000);
}
GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.

简介

ArduinoADFSimplify Arduino音频开发库,适用于任意ESP32 Audio Board,本库是将ESP-ADF底层CodeC芯片ES8311和ES8388驱动移植到Arduino的一个实例,可以认为是个ESP-ADF的简化版,后续将支持更多音频相关的功能,敬请期待! 展开 收起
README
LGPL-3.0
取消

发行版

暂无发行版

贡献者

全部

语言

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mingyee/ArduinoADFSimplify.git
git@gitee.com:mingyee/ArduinoADFSimplify.git
mingyee
ArduinoADFSimplify
ArduinoADFSimplify
master

搜索帮助