Reputation: 33
I am recording and streaming over BLE, audio on XIAO Esp32S3 Sense device with this code:
#include <ESP_I2S.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define NOTIFY_CHARACTERISTIC_UUID "abc3483e-36e1-4688-b7f5-ea07361b26a8"
#define WRITE_CHARACTERISTIC_UUID "3cba5540-1d0b-4b0b-929f-81c2ec474cf4"
#define SAMPLE_RATE 16000U
#define SAMPLE_BITS 16
#define RECORD_TIME 5 // seconds
bool deviceConnected = false;
bool captureRequest = false;
bool recordingRequest = false;
int maxPacketSize = 509; // BLE packet size limit
I2SClass I2S;
BLEServer *pServer;
BLECharacteristic *pNotifyCharacteristic;
BLECharacteristic *pWriteCharacteristic;
// BLE Callbacks to track connection/disconnection
class MyServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
Serial.println("Device connected!");
}
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
Serial.println("Device disconnected!");
BLEDevice::startAdvertising();
}
};
// BLE Write Callback
class MyWriteCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
String value = pCharacteristic->getValue();
if (value == "START_RECORDING") {
Serial.println("Recording request received.");
recordingRequest = true; // Set flag to start recording
}
}
};
// Function to record and stream audio over BLE for 20 seconds
void recordAndStreamAudio() {
uint32_t record_size = (SAMPLE_RATE * SAMPLE_BITS / 8) * RECORD_TIME; // 20 seconds buffer
uint8_t *rec_buffer = (uint8_t *)ps_malloc(record_size); // Allocate buffer for recording
if (rec_buffer == NULL) {
Serial.println("Audio buffer malloc failed!");
return;
}
Serial.printf("Recording for %d seconds...\n", RECORD_TIME);
// Start recording
uint32_t sample_size = I2S.readBytes((char*)rec_buffer, record_size);
if (sample_size == 0) {
Serial.println("Recording failed!");
free(rec_buffer);
return;
} else {
Serial.printf("Recorded %d bytes\n", sample_size);
}
// Stream recorded audio in chunks over BLE
size_t offset = 0;
while (offset < sample_size) {
size_t chunk_size = min((size_t)maxPacketSize, (size_t)(sample_size - offset));
uint8_t* chunk_data = rec_buffer + offset;
pNotifyCharacteristic->setValue(chunk_data, chunk_size);
pNotifyCharacteristic->notify();
offset += chunk_size;
delay(30); // Small delay to avoid overwhelming BLE
}
// Send "END" to signal the end of transmission
const char* terminator = "END_AUDIO";
pNotifyCharacteristic->setValue((uint8_t*)terminator, 9);
pNotifyCharacteristic->notify();
free(rec_buffer); // Release buffer memory
Serial.println("Audio sent successfully over BLE.");
}
void setup() {
Serial.begin(115200);
// Initialize BLE
BLEDevice::init("Mira_Glasses");
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
// Notify Characteristic for sending audio data
pNotifyCharacteristic = pService->createCharacteristic(
NOTIFY_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_NOTIFY);
pNotifyCharacteristic->addDescriptor(new BLE2902());
// Write Characteristic for receiving recording requests
pWriteCharacteristic = pService->createCharacteristic(
WRITE_CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_WRITE);
pWriteCharacteristic->setCallbacks(new MyWriteCallbacks());
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
BLEDevice::startAdvertising();
Serial.println("Waiting for client to connect...");
// Setup I2S
I2S.setPinsPdmRx(42, 41);
if (!I2S.begin(I2S_MODE_PDM_RX, SAMPLE_RATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO)) {
Serial.println("Failed to initialize I2S!");
while (1);
}
}
void loop() {
if (deviceConnected && recordingRequest) {
recordAndStreamAudio(); // Record and stream audio when request is received
recordingRequest = false; // Reset the request flag after recording
}
}
Receiving streamed data and playing with this code:
package
import android.media.AudioFormat
import android.media.AudioManager
import android.media.AudioTrack
import android.util.Log
private lateinit var audioTrack: AudioTrack
private const val SAMPLE_RATE = 16000
private const val CHANNEL_CONFIG = AudioFormat.CHANNEL_OUT_MONO
private const val AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT
fun initAudioTrack() {
val bufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT)
audioTrack = AudioTrack( AudioManager.STREAM_MUSIC,
SAMPLE_RATE,
CHANNEL_CONFIG,
AUDIO_FORMAT,
bufferSize,
AudioTrack.MODE_STREAM
)
audioTrack.play()
}
fun playAudio(pcmData: ByteArray) {
// Write the PCM data to AudioTrack for playback
val written = audioTrack.write(pcmData, 0, pcmData.size)
if (written < 0) {
Log.e("AudioTrack", "Error writing audio data: $written")
}
}
fun releaseAudioTrack() {
audioTrack.stop()
audioTrack.release()
}
But it is only playing noise. Only NOISE. I measured the pitch, and pitch matches with what actually I am speaking, but literally we can't understand single recorded word.
My goal is to play the streamed audio data in realtime. My IoT device is Little Endian and I guess Android also works on Little Endian, so probably that could not be the reason of noise.
I believe configuration params such as Sample rate, channel etc. are same on IoT device and Android, so it should work as intended, but why it is not working?
Thank you
Upvotes: 0
Views: 26