Tuesday

AutoAnalogAudio Library: New examples for nRF52x including a BLE controlled Audio Player

 AutoAnalogAudio Library Updates for nRF52x:

 New examples for nRF52x & a BLE controlled Audio Player

With recent updates to the AutoAnalogAudio library, I've been able to put together a bunch of examples specific to the nRF52x platforms. The examples range from examples that use the onboard PDM microphone capabilities and either an I2S or Analog (PWM output) amplifier to a BLE controlled Audio Player.




The examples also demonstrate usage of the radio capabilities as well, using the radio either at a low level (nrf_to_nrf library), capable of streaming very high-quality audio or using BLE control to playback audio from SD card. 

When recording via PDM and either re-playing to an amplifier or broadcasting via radio, fairly high sample-rates can be used along with 16-bit modes, making for very decent quality wireless audio. There are some limitations when reading from SD card, as it seems the max SPI speed on these devices isn't that fast, so users need to play around with sample rates, stereo/mono modes and 8 or 16-bit samples.

Once the AutoAnalogAudio library is installed, the nRF52x examples can be found in Arduino examples under AutoAnalogAudio/Platforms/NRF52

XIAO BLE Sense 52840 used for testing


The BLE controlled audio player uses a bunch of different peripherals and pushes the capabilities of the device a bit, but it seems to work great. I've created another example using the Adafruit Bluefruit library as well, which supports faster SD reading & higher quality playback, which I will also include in the library soon. 

Here is the current code using the standard Arduino BLE library:

/* Arduino BLE control
led Audio Player for nRF52

 *
 * This is an example of me playing around with BLE control and different
 * services/characteristics to test the AutoAnalogAudio library.
 *
 * Requirements:
 * 1. nRF52 Device (Tested on nRF52840)
*  2. SD Card with WAV files: 8-bit, 16-24kHz, Mono
 * 3. I2S or Analog Amplifier + Speaker connected
 * 4. Mobile device or 'other' with nRF Connect installed
 *
 * Connect via nRF Connect App:
 * 1. Device should come up as BLE Audio Player
 * 2. You should see:
 *   a: Common Audio
*    b: Audio Input Type:
      Send a UTF-8 String to play a file: myfileDirectory/myfilename.wav
 *   c: Audio Input Control Point:
      Send an Unsigned value between 0-10 to set the volume low-high
 */


#include <SPI.h>
#include <SD.h>
#include <ArduinoBLE.h>
#include <AutoAnalogAudio.h>

AutoAnalog aaAudio;

/************** USER CONFIG ***********/
// File to play on startup
const char* audioFilename = "far8b16k.wav";  // 8-bit @ 24kHz audio is the max over SD card while BLE is running
uint8_t SD_CS_PIN = 2;                       // Set this to your CS pin for the SD card/module
#define USE_I2S 1                            // Set this to 0 for analog (PWM) audio output instead of I2S

/*********************************************************/
/* Tested with MAX98357A I2S breakout
/* BCLK connected to Arduino D1 (p0.03)
/* LRCK connected to Arduino D3 (p0.29)
/* DIN  connected to Arduino D5 (p0.05)
/* SD   connected to Arduino D6 (p1.11)
/*********************************************************/

#define FILENAME_BUFFER_LENGTH 64
char songName[FILENAME_BUFFER_LENGTH];
float volumeControl = 0.2;
#define AUDIO_BUFFER_SIZE 1600

BLEService audioService("1853");

// BLE Audio Charactaristic
BLECharacteristic audioDataCharacteristic("2b79", BLERead | BLEWrite | BLENotify, FILENAME_BUFFER_LENGTH);
BLEByteCharacteristic audioVolumeCharactaristic("2b7b", BLERead | BLEWrite);

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

  aaAudio.begin(0, 1, USE_I2S);  //Setup aaAudio using DAC and I2S or PWM

  // BLE initialization
  if (!BLE.begin()) {
    Serial.println("Starting BLE failed!");
    while (1) {};
  }

  BLE.setLocalName("BLE Audio Player");
  BLE.setAdvertisedService(audioService);

  audioService.addCharacteristic(audioDataCharacteristic);
  audioService.addCharacteristic(audioVolumeCharactaristic);
  BLE.addService(audioService);

  BLE.advertise();
  Serial.println("BLE Peripheral is now advertising");

  Serial.print("Init SD card...");
  if (!SD.begin(SD_CS_PIN)) {
    Serial.println("init failed!");
    return;
  }
  Serial.println("SD init ok");
  pinMode(6, OUTPUT);  //Connected to SD pin of MAX98357A
  digitalWrite(6, HIGH);

  playAudio(audioFilename);
}

void loop() {

  BLEDevice central = BLE.central();

  if (central) {

    if (central.connected()) {
      if (audioDataCharacteristic.written()) {
        memset(songName, 0, sizeof(songName));
        audioDataCharacteristic.readValue((uint8_t*)songName, FILENAME_BUFFER_LENGTH);
        playAudio(songName);
        Serial.println(songName);
      }
      if (audioVolumeCharactaristic.written()) {
        uint8_t vol;
        audioVolumeCharactaristic.readValue(vol);
        volumeControl = vol / 10.0;
        Serial.print("BLE Set Volume: ");
        Serial.println(volumeControl);
      }
    }
  }

  loadBuffer();

  // Control via Serial for testing
  if (Serial.available()) {
    char c = Serial.read();
    if (c == '=') {
      volumeControl += 0.1;
    } else if (c == '-') {
      volumeControl -= 0.1;
      volumeControl = max(0.0, volumeControl);
    } else if (c == 'p') {
      playAudio("brick/brick24.wav");
    }
    Serial.println(volumeControl);
  }
}

/*********************************************************/
/* A simple function to handle playing audio files
/*********************************************************/

File myFile;

void playAudio(const char* audioFile) {

  if (myFile) {
    myFile.close();
  }
  //Open the designated file
  myFile = SD.open(audioFile);

  myFile.seek(22);
  uint16_t var;
  uint32_t var2;
  myFile.read(&var, 2);   // Get channels (Stereo or Mono)
  myFile.read(&var2, 4);  // Get Sample Rate
  aaAudio.setSampleRate(var2, var - 1);

  myFile.seek(34);
  myFile.read(&var, 2);  // Get Bits Per Sample
  aaAudio.dacBitsPerSample = var;

  myFile.seek(44);  //Skip past the WAV header
}

void loadBuffer() {

  if (myFile.available()) {

    if (aaAudio.dacBitsPerSample == 8) {
      myFile.read(aaAudio.dacBuffer, AUDIO_BUFFER_SIZE);
      for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE; i++) {
        aaAudio.dacBuffer[i] *= volumeControl;
      }
      aaAudio.feedDAC(0, AUDIO_BUFFER_SIZE);
    } else {
      myFile.read(aaAudio.dacBuffer16, AUDIO_BUFFER_SIZE);
      for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE / 2; i++) {
        int16_t sample = aaAudio.dacBuffer16[i];
        sample *= volumeControl;
        aaAudio.dacBuffer16[i] = (uint16_t)sample;
      }
      aaAudio.feedDAC(0, AUDIO_BUFFER_SIZE / 2);
    }

  } else {
    myFile.seek(44);
  }
}



No comments:

AutoAnalogAudio Library - nRF52 Now supports Successive Approximation Analog-to-Digital Converter for Audio Input

 AutoAnalogAudio Library - nRF52 Now supports SAADC Support for Successive Approximation Analog-to-Digital Converter for Audio Input I'v...