16bit@44.1kHz audio over http

So far I’m able to send 16bit@22050kHz stereo over http to an audio player or liquidsoap I’d like to speed it up to 44.1kHz, but how? Code in the current state below. I’m using a Wemos D1 @160MHz.

As I see no buffer under runs I assume the the Async webserver is the bottleneck. How can I measure that?
The sine wave function seems to idle a lot. How can I “direct” those clock cycles to the webserver?

Regarding the webserver, is it really needed to loop through the buffers or is there an other way?

It seems not possible to increase the ‘buffer size’ maxLen.

What ways are there to speed things up, besides a beefier processor? Other protocol? Barebones TCP and http tailored to icecast spec?

#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;

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){
  // implement full blown synth here, or get I2S data.
  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))/4;
      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++;
      if (cnt == (uint32_t) audio_res.samplerate){ //one second of audio
        cnt = 1;
      }
    } // find a better way to throttle data creation.
    while(audio_buffer.isFull()){
      Serial.println("Buffer Full!");
      yield();
    }  
  }
}

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){
          Serial.println(maxLen);
          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{ 
          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;
        }
      }
    );
    request->send(response);
  });
  server.begin();
  Serial.println("HTTP server started");
  sinewave(250.0);
}

void loop() {
}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.