pschatzmann / ESP32-A2DP

A Simple ESP32 Bluetooth A2DP Library (to implement a Music Receiver or Sender) that supports Arduino, PlatformIO and Espressif IDF
Apache License 2.0
1.63k stars 269 forks source link

24bit I2S unable to reach full range in output stream #138

Closed jameshamond closed 2 years ago

jameshamond commented 2 years ago

Using a logic analyser, I can confirm that at 24bit does not reach full volume when used with either an Android phone ("Signal generator app" set to full volume with square wave) or Windows 10 PC ("Frequency generator app set to square wave full volume). Both devices exhibit the same max values of ~+-5,142,528 (decimal), where closer to 8,388,608 is expected.

image

pschatzmann commented 2 years ago

I am not sure if this is an issue at all because nothing in any spec in the IDF (for A2DP and I2S expansion) would describe the effective value range.

Do you know, what would be the effective range with 16 bits ? Just to double check, please confirm that you are not using the volume functionality of this library ?

jameshamond commented 2 years ago

As an experiment, I changed settings to 16 bit. as before range is limited, to ~60% of full, now max is 17136 (below) when we would expect 2^(BITS-1) = 32768. Note (BITS-1) as this allows for the polarity bit. The full range is of course 2^(BITS) = ~65,000, or 0xffff.

I also note that with 16bit set, now only one channel is broadcast:

image

I've also followed these steps to ensure that andoird isn't reducing gain somehow: https://www.reddit.com/r/GooglePixel/comments/8hbcuu/the_100_solution_to_bluetooth_volume_issues/

I can confirm that no functionality other than that shown in the code block below is in use- so no additional volume settings:

#define BITS 16

// ==> Example to use external 32 bit DAC - We demonstrate how to create a subclass and override the audio_data_callback method

#include "BluetoothA2DPSink32.h"

BluetoothA2DPSink32 a2dp_sink; // Subclass of BluetoothA2DPSink

void setup() 
{
  static i2s_config_t i2s_config = {
  .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX),
  .sample_rate = 48000,//44100, // updated automatically by A2DP *** Note this change has no impact
  .bits_per_sample = (i2s_bits_per_sample_t)BITS,
  .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
  .communication_format = (i2s_comm_format_t) (I2S_COMM_FORMAT_STAND_I2S),
  .intr_alloc_flags = 0, // default interrupt priority
  .dma_buf_count = 8,
  .dma_buf_len = 256,//64,
  .use_apll = true,
  .tx_desc_auto_clear = true // avoiding noise in case of data unavailability
  };
  a2dp_sink.set_i2s_config(i2s_config);

  //a2dp_sink.set_bits_per_sample(BITS);  
  a2dp_sink.start("MyBTAudio");  
}

void loop() {
}

I also note that noe only one channel is working:

jameshamond commented 2 years ago

Can confirm that one channel drops out if chennel bits is set to 16, but with sample_rate left at 441000:

#define BITS 16

// ==> Example to use external 32 bit DAC - We demonstrate how to create a subclass and override the audio_data_callback method

#include "BluetoothA2DPSink32.h"

BluetoothA2DPSink32 a2dp_sink; // Subclass of BluetoothA2DPSink

void setup() 
{
  static i2s_config_t i2s_config = {
  .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX),
  .sample_rate = 44100, // updated automatically by A2DP
  .bits_per_sample = (i2s_bits_per_sample_t)BITS,
  .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
  .communication_format = (i2s_comm_format_t) (I2S_COMM_FORMAT_STAND_I2S),
  .intr_alloc_flags = 0, // default interrupt priority
  .dma_buf_count = 8,
  .dma_buf_len = 256,//64,
  .use_apll = true,
  .tx_desc_auto_clear = true // avoiding noise in case of data unavailability
  };
  a2dp_sink.set_i2s_config(i2s_config);

  //a2dp_sink.set_bits_per_sample(BITS);  
  a2dp_sink.start("MyBTAudio");  
}

void loop() {
}

Side note, while not full rail, windws 10 with "Frequency generator" able to reach slightly higher levels than android (~20k vs 16k): image

pschatzmann commented 2 years ago

One more question: when you set the log-level to debug in Arduino and you change the volume: Do you see any AVRC or volume related entries.

There are different ways to handle the volume depending on the capability of the partner and the ESP32-Arduino version.

ps. on 16 bits, I definitly get 2 channels. You should get the same result (instead of using an oscilloscope) by running the following example: https://github.com/pschatzmann/ESP32-A2DP/blob/main/examples/bt_music_receiver_datacallback/bt_music_receiver_datacallback.ino

This will show the PCM values which are received.

jameshamond commented 2 years ago

Having done more research, it appears my pixel 3 is part of the issue. It only transmits with SBC "quality". I don't know what A2DP then does, but this might explain why the PC performed slightly (though still poorly) better. In short, this is very unlikely to be an issue with the wrapper class, bluetooth audio has a long way to go before I can use it in pro audo gear. Thanks for the assistance.