a2dp_sink_demo: use linear resampling to fix samplerate drift

This commit is contained in:
Matthias Ringwald 2019-02-21 12:03:23 +01:00
parent c21cc0eb6e
commit e9c5f44ee8
7 changed files with 91 additions and 61 deletions

View File

@ -311,7 +311,7 @@ hid_mouse_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} btstack_r
a2dp_source_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_ENCODER_OBJ} ${AVDTP_OBJ} ${HXCMOD_PLAYER_OBJ} avrcp.o avrcp_target.o a2dp_source_demo.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
a2dp_sink_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_DECODER_OBJ} ${AVDTP_OBJ} avrcp.o avrcp_controller.o a2dp_sink_demo.c
a2dp_sink_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_DECODER_OBJ} ${AVDTP_OBJ} avrcp.o avrcp_controller.o btstack_resample.o a2dp_sink_demo.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
avrcp_browsing_client: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} avrcp.o avrcp_controller.o avrcp_browsing_controller.o avrcp_media_item_iterator.o avrcp_browsing_client.c

View File

@ -67,6 +67,7 @@
#include <string.h>
#include "btstack.h"
#include "btstack_resample.h"
//#define AVRCP_BROWSING_ENABLED
@ -94,15 +95,13 @@ static btstack_sbc_mode_t mode = SBC_MODE_STANDARD;
// below 30: add samples, 30-40: fine, above 40: drop samples
#define OPTIMAL_FRAMES_MIN 30
#define OPTIMAL_FRAMES_MAX 40
#define ADDITIONAL_FRAMES 10
#define ADDITIONAL_FRAMES 20
static uint8_t sbc_frame_storage[(OPTIMAL_FRAMES_MAX + ADDITIONAL_FRAMES) * MAX_SBC_FRAME_SIZE];
static btstack_ring_buffer_t sbc_frame_ring_buffer;
static unsigned int sbc_frame_size;
static int sbc_samples_fix;
// ring buffer for not fully used sbc frames
#define MAX_NUM_SBC_SAMPLES 128
static uint8_t decoded_audio_storage[(MAX_NUM_SBC_SAMPLES+4) * BYTES_PER_FRAME];
// rest buffer for not fully used sbc frames, with additional frames for resampling
static uint8_t decoded_audio_storage[(128+16) * BYTES_PER_FRAME];
static btstack_ring_buffer_t decoded_audio_ring_buffer;
//
@ -110,7 +109,9 @@ static int audio_stream_started;
// temp storage of lower-layer request
static int16_t * request_buffer;
static int request_samples;
static int request_frames;
#define STORE_FROM_PLAYBACK
// WAV File
#ifdef STORE_SBC_TO_WAV_FILE
@ -185,6 +186,7 @@ static uint8_t media_sbc_codec_configuration[] = {
2, 53
};
static btstack_resample_t resample_instance;
/* @section Main Application Setup
*
@ -255,76 +257,93 @@ static int a2dp_and_avrcp_setup(void){
return 0;
}
static void playback_handler(int16_t * buffer, uint16_t num_samples){
static void playback_handler(int16_t * buffer, uint16_t num_frames){
#ifdef STORE_FROM_PLAYBACK
#ifdef STORE_SBC_TO_WAV_FILE
int wav_samples = num_frames * NUM_CHANNELS;
int16_t * wav_buffer = buffer;
#endif
#endif
// called from lower-layer but guaranteed to be on main thread
// first fill from decoded_audio
// first fill from resampled audio
uint32_t bytes_read;
btstack_ring_buffer_read(&decoded_audio_ring_buffer, (uint8_t *) buffer, num_samples * BYTES_PER_FRAME, &bytes_read);
btstack_ring_buffer_read(&decoded_audio_ring_buffer, (uint8_t *) buffer, num_frames * BYTES_PER_FRAME, &bytes_read);
buffer += bytes_read / NUM_CHANNELS;
num_samples -= bytes_read / BYTES_PER_FRAME;
num_frames -= bytes_read / BYTES_PER_FRAME;
// then start decoding sbc frames using request_* globals
request_buffer = buffer;
request_samples = num_samples;
while (request_samples && btstack_ring_buffer_bytes_available(&sbc_frame_ring_buffer) >= sbc_frame_size){
// log_info("buffer %06u bytes -- need %d", btstack_ring_buffer_bytes_available(&sbc_frame_ring_buffer), request_samples);
request_frames = num_frames;
while (request_frames){
if (btstack_ring_buffer_bytes_available(&sbc_frame_ring_buffer) >= sbc_frame_size){
// decode frame
uint8_t frame[MAX_SBC_FRAME_SIZE];
btstack_ring_buffer_read(&sbc_frame_ring_buffer, frame, sbc_frame_size, &bytes_read);
btstack_sbc_decoder_process_data(&state, 0, frame, sbc_frame_size);
uint8_t sbc_frame[MAX_SBC_FRAME_SIZE];
btstack_ring_buffer_read(&sbc_frame_ring_buffer, sbc_frame, sbc_frame_size, &bytes_read);
btstack_sbc_decoder_process_data(&state, 0, sbc_frame, sbc_frame_size);
} else {
printf("Error: no SBC frame ready in ring buffer\n");
}
}
#ifdef STORE_FROM_PLAYBACK
#ifdef STORE_SBC_TO_WAV_FILE
wav_writer_write_int16(wav_samples, wav_buffer);
#endif
#endif
}
static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){
static void handle_pcm_data(int16_t * data, int num_frames, int num_channels, int sample_rate, void * context){
UNUSED(sample_rate);
UNUSED(context);
UNUSED(num_channels); // must be stereo == 2
#ifdef STORE_SBC_TO_WAV_FILE
wav_writer_write_int16(num_samples * NUM_CHANNELS, data);
frame_count++;
#ifdef STORE_DECODED
wav_writer_write_int16(num_frames * NUM_CHANNELS, data);
#endif
int sbc_samples_fix_applied = 0;
// resample into request buffer - add some additional space for resampling
int16_t output_buffer[(128+16) * NUM_CHANNELS]; // 16 * 8 * 2
uint32_t resampled_frames = btstack_resample_block(&resample_instance, data, num_frames, output_buffer);
// drop audio frame to fix drift
if (sbc_samples_fix < 0){
num_samples--;
data += NUM_CHANNELS;
sbc_samples_fix_applied = 1;
}
#ifdef STORE_RESAMPLED
#ifdef STORE_SBC_TO_WAV_FILE
wav_writer_write_int16(resampled_frames * NUM_CHANNELS, output_buffer);
frame_count++;
#endif
#endif
const btstack_audio_t * audio = btstack_audio_get_instance();
if (!audio) return;
// store data in btstack_audio buffer first
if (request_samples){
int frames_to_copy = btstack_min(resampled_frames, request_frames);
memcpy(request_buffer, output_buffer, frames_to_copy * BYTES_PER_FRAME);
request_frames -= frames_to_copy;
request_buffer += frames_to_copy * NUM_CHANNELS;
// add audio frame to fix drift
if (!sbc_samples_fix_applied && sbc_samples_fix > 0){
memcpy(request_buffer, data, BYTES_PER_FRAME);
request_samples--;
request_buffer += NUM_CHANNELS;
sbc_samples_fix_applied = 1;
}
int samples_to_copy = btstack_min(num_samples, request_samples);
memcpy(request_buffer, data, samples_to_copy * BYTES_PER_FRAME);
num_samples -= samples_to_copy;
request_samples -= samples_to_copy;
data += samples_to_copy * NUM_CHANNELS;
request_buffer += samples_to_copy * NUM_CHANNELS;
}
#ifdef STORE_BEFORE_PLAYBACK
#ifdef STORE_SBC_TO_WAV_FILE
wav_writer_write_int16(frames_to_copy * NUM_CHANNELS, output_buffer);
frame_count++;
#endif
#endif
// and rest in ring buffer
if (num_samples){
// add audio frame to fix drift
if (!sbc_samples_fix_applied && sbc_samples_fix > 0){
btstack_ring_buffer_write(&decoded_audio_ring_buffer, (uint8_t *) data, BYTES_PER_FRAME);
sbc_samples_fix_applied = 1;
int frames_to_store = resampled_frames - frames_to_copy;
if (frames_to_store){
int status = btstack_ring_buffer_write(&decoded_audio_ring_buffer, (uint8_t *)&output_buffer[frames_to_copy * NUM_CHANNELS], frames_to_store * BYTES_PER_FRAME);
if (status){
printf("Error storing samples in PCM ring buffer!!!\n");
}
btstack_ring_buffer_write(&decoded_audio_ring_buffer, (uint8_t *) data, num_samples * BYTES_PER_FRAME);
#ifdef STORE_BEFORE_PLAYBACK
#ifdef STORE_SBC_TO_WAV_FILE
wav_writer_write_int16(frames_to_store * NUM_CHANNELS, &output_buffer[frames_to_copy * NUM_CHANNELS]);
frame_count++;
#endif
#endif
}
}
@ -343,6 +362,7 @@ static int media_processing_init(avdtp_media_codec_configuration_sbc_t configura
btstack_ring_buffer_init(&sbc_frame_ring_buffer, sbc_frame_storage, sizeof(sbc_frame_storage));
btstack_ring_buffer_init(&decoded_audio_ring_buffer, decoded_audio_storage, sizeof(decoded_audio_storage));
btstack_resample_init(&resample_instance, configuration.num_channels);
// setup audio playback
const btstack_audio_t * audio = btstack_audio_get_instance();
@ -412,28 +432,34 @@ static void handle_l2cap_media_data_packet(uint8_t seid, uint8_t *packet, uint16
// store sbc frame size for buffer management
sbc_frame_size = (size-pos)/ sbc_header.num_frames;
btstack_ring_buffer_write(&sbc_frame_ring_buffer, packet+pos, size-pos);
int status = btstack_ring_buffer_write(&sbc_frame_ring_buffer, packet+pos, size-pos);
if (status){
printf("Error storing samples in SBC ring buffer!!!\n");
}
// decide on audio sync drift based on number of sbc frames in queue
int sbc_frames_in_buffer = btstack_ring_buffer_bytes_available(&sbc_frame_ring_buffer) / sbc_frame_size;
uint32_t resampling_factor;
if (sbc_frames_in_buffer < OPTIMAL_FRAMES_MIN){
sbc_samples_fix = 1; // duplicate last sample
resampling_factor = 0x0FE00; // stretch samples
} else if (sbc_frames_in_buffer <= OPTIMAL_FRAMES_MAX){
sbc_samples_fix = 0; // nothing to do
resampling_factor = 0x10000; // nothing to do
} else {
sbc_samples_fix = -1; // drop last sample
resampling_factor = 0x10200; // compress samples
}
btstack_resample_set_factor(&resample_instance, resampling_factor);
// dump
// printf("%6u %03u %d\n", (int) btstack_run_loop_get_time_ms(), sbc_frames_in_buffer, sbc_samples_fix);
// log_info("%03u %d", sbc_frames_in_buffer, sbc_samples_fix);
// printf("%6u %03u %05x\n", (int) btstack_run_loop_get_time_ms(), sbc_frames_in_buffer, resampling_factor);
// log_info("%03u %05x", sbc_frames_in_buffer, resampling_factor);
#ifdef STORE_SBC_TO_SBC_FILE
fwrite(packet+pos, size-pos, 1, sbc_file);
#endif
// start stream if enough frames buffered
if (!audio_stream_started && sbc_frames_in_buffer >= (OPTIMAL_FRAMES_MAX+OPTIMAL_FRAMES_MIN)/2){
if (!audio_stream_started && sbc_frames_in_buffer >= OPTIMAL_FRAMES_MIN){
audio_stream_started = 1;
// setup audio playback
if (audio){

View File

@ -37,8 +37,8 @@ csr_set_bd_addr: ${CORE_OBJ} ${COMMON_OBJ} btstack_chipset_csr.o csr_set_bd_addr
# use pkg-config for portaudio
# CFLAGS += $(shell pkg-config portaudio-2.0 --cflags) -DHAVE_PORTAUDIO
# LDFLAGS += $(shell pkg-config portaudio-2.0 --libs)
CFLAGS += $(shell pkg-config portaudio-2.0 --cflags) -DHAVE_PORTAUDIO
LDFLAGS += $(shell pkg-config portaudio-2.0 --libs)
# hard coded flags for portaudio in /usr/local/lib
# CFLAGS += -I/usr/local/include -DHAVE_PORTAUDIO

View File

@ -184,6 +184,7 @@ AVDTP += \
a2dp_source.c \
a2dp_sink.c \
btstack_ring_buffer.c \
btstack_resample.c \
avrcp.c \
avrcp_target.c \
avrcp_controller.c \

View File

@ -99,6 +99,7 @@ btstack_cvsd_plc.c \
btstack_linked_list.c \
btstack_memory.c \
btstack_memory_pool.c \
btstack_resample.c \
btstack_ring_buffer.c \
btstack_stdin_embedded.c \
btstack_run_loop.c \

View File

@ -47,6 +47,7 @@ $(NAME)_SOURCES += \
../../src/btstack_linked_list.c \
../../src/btstack_memory.c \
../../src/btstack_memory_pool.c \
../../src/btstack_resample.c \
../../src/btstack_run_loop.c \
../../src/btstack_tlv.c \
../../src/btstack_util.c \

View File

@ -47,6 +47,7 @@ $(NAME)_SOURCES += \
../../src/btstack_linked_list.c \
../../src/btstack_memory.c \
../../src/btstack_memory_pool.c \
../../src/btstack_resample.c \
../../src/btstack_run_loop.c \
../../src/btstack_util.c \
../../src/btstack_slip.c \