mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-04-17 11:42:34 +00:00
a2dp_sink_demo: use linear resampling to fix samplerate drift
This commit is contained in:
parent
c21cc0eb6e
commit
e9c5f44ee8
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){
|
||||
#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_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){
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
Loading…
x
Reference in New Issue
Block a user