Skip to content

Commit

Permalink
A2DP stream support
Browse files Browse the repository at this point in the history
  • Loading branch information
pschatzmann committed May 7, 2021
1 parent e2e5cf3 commit 1f3a6f6
Show file tree
Hide file tree
Showing 50 changed files with 665 additions and 304 deletions.
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Some basic __header-only C++ classes__ that can be used for __Audio Processing__
- AudioOutputWithCallback class to provide callback integration e.g. with ESP8266Audio

This functionality provides the glue which makes different audio processing components and libraries work together.
We also provide plenty of examples that demonstrate how to implement the different scenarios. The __design philosophy__ is based on the Arduino conventions: we use the ```begin()``` and ```end()``` methods to start and stop the processing and we propagate the __use of Streams__. We all know the Arduino Streams: We use them to write out print messages and sometimes we use them to read the output from Serial devices. The same thing applies to my “Audio Streams”: You can read audio data from “Audio Sources” and you write them to “Audio Sinks”.
We also provide plenty of examples that demonstrate how to implement the different scenarios. The __design philosophy__ is based on the Arduino conventions: we use the ```begin()``` and ```end()``` methods to start and stop the processing and we propagate the __use of Streams__. We all know the [Arduino Streams](https://pschatzmann.github.io/arduino-audio-tools/html/class_stream.html): We use them to write out print messages and sometimes we use them to read the output from Serial devices. The same thing applies to my “Audio Streams”: You can read audio data from “Audio Sources” and you write them to “Audio Sinks”.

As “Audio Sources” we will have e.g.:

Expand Down Expand Up @@ -83,10 +83,17 @@ Here is the list of examples:

#### Stream API

- [streams-adc-serial](/examples/streams-adc-serial) Displaying input from analog microphone on the Serial Plotter
Here are a couple of simple test sketches to demo different output destinations:

- [streams-generator-serial](/examples/streams-generator-serial) Displaying generated sound on the Serial Plotter
- [streams-generator-i2s_external_dac](/examples/streams-generator-i2s_external_dac) Playing generated sound on external DAC via I2S
- [streams-memory_raw-i2s_external_dac](examples/streams-memory_raw-i2s_external_dac) - Play music form Flash Memory via I2S to External DAC
- [streams-generator-i2s](/examples/streams-generator-i2s) Output of generated sound on external DAC via I2S
- [streams-generator-dac](/examples/streams-generator-dac) Output of generated sound on ESP32 internal DAC via I2S
- [streams-generator-a2dp](/examples/streams-generator-a2dp) Output of generated sound on Bluetooth Speaker using A2DP
- [streams-adc-serial](/examples/streams-adc-serial) Displaying input from analog microphone on the Serial Plotter

And some more useful examples:

- [streams-memory_raw-i2s](examples/streams-memory_raw-i2s_external_dac) - Play music form Flash Memory via I2S to External DAC
- [streams-url_raw-serial](/examples/streams-url_raw-serial) Displaying a music file from the internet on the Serial Plotter
- [streams-url_raw-I2S_external_dac.ino](/examples/streams-url_raw-I2S_external_dac.ino) Streaming a File from the Internet to on external DAC via I2S

Expand Down
2 changes: 2 additions & 0 deletions basic-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

Just some examples using the basic API. Though this continues to work, we recommend that you use the Stream API because it is easier to use...
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
31 changes: 31 additions & 0 deletions examples/streams-a2dp-i2s/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Receive Sound Data from Bluetooth A2DP

We receive some music via Bluetooth e.g. from your mobile phone and push it out on I2S: Something that the A2DP library does out of the box.
However we just test this function here because the implementation runs via some additional buffers. The critical setting here is to make sure that the buffer for StreamCopy is big enough: we use 4100 bytes.

### External DAC:

For my tests I am using the 24-bit PCM5102 PCM5102A Stereo DAC Digital-to-analog Converter PLL Voice Module pHAT

![DAC](https://pschatzmann.github.io/arduino-audio-tools/resources/dac.jpeg)

I am just using the default pins defined by the framework. However I could change them with the help of the config object. The mute pin can be defined in the constructor of the I2SStream - by not defining anything we use the default which is GPIO23


| DAC | ESP32
| --------| ---------------
| VDD | 5V
| GND | GND
| SD | OUT (GPIO22)
| L/R | GND
| WS | WS (GPIO15)
| SCK | BCK (GPIO14)
| FMT | GND
| XSMT | GPIO23


- DEMP - De-emphasis control for 44.1kHz sampling rate(1): Off (Low) / On (High)
- FLT - Filter select : Normal latency (Low) / Low latency (High)
- SCK - System clock input (probably SCL on your board).
- FMT - Audio format selection : I2S (Low) / Left justified (High)
- XSMT - Soft mute control(1): Soft mute (Low) / soft un-mute (High)
38 changes: 38 additions & 0 deletions examples/streams-a2dp-i2s/streams-a2dp-i2s.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
/**
* @file streams-a2dp-i2s.ino
* @author Phil Schatzmann
* @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/streams-a2dp-i2s/README.md
*
* @author Phil Schatzmann
* @copyright GPLv3
*
*/
#include "AudioTools.h"
#include "AudioA2DP.h"

using namespace audio_tools;

A2DPStream in = A2DPStream::instance() ; // A2DP input - A2DPStream is a singleton!
I2SStream out;
StreamCopy copier(out, in, 4100); // copy in to out

// Arduino Setup
void setup(void) {
Serial.begin(115200);

// start the bluetooth audio receiver
Serial.println("starting A2DP...");
in.begin(RX_MODE, "MyReceiver");

I2SConfig config = out.defaultConfig(TX_MODE);
config.sample_rate = in.sink().sample_rate();
config.channels = 2;
config.bits_per_sample = 16;
out.begin(config);
}

// Arduino loop
void loop() {
copier.copy();
}
3 changes: 3 additions & 0 deletions examples/streams-a2dp-serial/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Receive Sound Data from Bluetooth A2DP

We receive some music via Bluetooth e.g. from your mobile phone and display it as CSV
32 changes: 32 additions & 0 deletions examples/streams-a2dp-serial/streams-a2dp-serial.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @file streams-a2dp-serial.ino
* @author Phil Schatzmann
* @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/streams-a2dp-serial/README.md
*
* @author Phil Schatzmann
* @copyright GPLv3
*
*/
#include "Arduino.h"
#include "AudioTools.h"
#include "AudioA2DP.h"

using namespace audio_tools;

A2DPStream in = A2DPStream::instance() ; // A2DP input - A2DPStream is a singleton!
CsvStream<int16_t> out(Serial, 2); // ASCII stream as csv
StreamCopy copier(out, in); // copy in to out

// Arduino Setup
void setup(void) {
Serial.begin(115200);

// start the bluetooth audio receiver
Serial.println("starting A2DP...");
in.begin(RX_MODE, "MyReceiver");
}

// Arduino loop
void loop() {
copier.copy();
}
7 changes: 7 additions & 0 deletions examples/streams-generator-a2dp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Digital output via I2S to a external DAC

Sometimes it is quite useful to be able to generate a test tone.
We can use the GeneratedSoundStream class together with a SoundGenerator class. In my example I use a SineWaveGenerator.

To test the I2S output I'm using this generated signal and write it to A2DP (e.g. a Bluetooth Speaker).

41 changes: 41 additions & 0 deletions examples/streams-generator-a2dp/streams-generator-a2dp.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* @file streams-generator-a2dp.ino
* @author Phil Schatzmann
* @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/streams-generator-a2dp/README.md
*
* @author Phil Schatzmann
* @copyright GPLv3
*
*/
#include "AudioTools.h"
#include "AudioA2DP.h"

using namespace audio_tools;

typedef int16_t sound_t; // sound will be represented as int16_t (with 2 bytes)
uint16_t sample_rate=44100;
uint8_t channels = 2; // The stream will have 2 channels
SineWaveGenerator<sound_t> sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000
GeneratedSoundStream<sound_t> in(sineWave, channels); // Stream generated from sine wave
A2DPStream out = A2DPStream::instance() ; // A2DP input - A2DPStream is a singleton!
StreamCopy copier(out, in); // copy in to out

// Arduino Setup
void setup(void) {
Serial.begin(115200);

// We send the test signal via A2DP - so we conect to the MyMusic Bluetooth Speaker
out.begin(TX_MODE, "MyMusic");

Serial.println("A2DP is connected now...");

// Setup sine wave
sineWave.begin(sample_rate, B4);

}

// Arduino loop
void loop() {
if (out)
copier.copy();
}
25 changes: 25 additions & 0 deletions examples/streams-generator-dac/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# I2S Analog Output Test

This is a simple basic test for the ESP32 __analog output__ using I2S.

We just send a generated sine wave and expect to hear a clean signal.
Please note the log level should be set so that there is no disturbing output!


### Output Device: Piezo Electric Element

To test the output I am using a piezo electric element

![DAC](https://pschatzmann.github.io/arduino-audio-tools/resources/piezo.jpeg)

It should also be possible to connect a headphone to the output pins...


On the ESP32 the output is on the Pins GPIO26 and GPIO27

| PIEZO | ESP32
| --------| ---------------
| + | GPIO25 / GPIO26
| - | GND


40 changes: 40 additions & 0 deletions examples/streams-generator-dac/streams-generator-dac.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* @file streams-generator-dac.ino
* @author Phil Schatzmann
* @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/streams-generator-dac/README.md
* @author Phil Schatzmann
* @copyright GPLv3
**/

#include "AudioTools.h"
#include "AudioA2DP.h"

using namespace audio_tools;

typedef int16_t sound_t; // sound will be represented as int16_t (with 2 bytes)
uint16_t sample_rate=44100;
uint8_t channels = 2; // The stream will have 2 channels
SineWaveGenerator<sound_t> sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000
GeneratedSoundStream<sound_t> sound(sineWave, channels); // Stream generated from sine wave
AnalogAudioStream out;
StreamCopy copier(out, sound); // copies sound into i2s

// Arduino Setup
void setup(void) {
// Open Serial
Serial.begin(115200);

// start the bluetooth
Serial.println("starting A2DP...");
AnalogConfig config = out.defaultConfig(TX_MODE);
config.sample_rate = sample_rate;
out.begin(config);

// Setup sine wave
sineWave.begin(sample_rate, B4);
}

// Arduino loop - copy sound to out
void loop() {
copier.copy();
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# Digital output via I2S to a external DAC
# I2S Digital Output Test

Sometimes it is quite useful to be able to generate a test tone.
We can use the GeneratedSoundStream class together with a SoundGenerator class. In my example I use a SineWaveGenerator.
This is a simple basic test for the I2S output to an external DAC

To test the I2S output I'm using this generated signal and write it to I2S.

For my tests I am using the 24-bit PCM5102 PCM5102A Stereo DAC Digital-to-analog Converter PLL Voice Module pHAT
![DAC](https://pschatzmann.github.io/arduino-audio-tools/resources/dac.jpeg)
We just send a generated sine wave and expect to hear a clean signal.
Please note the log level should be set so that there is no disturbing output!


### External DAC:

For my tests I am using the 24-bit PCM5102 PCM5102A Stereo DAC Digital-to-analog Converter PLL Voice Module pHAT

![DAC](https://pschatzmann.github.io/arduino-audio-tools/resources/dac.jpeg)

I am just using the default pins defined by the framework. However I could change them with the help of the config object. The mute pin can be defined in the constructor of the I2SStream - by not defining anything we use the default which is GPIO23


Expand All @@ -31,5 +32,3 @@ I am just using the default pins defined by the framework. However I could chang
- SCK - System clock input (probably SCL on your board).
- FMT - Audio format selection : I2S (Low) / Left justified (High)
- XSMT - Soft mute control(1): Soft mute (Low) / soft un-mute (High)


Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/**
* @file streams-generator-i2s.ino
* @file streams-generator-a2dp.ino
* @author Phil Schatzmann
* @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/streams-generator-i2s/README.md
* @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/streams-generator-a2dp/README.md
* @author Phil Schatzmann
* @copyright GPLv3
*/

#include "AudioTools.h"
#include "AudioA2DP.h"

using namespace audio_tools;

Expand All @@ -15,26 +16,27 @@ uint16_t sample_rate=44100;
uint8_t channels = 2; // The stream will have 2 channels
SineWaveGenerator<sound_t> sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000
GeneratedSoundStream<sound_t> sound(sineWave, channels); // Stream generated from sine wave
I2SStream out; // Output to I2S
I2SStream out;
StreamCopy copier(out, sound); // copies sound into i2s

// Arduino Setup
void setup(void) {
// Open Serial
Serial.begin(115200);

// Setup sine wave
sineWave.begin(sample_rate, B4);

// Open I2S
// start the bluetooth
Serial.println("starting A2DP...");
I2SConfig config = out.defaultConfig(TX_MODE);
config.sample_rate = sample_rate;
config.channels = channels;
config.bits_per_sample = sizeof(sound_t)*8;
config.sample_rate = sample_rate;
config.channels = 2;
config.bits_per_sample = 16;
out.begin(config);

// Setup sine wave
sineWave.begin(sample_rate, B4);
}

// Arduino loop - copy sound to out
void loop() {
copier.copy();
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
31 changes: 31 additions & 0 deletions sandbox/streams-a2dp-serial/streams-a2dp-serial.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @file streams-a2dp-serial.ino
* @author Phil Schatzmann
* @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/streams-a2dp-serial/README.md
*
* @author Phil Schatzmann
* @copyright GPLv3
*
*/
#include "AudioTools.h"
#include "AudioA2DP.h"

using namespace audio_tools;

A2DPStream in = A2DPStream::instance() ; // A2DP input - A2DPStream is a singleton!
CsvStream<int16_t> out(Serial, 2); // ASCII stream as csv
StreamCopy copier(out, in); // copy in to out

// Arduino Setup
void setup(void) {
Serial.begin(115200);

// start the bluetooth audio receiver
Serial.println("starting A2DP...");
in.begin(RX_MODE, "MyReceiver");
}

// Arduino loop
void loop() {
copier.copy();
}
Loading

0 comments on commit 1f3a6f6

Please sign in to comment.