1dfa32746SMatthias Ringwald /*
2dfa32746SMatthias Ringwald * Copyright (C) 2018 BlueKitchen GmbH
3dfa32746SMatthias Ringwald *
4dfa32746SMatthias Ringwald * Redistribution and use in source and binary forms, with or without
5dfa32746SMatthias Ringwald * modification, are permitted provided that the following conditions
6dfa32746SMatthias Ringwald * are met:
7dfa32746SMatthias Ringwald *
8dfa32746SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright
9dfa32746SMatthias Ringwald * notice, this list of conditions and the following disclaimer.
10dfa32746SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright
11dfa32746SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the
12dfa32746SMatthias Ringwald * documentation and/or other materials provided with the distribution.
13dfa32746SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of
14dfa32746SMatthias Ringwald * contributors may be used to endorse or promote products derived
15dfa32746SMatthias Ringwald * from this software without specific prior written permission.
16dfa32746SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for
17dfa32746SMatthias Ringwald * personal benefit and not for any commercial purpose or for
18dfa32746SMatthias Ringwald * monetary gain.
19dfa32746SMatthias Ringwald *
20dfa32746SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21dfa32746SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22dfa32746SMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25dfa32746SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26dfa32746SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27dfa32746SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28dfa32746SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29dfa32746SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30dfa32746SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31dfa32746SMatthias Ringwald * SUCH DAMAGE.
32dfa32746SMatthias Ringwald *
33dfa32746SMatthias Ringwald * Please inquire about commercial licensing options at
34dfa32746SMatthias Ringwald * [email protected]
35dfa32746SMatthias Ringwald *
36dfa32746SMatthias Ringwald */
37dfa32746SMatthias Ringwald
38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "btstack_audio_embedded.c"
39dfa32746SMatthias Ringwald
40dfa32746SMatthias Ringwald /*
41dfa32746SMatthias Ringwald * btstack_audio_embedded.c
42dfa32746SMatthias Ringwald *
43dfa32746SMatthias Ringwald * Implementation of btstack_audio.h using hal_audio.h for embedded run loop
44dfa32746SMatthias Ringwald *
45dfa32746SMatthias Ringwald */
46dfa32746SMatthias Ringwald
470ef4369cSMatthias Ringwald #include "btstack_config.h"
480ef4369cSMatthias Ringwald
49f0b78f48SMatthias Ringwald // allow to compile all files in embedded folder even if there's no audio support
50f0b78f48SMatthias Ringwald #ifdef HAVE_HAL_AUDIO
51f0b78f48SMatthias Ringwald
52dfa32746SMatthias Ringwald #include "btstack_debug.h"
53dfa32746SMatthias Ringwald #include "btstack_audio.h"
54dfa32746SMatthias Ringwald #include "btstack_run_loop_embedded.h"
55dfa32746SMatthias Ringwald #include "hal_audio.h"
56dfa32746SMatthias Ringwald
57f0b78f48SMatthias Ringwald #include <stddef.h>
58dfa32746SMatthias Ringwald
59dfa32746SMatthias Ringwald #define DRIVER_POLL_INTERVAL_MS 5
60dfa32746SMatthias Ringwald
61dfa32746SMatthias Ringwald // client
62dfa32746SMatthias Ringwald static void (*playback_callback)(int16_t * buffer, uint16_t num_samples);
63fb90eb3fSMatthias Ringwald static void (*recording_callback)(const int16_t * buffer, uint16_t num_samples);
64dfa32746SMatthias Ringwald
65dfa32746SMatthias Ringwald // timer to fill output ring buffer
660402264cSMatthias Ringwald static btstack_timer_source_t driver_timer_sink;
67fb90eb3fSMatthias Ringwald static btstack_timer_source_t driver_timer_source;
68dfa32746SMatthias Ringwald
69dfa32746SMatthias Ringwald static volatile unsigned int output_buffer_to_play;
70dfa32746SMatthias Ringwald static unsigned int output_buffer_to_fill;
71dfa32746SMatthias Ringwald static unsigned int output_buffer_count;
72dfa32746SMatthias Ringwald static unsigned int output_buffer_samples;
73dfa32746SMatthias Ringwald
74fb90eb3fSMatthias Ringwald static volatile int input_buffer_ready;
75fb90eb3fSMatthias Ringwald static volatile const int16_t * input_buffer_samples;
76fb90eb3fSMatthias Ringwald static volatile uint16_t input_buffer_num_samples;
77fb90eb3fSMatthias Ringwald
786fd4ca9eSMatthias Ringwald static int source_active;
796fd4ca9eSMatthias Ringwald static int sink_active;
806fd4ca9eSMatthias Ringwald
81329d55f3SMilanka Ringwald // Support for stereo-only audio hal
82329d55f3SMilanka Ringwald #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
83329d55f3SMilanka Ringwald static bool output_duplicate_samples;
84329d55f3SMilanka Ringwald #endif
85329d55f3SMilanka Ringwald
btstack_audio_audio_played(uint8_t buffer_index)86dfa32746SMatthias Ringwald static void btstack_audio_audio_played(uint8_t buffer_index){
87dfa32746SMatthias Ringwald output_buffer_to_play = (buffer_index + 1) % output_buffer_count;
88dfa32746SMatthias Ringwald }
89dfa32746SMatthias Ringwald
btstack_audio_audio_recorded(const int16_t * samples,uint16_t num_samples)90dfa32746SMatthias Ringwald static void btstack_audio_audio_recorded(const int16_t * samples, uint16_t num_samples){
91fb90eb3fSMatthias Ringwald input_buffer_samples = samples;
92fb90eb3fSMatthias Ringwald input_buffer_num_samples = num_samples;
93fb90eb3fSMatthias Ringwald input_buffer_ready = 1;
94dfa32746SMatthias Ringwald }
95dfa32746SMatthias Ringwald
driver_timer_handler_sink(btstack_timer_source_t * ts)960402264cSMatthias Ringwald static void driver_timer_handler_sink(btstack_timer_source_t * ts){
97dfa32746SMatthias Ringwald
98dfa32746SMatthias Ringwald // playback buffer ready to fill
990402264cSMatthias Ringwald if (output_buffer_to_play != output_buffer_to_fill){
100ff0081eeSMatthias Ringwald int16_t * buffer = hal_audio_sink_get_output_buffer(output_buffer_to_fill);
101dfa32746SMatthias Ringwald (*playback_callback)(buffer, output_buffer_samples);
102dfa32746SMatthias Ringwald
103329d55f3SMilanka Ringwald #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
104329d55f3SMilanka Ringwald if (output_duplicate_samples){
105329d55f3SMilanka Ringwald unsigned int i = output_buffer_samples;
106329d55f3SMilanka Ringwald do {
107329d55f3SMilanka Ringwald i--;
108329d55f3SMilanka Ringwald int16_t sample = buffer[i];
109329d55f3SMilanka Ringwald buffer[2*i + 0] = sample;
110329d55f3SMilanka Ringwald buffer[2*i + 1] = sample;
111329d55f3SMilanka Ringwald } while ( i > 0 );
112329d55f3SMilanka Ringwald }
113329d55f3SMilanka Ringwald #endif
114329d55f3SMilanka Ringwald
115dfa32746SMatthias Ringwald // next
116dfa32746SMatthias Ringwald output_buffer_to_fill = (output_buffer_to_fill + 1 ) % output_buffer_count;
117dfa32746SMatthias Ringwald }
118dfa32746SMatthias Ringwald
119dfa32746SMatthias Ringwald // re-set timer
120dfa32746SMatthias Ringwald btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
121dfa32746SMatthias Ringwald btstack_run_loop_add_timer(ts);
122dfa32746SMatthias Ringwald }
123dfa32746SMatthias Ringwald
driver_timer_handler_source(btstack_timer_source_t * ts)1240402264cSMatthias Ringwald static void driver_timer_handler_source(btstack_timer_source_t * ts){
125fb90eb3fSMatthias Ringwald // deliver samples if ready
126fb90eb3fSMatthias Ringwald if (input_buffer_ready){
127fb90eb3fSMatthias Ringwald (*recording_callback)((const int16_t *)input_buffer_samples, input_buffer_num_samples);
128fb90eb3fSMatthias Ringwald input_buffer_ready = 0;
1290402264cSMatthias Ringwald }
130fb90eb3fSMatthias Ringwald
131fb90eb3fSMatthias Ringwald // re-set timer
132fb90eb3fSMatthias Ringwald btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
133fb90eb3fSMatthias Ringwald btstack_run_loop_add_timer(ts);
134fb90eb3fSMatthias Ringwald }
1350402264cSMatthias Ringwald
13632d9a3dfSDirk Helbig
13732d9a3dfSDirk Helbig static uint32_t sink_samplerate = 0;
13832d9a3dfSDirk Helbig
btstack_audio_embedded_sink_init(uint8_t channels,uint32_t samplerate,void (* playback)(int16_t * buffer,uint16_t num_samples))1390402264cSMatthias Ringwald static int btstack_audio_embedded_sink_init(
140dfa32746SMatthias Ringwald uint8_t channels,
141dfa32746SMatthias Ringwald uint32_t samplerate,
1420402264cSMatthias Ringwald void (*playback)(int16_t * buffer, uint16_t num_samples)
143dfa32746SMatthias Ringwald ){
144329d55f3SMilanka Ringwald btstack_assert(playback != NULL);
145329d55f3SMilanka Ringwald btstack_assert(channels != 0);
146329d55f3SMilanka Ringwald
147329d55f3SMilanka Ringwald #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
148329d55f3SMilanka Ringwald // always use stereo from hal, duplicate samples if needed
14938b1a761SMatthias Ringwald output_duplicate_samples = channels != 2;
150329d55f3SMilanka Ringwald channels = 2;
151329d55f3SMilanka Ringwald #endif
152dfa32746SMatthias Ringwald
1530402264cSMatthias Ringwald playback_callback = playback;
15432d9a3dfSDirk Helbig sink_samplerate = samplerate;
155ff0081eeSMatthias Ringwald hal_audio_sink_init(channels, samplerate, &btstack_audio_audio_played);
156dfa32746SMatthias Ringwald
157dfa32746SMatthias Ringwald return 0;
158dfa32746SMatthias Ringwald }
159dfa32746SMatthias Ringwald
btstack_audio_embedded_sink_get_samplerate()16032d9a3dfSDirk Helbig static uint32_t btstack_audio_embedded_sink_get_samplerate() {
161*2d8556b2SDirk Helbig return BSP_AUDIO_OUT_GetFrequency();
16232d9a3dfSDirk Helbig }
16332d9a3dfSDirk Helbig
16432d9a3dfSDirk Helbig static uint32_t source_samplerate = 0;
16532d9a3dfSDirk Helbig
btstack_audio_embedded_source_init(uint8_t channels,uint32_t samplerate,void (* recording)(const int16_t * buffer,uint16_t num_samples))1660402264cSMatthias Ringwald static int btstack_audio_embedded_source_init(
1670402264cSMatthias Ringwald uint8_t channels,
1680402264cSMatthias Ringwald uint32_t samplerate,
1690402264cSMatthias Ringwald void (*recording)(const int16_t * buffer, uint16_t num_samples)
1700402264cSMatthias Ringwald ){
1710402264cSMatthias Ringwald if (!recording){
1720402264cSMatthias Ringwald log_error("No recording callback");
1730402264cSMatthias Ringwald return 1;
1740402264cSMatthias Ringwald }
175fb90eb3fSMatthias Ringwald
176fb90eb3fSMatthias Ringwald recording_callback = recording;
17732d9a3dfSDirk Helbig source_samplerate = samplerate;
178fb90eb3fSMatthias Ringwald hal_audio_source_init(channels, samplerate, &btstack_audio_audio_recorded);
179fb90eb3fSMatthias Ringwald
1800402264cSMatthias Ringwald return 0;
1810402264cSMatthias Ringwald }
182dfa32746SMatthias Ringwald
btstack_audio_embedded_source_get_samplerate()18332d9a3dfSDirk Helbig static uint32_t btstack_audio_embedded_source_get_samplerate() {
18432d9a3dfSDirk Helbig return source_samplerate;
18532d9a3dfSDirk Helbig }
18632d9a3dfSDirk Helbig
btstack_audio_embedded_sink_set_volume(uint8_t volume)1871b7f8fa1SMatthias Ringwald static void btstack_audio_embedded_sink_set_volume(uint8_t volume){
1881b7f8fa1SMatthias Ringwald UNUSED(volume);
1891b7f8fa1SMatthias Ringwald }
1901b7f8fa1SMatthias Ringwald
btstack_audio_embedded_source_set_gain(uint8_t gain)1911b7f8fa1SMatthias Ringwald static void btstack_audio_embedded_source_set_gain(uint8_t gain){
1921b7f8fa1SMatthias Ringwald UNUSED(gain);
1931b7f8fa1SMatthias Ringwald }
1941b7f8fa1SMatthias Ringwald
btstack_audio_embedded_sink_start_stream(void)1950402264cSMatthias Ringwald static void btstack_audio_embedded_sink_start_stream(void){
196ff0081eeSMatthias Ringwald output_buffer_count = hal_audio_sink_get_num_output_buffers();
197ff0081eeSMatthias Ringwald output_buffer_samples = hal_audio_sink_get_num_output_buffer_samples();
198dfa32746SMatthias Ringwald
199329d55f3SMilanka Ringwald btstack_assert(output_buffer_samples > 0);
200329d55f3SMilanka Ringwald
201dfa32746SMatthias Ringwald // pre-fill HAL buffers
202dfa32746SMatthias Ringwald uint16_t i;
203dfa32746SMatthias Ringwald for (i=0;i<output_buffer_count;i++){
204ff0081eeSMatthias Ringwald int16_t * buffer = hal_audio_sink_get_output_buffer(i);
205dfa32746SMatthias Ringwald (*playback_callback)(buffer, output_buffer_samples);
206dfa32746SMatthias Ringwald }
207dfa32746SMatthias Ringwald
208dfa32746SMatthias Ringwald output_buffer_to_play = 0;
209dfa32746SMatthias Ringwald output_buffer_to_fill = 0;
210dfa32746SMatthias Ringwald
211dfa32746SMatthias Ringwald // start playback
212ff0081eeSMatthias Ringwald hal_audio_sink_start();
213dfa32746SMatthias Ringwald
214dfa32746SMatthias Ringwald // start timer
2150402264cSMatthias Ringwald btstack_run_loop_set_timer_handler(&driver_timer_sink, &driver_timer_handler_sink);
2160402264cSMatthias Ringwald btstack_run_loop_set_timer(&driver_timer_sink, DRIVER_POLL_INTERVAL_MS);
2170402264cSMatthias Ringwald btstack_run_loop_add_timer(&driver_timer_sink);
2186fd4ca9eSMatthias Ringwald
2196fd4ca9eSMatthias Ringwald // state
2206fd4ca9eSMatthias Ringwald sink_active = 1;
221dfa32746SMatthias Ringwald }
222dfa32746SMatthias Ringwald
btstack_audio_embedded_source_start_stream(void)2230402264cSMatthias Ringwald static void btstack_audio_embedded_source_start_stream(void){
224fb90eb3fSMatthias Ringwald // just started, no data ready
225fb90eb3fSMatthias Ringwald input_buffer_ready = 0;
226fb90eb3fSMatthias Ringwald
227fb90eb3fSMatthias Ringwald // start recording
228fb90eb3fSMatthias Ringwald hal_audio_source_start();
229fb90eb3fSMatthias Ringwald
230fb90eb3fSMatthias Ringwald // start timer
231fb90eb3fSMatthias Ringwald btstack_run_loop_set_timer_handler(&driver_timer_source, &driver_timer_handler_source);
232fb90eb3fSMatthias Ringwald btstack_run_loop_set_timer(&driver_timer_source, DRIVER_POLL_INTERVAL_MS);
233fb90eb3fSMatthias Ringwald btstack_run_loop_add_timer(&driver_timer_source);
2346fd4ca9eSMatthias Ringwald
2356fd4ca9eSMatthias Ringwald // state
2366fd4ca9eSMatthias Ringwald source_active = 1;
2376fd4ca9eSMatthias Ringwald }
2386fd4ca9eSMatthias Ringwald
btstack_audio_embedded_sink_stop_stream(void)2396fd4ca9eSMatthias Ringwald static void btstack_audio_embedded_sink_stop_stream(void){
2406fd4ca9eSMatthias Ringwald // stop stream
2416fd4ca9eSMatthias Ringwald hal_audio_sink_stop();
2426fd4ca9eSMatthias Ringwald // stop timer
2436fd4ca9eSMatthias Ringwald btstack_run_loop_remove_timer(&driver_timer_sink);
2446fd4ca9eSMatthias Ringwald // state
2456fd4ca9eSMatthias Ringwald sink_active = 0;
2466fd4ca9eSMatthias Ringwald }
2476fd4ca9eSMatthias Ringwald
btstack_audio_embedded_source_stop_stream(void)2486fd4ca9eSMatthias Ringwald static void btstack_audio_embedded_source_stop_stream(void){
2496fd4ca9eSMatthias Ringwald // stop stream
2506fd4ca9eSMatthias Ringwald hal_audio_source_stop();
2516fd4ca9eSMatthias Ringwald // stop timer
2526fd4ca9eSMatthias Ringwald btstack_run_loop_remove_timer(&driver_timer_source);
2536fd4ca9eSMatthias Ringwald // state
2546fd4ca9eSMatthias Ringwald source_active = 0;
2550402264cSMatthias Ringwald }
2560402264cSMatthias Ringwald
btstack_audio_embedded_sink_close(void)2570402264cSMatthias Ringwald static void btstack_audio_embedded_sink_close(void){
2586fd4ca9eSMatthias Ringwald // stop stream if needed
2596fd4ca9eSMatthias Ringwald if (sink_active){
2606fd4ca9eSMatthias Ringwald btstack_audio_embedded_sink_stop_stream();
2616fd4ca9eSMatthias Ringwald }
262dfa32746SMatthias Ringwald // close HAL
263ff0081eeSMatthias Ringwald hal_audio_sink_close();
264dfa32746SMatthias Ringwald }
265dfa32746SMatthias Ringwald
btstack_audio_embedded_source_close(void)2660402264cSMatthias Ringwald static void btstack_audio_embedded_source_close(void){
2676fd4ca9eSMatthias Ringwald // stop stream if needed
2686fd4ca9eSMatthias Ringwald if (source_active){
2696fd4ca9eSMatthias Ringwald btstack_audio_embedded_source_stop_stream();
2706fd4ca9eSMatthias Ringwald }
271fb90eb3fSMatthias Ringwald // close HAL
272fb90eb3fSMatthias Ringwald hal_audio_source_close();
2730402264cSMatthias Ringwald }
2740402264cSMatthias Ringwald
2750402264cSMatthias Ringwald static const btstack_audio_sink_t btstack_audio_embedded_sink = {
276c61b3cefSMatthias Ringwald .init = &btstack_audio_embedded_sink_init,
27732d9a3dfSDirk Helbig .get_samplerate = &btstack_audio_embedded_sink_get_samplerate,
278c61b3cefSMatthias Ringwald .set_volume = &btstack_audio_embedded_sink_set_volume,
279c61b3cefSMatthias Ringwald .start_stream = &btstack_audio_embedded_sink_start_stream,
280c61b3cefSMatthias Ringwald .stop_stream = &btstack_audio_embedded_sink_stop_stream,
281c61b3cefSMatthias Ringwald .close = &btstack_audio_embedded_sink_close
282dfa32746SMatthias Ringwald };
283dfa32746SMatthias Ringwald
2840402264cSMatthias Ringwald static const btstack_audio_source_t btstack_audio_embedded_source = {
285c61b3cefSMatthias Ringwald .init = &btstack_audio_embedded_source_init,
28632d9a3dfSDirk Helbig .get_samplerate = &btstack_audio_embedded_source_get_samplerate,
287c61b3cefSMatthias Ringwald .set_gain = &btstack_audio_embedded_source_set_gain,
288c61b3cefSMatthias Ringwald .start_stream = &btstack_audio_embedded_source_start_stream,
289c61b3cefSMatthias Ringwald .stop_stream = &btstack_audio_embedded_source_stop_stream,
290c61b3cefSMatthias Ringwald .close = &btstack_audio_embedded_source_close
2910402264cSMatthias Ringwald };
2920402264cSMatthias Ringwald
btstack_audio_embedded_sink_get_instance(void)2930402264cSMatthias Ringwald const btstack_audio_sink_t * btstack_audio_embedded_sink_get_instance(void){
2940402264cSMatthias Ringwald return &btstack_audio_embedded_sink;
2950402264cSMatthias Ringwald }
2960402264cSMatthias Ringwald
btstack_audio_embedded_source_get_instance(void)2970402264cSMatthias Ringwald const btstack_audio_source_t * btstack_audio_embedded_source_get_instance(void){
2980402264cSMatthias Ringwald return &btstack_audio_embedded_source;
299dfa32746SMatthias Ringwald }
300dfa32746SMatthias Ringwald
301dfa32746SMatthias Ringwald #endif
302