From 9eddf5e891d3c2342e7a0998f934301e15e032a3 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 22 Feb 2019 22:13:12 +0100 Subject: [PATCH] new audio_duplex example to quickly test audio implementation --- example/Makefile.inc | 4 ++ example/audio_duplex.c | 139 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 example/audio_duplex.c diff --git a/example/Makefile.inc b/example/Makefile.inc index 0a89d133f..d67674c59 100644 --- a/example/Makefile.inc +++ b/example/Makefile.inc @@ -114,6 +114,7 @@ HXCMOD_PLAYER = \ nao-deceased_by_disease.c \ EXAMPLES = \ + audio_duplex \ a2dp_sink_demo \ a2dp_source_demo \ ancs_client_demo \ @@ -332,6 +333,9 @@ mod_player: ${CORE_OBJ} ${COMMON_OBJ} ${HXCMOD_PLAYER_OBJ} btstack_audio.o mod_p sine_player: ${CORE_OBJ} ${COMMON_OBJ} btstack_audio.o sine_player.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ +audio_duplex: ${CORE_OBJ} ${COMMON_OBJ} btstack_audio.o btstack_ring_buffer.o audio_duplex.c + ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ + nordic_spp_le_counter: nordic_spp_le_counter.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${SM_OBJ} nordic_spp_service_server.o nordic_spp_le_counter.c ${CC} $(filter-out nordic_spp_le_counter.h,$^) ${CFLAGS} ${LDFLAGS} -o $@ diff --git a/example/audio_duplex.c b/example/audio_duplex.c new file mode 100644 index 000000000..d08350156 --- /dev/null +++ b/example/audio_duplex.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2014 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * Audio Duplex: forward audio from BTstack audio source to audio sink - test for audio interface + * + */ + +#include "btstack.h" + +// samplerate +const uint32_t samplerate = 16000; + +// ring buffer for audio +#define BUFFER_SAMPLES 1024 +static uint16_t audio_buffer_storage[BUFFER_SAMPLES]; +static btstack_ring_buffer_t audio_buffer; + +// mono buffer +#define MONO_BUFFER_LEN 128 +static int16_t mono_buffer[MONO_BUFFER_LEN]; + +// playback starts after audio_buffer is half full +static int playback_started; + +// sample couners +static int count_recording; +static int count_playback; + +static void audio_recording(const int16_t * pcm_buffer, uint16_t num_samples_to_write){ + count_recording += num_samples_to_write; + int err = btstack_ring_buffer_write(&audio_buffer, (uint8_t *) pcm_buffer, num_samples_to_write * 2); + if (err){ + printf("Failed to store %u samples\n", num_samples_to_write); + } +} + +static void audio_playback(int16_t * pcm_buffer, uint16_t num_samples_to_write){ + int num_samples_in_buffer = btstack_ring_buffer_bytes_available(&audio_buffer) / 2; + if (playback_started == 0){ + if ( num_samples_in_buffer < (BUFFER_SAMPLES / 2)) return; + playback_started = 1; + } + count_playback += num_samples_to_write; + while (num_samples_to_write){ + num_samples_in_buffer = btstack_ring_buffer_bytes_available(&audio_buffer) / 2; + int num_samples_ready = btstack_min(num_samples_in_buffer, num_samples_to_write); + // limit by mono_buffer + int num_samples_from_buffer = btstack_min(num_samples_ready, MONO_BUFFER_LEN); + if (!num_samples_from_buffer) break; + uint32_t bytes_read; + btstack_ring_buffer_read(&audio_buffer, (uint8_t *) mono_buffer, num_samples_from_buffer * 2, &bytes_read); + // duplicate samples for stereo output + int i; + for (i=0; i < num_samples_from_buffer;i++){ + *pcm_buffer++ = mono_buffer[i]; + *pcm_buffer++ = mono_buffer[i]; + num_samples_to_write--; + } + } + + // warn about underrun + if (num_samples_to_write){ + printf("Buffer underrun - recording %u, playback %u - delta %d!\n", count_recording, count_playback, count_recording - count_playback); + } + + // fill rest with silence + while (num_samples_to_write){ + *pcm_buffer++ = 0; + *pcm_buffer++ = 0; + num_samples_to_write--; + } +} + +int btstack_main(int argc, const char * argv[]); +int btstack_main(int argc, const char * argv[]){ + (void)argc; + (void)argv; + + // check audio interface + const btstack_audio_sink_t * audio_sink = btstack_audio_sink_get_instance(); + if (!audio_sink){ + printf("BTstack Audio Sink not setup\n"); + return 10; + } + + const btstack_audio_source_t * audio_source = btstack_audio_source_get_instance(); + if (!audio_source){ + printf("BTstack Audio Source not setup\n"); + return 10; + } + + // prepare audio buffer + btstack_ring_buffer_init(&audio_buffer, (uint8_t*) &audio_buffer_storage[0], sizeof(audio_buffer_storage)); + + // setup audio: mono input -> stereo output + audio_source->init(1, samplerate, &audio_recording); + audio_sink->init(2, samplerate, &audio_playback); + + // start duplex + audio_source->start_stream(); + audio_sink->start_stream(); + + return 0; +}