Async webserver chunked audio 16bit@44.1kHz
Posted: Sat Feb 13, 2021 7:53 am
There are several issues with the code below I can't get my head around.
When I set maxLength to 2048 all still runs fine. When I increase the buffer size from 1024 to 2048 it crashes. Why? How Can I increase the buffers?
Currently I can send audio 16bit@22kHz, but I'd the goal is 44.1kHz or even 48kHz. Hence the attempt to increase the buffer size. As I have no buffer under runs my guess the web server is the bottleneck. How can I measure that? Filling the data buffer in the sinewave function takes about 8ms.
Is looping the way to "fill" the webserver with data?
Are there other ways to speed things up?
When I set maxLength to 2048 all still runs fine. When I increase the buffer size from 1024 to 2048 it crashes. Why? How Can I increase the buffers?
Currently I can send audio 16bit@22kHz, but I'd the goal is 44.1kHz or even 48kHz. Hence the attempt to increase the buffer size. As I have no buffer under runs my guess the web server is the bottleneck. How can I measure that? Filling the data buffer in the sinewave function takes about 8ms.
Is looping the way to "fill" the webserver with data?
Are there other ways to speed things up?
Code: Select all
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <CircularBuffer.h>
const char *ssid = "***";
const char *password = "***";
AsyncWebServer server(80);
const uint16_t buffer_size = 1024; //256 - 1024. maxLen = 1064 but seems worse than 1024.
CircularBuffer<uint8_t, buffer_size> audio_buffer;
uint8_t makenoise = 1;
uint32_t t;
struct audio_spec{
float bitdepth = 16.0;
float samplerate = 22050; //44100.0; 32000;
uint16_t num_channels = 2;
float tick = 1/samplerate;
float twopitick = 2*PI*tick;
float amplitude = pow(2, bitdepth)/2.0; //0dB mind your ears!
} audio_res;
struct wave_header_pcm{
char riff_tag[4]{'R','I','F','F'};
uint32_t riff_length{4294967295};//4294967295
char wave_tag[4]{'W','A','V','E'};
char format_tag[4]{'f','m','t',' '}; //mind the space!!
uint32_t format_length{16};
uint16_t format_code{1};
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bit_depth;
char data_tag[4]{'d','a','t','a'};
uint32_t data_length{4294967295}; //4294967295 max length as we stream. Should resend the header when max is reached.
} header_pcm;
void fill_header(uint16_t bit_depth, uint32_t sample_rate, uint16_t num_channels){
header_pcm.num_channels = num_channels;
header_pcm.sample_rate = sample_rate;
header_pcm.byte_rate = sample_rate*(bit_depth/8)*num_channels;
header_pcm.block_align = bit_depth/8;
header_pcm.bit_depth = bit_depth;
}
void sinewave(float frequency){
static float f_twopitick = frequency*audio_res.twopitick;
static uint32_t cnt = 0;
while(makenoise){
while(!audio_buffer.isFull()){
int16_t amplitude = (audio_res.amplitude * sin(cnt*f_twopitick));
audio_buffer.push((amplitude >> 8 & 0xff));
audio_buffer.push((amplitude & 0xff));
if(audio_res.num_channels == 2){
audio_buffer.push((amplitude >> 8 & 0xff));
audio_buffer.push((amplitude & 0xff));
}
cnt++; //takes ~8ms
}
while(audio_buffer.isFull()){
//Serial.println("Buffer Full!");
yield();
// 2-5ms with every now and then a peak 1000+ and 3000+
}
}
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network\nIP Address: ");
Serial.println(WiFi.localIP());
fill_header(audio_res.bitdepth, audio_res.samplerate, audio_res.num_channels);
server.on("/sinewave", HTTP_GET, [](AsyncWebServerRequest * request) {
AsyncWebServerResponse* response = request->beginChunkedResponse(
"audio/wave",
[](uint8_t* buffer, size_t maxLen, size_t index){
size_t len;
// first chunk does not have the full maxLen available
// http-headers(?) Send these plus the WAVE header first.
// after that just the audio data
if(index == 0){
uint8_t* pcm_bytes = static_cast<uint8_t*>(static_cast<void*>(&header_pcm));
for (size_t i = 0; i < sizeof(header_pcm); i++){
buffer[i] = pcm_bytes[i];
}
len = (1064 - maxLen) + sizeof(header_pcm);
return len;
}
else{
//maxLen = 2048;
size_t len = buffer_size;
if (!audio_buffer.isEmpty()){
for (size_t i = 0; i < len; i++){
buffer[i] = audio_buffer.shift();
}
}
else{
while(audio_buffer.isEmpty()){
Serial.println("Buffer Empty!");
}
}
return len;
}
}
);
response->addHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response->addHeader("Pragma", "no-cache");
response->addHeader("Expires", "-1");
request->send(response);
});
server.begin();
Serial.println("HTTP server started");
sinewave(250.0);
}
void loop() {
}