xref: /btstack/platform/embedded/btstack_audio_embedded.c (revision f0b78f48e22dc8fc6c04986a7e0be8aa569355da)
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 
47*f0b78f48SMatthias Ringwald // allow to compile all files in embedded folder even if there's no audio support
48*f0b78f48SMatthias Ringwald #ifdef HAVE_HAL_AUDIO
49*f0b78f48SMatthias Ringwald 
50dfa32746SMatthias Ringwald #include "btstack_config.h"
51dfa32746SMatthias Ringwald #include "btstack_debug.h"
52dfa32746SMatthias Ringwald #include "btstack_audio.h"
53dfa32746SMatthias Ringwald #include "btstack_run_loop_embedded.h"
54dfa32746SMatthias Ringwald #include "hal_audio.h"
55dfa32746SMatthias Ringwald 
56*f0b78f48SMatthias Ringwald #include <stddef.h>
57dfa32746SMatthias Ringwald 
58dfa32746SMatthias Ringwald #define DRIVER_POLL_INTERVAL_MS          5
59dfa32746SMatthias Ringwald 
60dfa32746SMatthias Ringwald // client
61dfa32746SMatthias Ringwald static void (*playback_callback)(int16_t * buffer, uint16_t num_samples);
62fb90eb3fSMatthias Ringwald static void (*recording_callback)(const int16_t * buffer, uint16_t num_samples);
63dfa32746SMatthias Ringwald 
64dfa32746SMatthias Ringwald // timer to fill output ring buffer
650402264cSMatthias Ringwald static btstack_timer_source_t  driver_timer_sink;
66fb90eb3fSMatthias Ringwald static btstack_timer_source_t  driver_timer_source;
67dfa32746SMatthias Ringwald 
68dfa32746SMatthias Ringwald static volatile unsigned int output_buffer_to_play;
69dfa32746SMatthias Ringwald static          unsigned int output_buffer_to_fill;
70dfa32746SMatthias Ringwald static          unsigned int output_buffer_count;
71dfa32746SMatthias Ringwald static          unsigned int output_buffer_samples;
72dfa32746SMatthias Ringwald 
73fb90eb3fSMatthias Ringwald static volatile int       input_buffer_ready;
74fb90eb3fSMatthias Ringwald static volatile const int16_t * input_buffer_samples;
75fb90eb3fSMatthias Ringwald static volatile uint16_t  input_buffer_num_samples;
76fb90eb3fSMatthias Ringwald 
776fd4ca9eSMatthias Ringwald static int source_active;
786fd4ca9eSMatthias Ringwald static int sink_active;
796fd4ca9eSMatthias Ringwald 
80329d55f3SMilanka Ringwald // Support for stereo-only audio hal
81329d55f3SMilanka Ringwald #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
82329d55f3SMilanka Ringwald static bool output_duplicate_samples;
83329d55f3SMilanka Ringwald #endif
84329d55f3SMilanka Ringwald 
85dfa32746SMatthias Ringwald static void btstack_audio_audio_played(uint8_t buffer_index){
86dfa32746SMatthias Ringwald     output_buffer_to_play = (buffer_index + 1) % output_buffer_count;
87dfa32746SMatthias Ringwald }
88dfa32746SMatthias Ringwald 
89dfa32746SMatthias Ringwald static void btstack_audio_audio_recorded(const int16_t * samples, uint16_t num_samples){
90fb90eb3fSMatthias Ringwald     input_buffer_samples     = samples;
91fb90eb3fSMatthias Ringwald     input_buffer_num_samples = num_samples;
92fb90eb3fSMatthias Ringwald     input_buffer_ready       = 1;
93dfa32746SMatthias Ringwald }
94dfa32746SMatthias Ringwald 
950402264cSMatthias Ringwald static void driver_timer_handler_sink(btstack_timer_source_t * ts){
96dfa32746SMatthias Ringwald 
97dfa32746SMatthias Ringwald     // playback buffer ready to fill
980402264cSMatthias Ringwald     if (output_buffer_to_play != output_buffer_to_fill){
99ff0081eeSMatthias Ringwald         int16_t * buffer = hal_audio_sink_get_output_buffer(output_buffer_to_fill);
100dfa32746SMatthias Ringwald         (*playback_callback)(buffer, output_buffer_samples);
101dfa32746SMatthias Ringwald 
102329d55f3SMilanka Ringwald #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
103329d55f3SMilanka Ringwald         if (output_duplicate_samples){
104329d55f3SMilanka Ringwald             unsigned int i = output_buffer_samples;
105329d55f3SMilanka Ringwald             do {
106329d55f3SMilanka Ringwald                 i--;
107329d55f3SMilanka Ringwald                 int16_t sample = buffer[i];
108329d55f3SMilanka Ringwald                 buffer[2*i + 0] = sample;
109329d55f3SMilanka Ringwald                 buffer[2*i + 1] = sample;
110329d55f3SMilanka Ringwald             } while ( i > 0 );
111329d55f3SMilanka Ringwald         }
112329d55f3SMilanka Ringwald #endif
113329d55f3SMilanka Ringwald 
114dfa32746SMatthias Ringwald         // next
115dfa32746SMatthias Ringwald         output_buffer_to_fill = (output_buffer_to_fill + 1 ) % output_buffer_count;
116dfa32746SMatthias Ringwald     }
117dfa32746SMatthias Ringwald 
118dfa32746SMatthias Ringwald     // re-set timer
119dfa32746SMatthias Ringwald     btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
120dfa32746SMatthias Ringwald     btstack_run_loop_add_timer(ts);
121dfa32746SMatthias Ringwald }
122dfa32746SMatthias Ringwald 
1230402264cSMatthias Ringwald static void driver_timer_handler_source(btstack_timer_source_t * ts){
124fb90eb3fSMatthias Ringwald     // deliver samples if ready
125fb90eb3fSMatthias Ringwald     if (input_buffer_ready){
126fb90eb3fSMatthias Ringwald         (*recording_callback)((const int16_t *)input_buffer_samples, input_buffer_num_samples);
127fb90eb3fSMatthias Ringwald         input_buffer_ready = 0;
1280402264cSMatthias Ringwald     }
129fb90eb3fSMatthias Ringwald 
130fb90eb3fSMatthias Ringwald     // re-set timer
131fb90eb3fSMatthias Ringwald     btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
132fb90eb3fSMatthias Ringwald     btstack_run_loop_add_timer(ts);
133fb90eb3fSMatthias Ringwald }
1340402264cSMatthias Ringwald 
1350402264cSMatthias Ringwald static int btstack_audio_embedded_sink_init(
136dfa32746SMatthias Ringwald     uint8_t channels,
137dfa32746SMatthias Ringwald     uint32_t samplerate,
1380402264cSMatthias Ringwald     void (*playback)(int16_t * buffer, uint16_t num_samples)
139dfa32746SMatthias Ringwald ){
140329d55f3SMilanka Ringwald     btstack_assert(playback != NULL);
141329d55f3SMilanka Ringwald     btstack_assert(channels != 0);
142329d55f3SMilanka Ringwald 
143329d55f3SMilanka Ringwald #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
144329d55f3SMilanka Ringwald     // always use stereo from hal, duplicate samples if needed
14538b1a761SMatthias Ringwald     output_duplicate_samples = channels != 2;
146329d55f3SMilanka Ringwald     channels = 2;
147329d55f3SMilanka Ringwald #endif
148dfa32746SMatthias Ringwald 
1490402264cSMatthias Ringwald     playback_callback  = playback;
1500402264cSMatthias Ringwald 
151ff0081eeSMatthias Ringwald     hal_audio_sink_init(channels, samplerate, &btstack_audio_audio_played);
152dfa32746SMatthias Ringwald 
153dfa32746SMatthias Ringwald     return 0;
154dfa32746SMatthias Ringwald }
155dfa32746SMatthias Ringwald 
1560402264cSMatthias Ringwald static int btstack_audio_embedded_source_init(
1570402264cSMatthias Ringwald     uint8_t channels,
1580402264cSMatthias Ringwald     uint32_t samplerate,
1590402264cSMatthias Ringwald     void (*recording)(const int16_t * buffer, uint16_t num_samples)
1600402264cSMatthias Ringwald ){
1610402264cSMatthias Ringwald     if (!recording){
1620402264cSMatthias Ringwald         log_error("No recording callback");
1630402264cSMatthias Ringwald         return 1;
1640402264cSMatthias Ringwald     }
165fb90eb3fSMatthias Ringwald 
166fb90eb3fSMatthias Ringwald     recording_callback  = recording;
167fb90eb3fSMatthias Ringwald 
168fb90eb3fSMatthias Ringwald     hal_audio_source_init(channels, samplerate, &btstack_audio_audio_recorded);
169fb90eb3fSMatthias Ringwald 
1700402264cSMatthias Ringwald     return 0;
1710402264cSMatthias Ringwald }
172dfa32746SMatthias Ringwald 
1731b7f8fa1SMatthias Ringwald static void btstack_audio_embedded_sink_set_volume(uint8_t volume){
1741b7f8fa1SMatthias Ringwald     UNUSED(volume);
1751b7f8fa1SMatthias Ringwald }
1761b7f8fa1SMatthias Ringwald 
1771b7f8fa1SMatthias Ringwald static void btstack_audio_embedded_source_set_gain(uint8_t gain){
1781b7f8fa1SMatthias Ringwald     UNUSED(gain);
1791b7f8fa1SMatthias Ringwald }
1801b7f8fa1SMatthias Ringwald 
1810402264cSMatthias Ringwald static void btstack_audio_embedded_sink_start_stream(void){
182ff0081eeSMatthias Ringwald     output_buffer_count   = hal_audio_sink_get_num_output_buffers();
183ff0081eeSMatthias Ringwald     output_buffer_samples = hal_audio_sink_get_num_output_buffer_samples();
184dfa32746SMatthias Ringwald 
185329d55f3SMilanka Ringwald     btstack_assert(output_buffer_samples > 0);
186329d55f3SMilanka Ringwald 
187dfa32746SMatthias Ringwald     // pre-fill HAL buffers
188dfa32746SMatthias Ringwald     uint16_t i;
189dfa32746SMatthias Ringwald     for (i=0;i<output_buffer_count;i++){
190ff0081eeSMatthias Ringwald         int16_t * buffer = hal_audio_sink_get_output_buffer(i);
191dfa32746SMatthias Ringwald         (*playback_callback)(buffer, output_buffer_samples);
192dfa32746SMatthias Ringwald     }
193dfa32746SMatthias Ringwald 
194dfa32746SMatthias Ringwald     output_buffer_to_play = 0;
195dfa32746SMatthias Ringwald     output_buffer_to_fill = 0;
196dfa32746SMatthias Ringwald 
197dfa32746SMatthias Ringwald     // start playback
198ff0081eeSMatthias Ringwald     hal_audio_sink_start();
199dfa32746SMatthias Ringwald 
200dfa32746SMatthias Ringwald     // start timer
2010402264cSMatthias Ringwald     btstack_run_loop_set_timer_handler(&driver_timer_sink, &driver_timer_handler_sink);
2020402264cSMatthias Ringwald     btstack_run_loop_set_timer(&driver_timer_sink, DRIVER_POLL_INTERVAL_MS);
2030402264cSMatthias Ringwald     btstack_run_loop_add_timer(&driver_timer_sink);
2046fd4ca9eSMatthias Ringwald 
2056fd4ca9eSMatthias Ringwald     // state
2066fd4ca9eSMatthias Ringwald     sink_active = 1;
207dfa32746SMatthias Ringwald }
208dfa32746SMatthias Ringwald 
2090402264cSMatthias Ringwald static void btstack_audio_embedded_source_start_stream(void){
210fb90eb3fSMatthias Ringwald     // just started, no data ready
211fb90eb3fSMatthias Ringwald     input_buffer_ready = 0;
212fb90eb3fSMatthias Ringwald 
213fb90eb3fSMatthias Ringwald     // start recording
214fb90eb3fSMatthias Ringwald     hal_audio_source_start();
215fb90eb3fSMatthias Ringwald 
216fb90eb3fSMatthias Ringwald     // start timer
217fb90eb3fSMatthias Ringwald     btstack_run_loop_set_timer_handler(&driver_timer_source, &driver_timer_handler_source);
218fb90eb3fSMatthias Ringwald     btstack_run_loop_set_timer(&driver_timer_source, DRIVER_POLL_INTERVAL_MS);
219fb90eb3fSMatthias Ringwald     btstack_run_loop_add_timer(&driver_timer_source);
2206fd4ca9eSMatthias Ringwald 
2216fd4ca9eSMatthias Ringwald     // state
2226fd4ca9eSMatthias Ringwald     source_active = 1;
2236fd4ca9eSMatthias Ringwald }
2246fd4ca9eSMatthias Ringwald 
2256fd4ca9eSMatthias Ringwald static void btstack_audio_embedded_sink_stop_stream(void){
2266fd4ca9eSMatthias Ringwald     // stop stream
2276fd4ca9eSMatthias Ringwald     hal_audio_sink_stop();
2286fd4ca9eSMatthias Ringwald     // stop timer
2296fd4ca9eSMatthias Ringwald     btstack_run_loop_remove_timer(&driver_timer_sink);
2306fd4ca9eSMatthias Ringwald     // state
2316fd4ca9eSMatthias Ringwald     sink_active = 0;
2326fd4ca9eSMatthias Ringwald }
2336fd4ca9eSMatthias Ringwald 
2346fd4ca9eSMatthias Ringwald static void btstack_audio_embedded_source_stop_stream(void){
2356fd4ca9eSMatthias Ringwald     // stop stream
2366fd4ca9eSMatthias Ringwald     hal_audio_source_stop();
2376fd4ca9eSMatthias Ringwald     // stop timer
2386fd4ca9eSMatthias Ringwald     btstack_run_loop_remove_timer(&driver_timer_source);
2396fd4ca9eSMatthias Ringwald     // state
2406fd4ca9eSMatthias Ringwald     source_active = 0;
2410402264cSMatthias Ringwald }
2420402264cSMatthias Ringwald 
2430402264cSMatthias Ringwald static void btstack_audio_embedded_sink_close(void){
2446fd4ca9eSMatthias Ringwald     // stop stream if needed
2456fd4ca9eSMatthias Ringwald     if (sink_active){
2466fd4ca9eSMatthias Ringwald         btstack_audio_embedded_sink_stop_stream();
2476fd4ca9eSMatthias Ringwald     }
248dfa32746SMatthias Ringwald     // close HAL
249ff0081eeSMatthias Ringwald     hal_audio_sink_close();
250dfa32746SMatthias Ringwald }
251dfa32746SMatthias Ringwald 
2520402264cSMatthias Ringwald static void btstack_audio_embedded_source_close(void){
2536fd4ca9eSMatthias Ringwald     // stop stream if needed
2546fd4ca9eSMatthias Ringwald     if (source_active){
2556fd4ca9eSMatthias Ringwald         btstack_audio_embedded_source_stop_stream();
2566fd4ca9eSMatthias Ringwald     }
257fb90eb3fSMatthias Ringwald     // close HAL
258fb90eb3fSMatthias Ringwald     hal_audio_source_close();
2590402264cSMatthias Ringwald }
2600402264cSMatthias Ringwald 
2610402264cSMatthias Ringwald static const btstack_audio_sink_t btstack_audio_embedded_sink = {
2620402264cSMatthias Ringwald     /* int (*init)(..);*/                                       &btstack_audio_embedded_sink_init,
2631b7f8fa1SMatthias Ringwald     /* void (*set_volume)(uint8_t volume); */                   &btstack_audio_embedded_sink_set_volume,
2640402264cSMatthias Ringwald     /* void (*start_stream(void));*/                            &btstack_audio_embedded_sink_start_stream,
2656fd4ca9eSMatthias Ringwald     /* void (*stop_stream)(void)  */                            &btstack_audio_embedded_sink_stop_stream,
2660402264cSMatthias Ringwald     /* void (*close)(void); */                                  &btstack_audio_embedded_sink_close
267dfa32746SMatthias Ringwald };
268dfa32746SMatthias Ringwald 
2690402264cSMatthias Ringwald static const btstack_audio_source_t btstack_audio_embedded_source = {
2700402264cSMatthias Ringwald     /* int (*init)(..);*/                                       &btstack_audio_embedded_source_init,
2711b7f8fa1SMatthias Ringwald     /* void (*set_gain)(uint8_t gain); */                       &btstack_audio_embedded_source_set_gain,
2720402264cSMatthias Ringwald     /* void (*start_stream(void));*/                            &btstack_audio_embedded_source_start_stream,
2736fd4ca9eSMatthias Ringwald     /* void (*stop_stream)(void)  */                            &btstack_audio_embedded_source_stop_stream,
2740402264cSMatthias Ringwald     /* void (*close)(void); */                                  &btstack_audio_embedded_source_close
2750402264cSMatthias Ringwald };
2760402264cSMatthias Ringwald 
2770402264cSMatthias Ringwald const btstack_audio_sink_t * btstack_audio_embedded_sink_get_instance(void){
2780402264cSMatthias Ringwald     return &btstack_audio_embedded_sink;
2790402264cSMatthias Ringwald }
2800402264cSMatthias Ringwald 
2810402264cSMatthias Ringwald const btstack_audio_source_t * btstack_audio_embedded_source_get_instance(void){
2820402264cSMatthias Ringwald     return &btstack_audio_embedded_source;
283dfa32746SMatthias Ringwald }
284dfa32746SMatthias Ringwald 
285dfa32746SMatthias Ringwald #endif
286