diff --git a/example/Makefile.inc b/example/Makefile.inc index 4d447630e..2d91fe67c 100644 --- a/example/Makefile.inc +++ b/example/Makefile.inc @@ -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 diff --git a/example/a2dp_sink_demo.c b/example/a2dp_sink_demo.c index de3a20e9c..0e19452e0 100644 --- a/example/a2dp_sink_demo.c +++ b/example/a2dp_sink_demo.c @@ -67,6 +67,7 @@ #include #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){ diff --git a/port/libusb/Makefile b/port/libusb/Makefile index 085b076d1..341d4bf20 100644 --- a/port/libusb/Makefile +++ b/port/libusb/Makefile @@ -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 diff --git a/port/max32630-fthr/example/template/Makefile b/port/max32630-fthr/example/template/Makefile index 4a283e0ec..953c0385c 100644 --- a/port/max32630-fthr/example/template/Makefile +++ b/port/max32630-fthr/example/template/Makefile @@ -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 \ diff --git a/port/stm32-f4discovery-cc256x/cubemx-f4discovery-cc256x/Makefile b/port/stm32-f4discovery-cc256x/cubemx-f4discovery-cc256x/Makefile index e13127aa5..f715d90ed 100644 --- a/port/stm32-f4discovery-cc256x/cubemx-f4discovery-cc256x/Makefile +++ b/port/stm32-f4discovery-cc256x/cubemx-f4discovery-cc256x/Makefile @@ -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 \ diff --git a/port/wiced-h4/wiced-h4.mk b/port/wiced-h4/wiced-h4.mk index 60ff39fc3..ddd644039 100644 --- a/port/wiced-h4/wiced-h4.mk +++ b/port/wiced-h4/wiced-h4.mk @@ -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 \ diff --git a/port/wiced-h5/wiced-h5.mk b/port/wiced-h5/wiced-h5.mk index 7c5eba9bb..2b74c5dd4 100644 --- a/port/wiced-h5/wiced-h5.mk +++ b/port/wiced-h5/wiced-h5.mk @@ -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 \