xref: /btstack/platform/embedded/btstack_audio_embedded.c (revision 2d8556b2db643312186f7d316b34bb060fd253a0)
1 /*
2  * Copyright (C) 2018 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "btstack_audio_embedded.c"
39 
40 /*
41  *  btstack_audio_embedded.c
42  *
43  *  Implementation of btstack_audio.h using hal_audio.h for embedded run loop
44  *
45  */
46 
47 #include "btstack_config.h"
48 
49 // allow to compile all files in embedded folder even if there's no audio support
50 #ifdef HAVE_HAL_AUDIO
51 
52 #include "btstack_debug.h"
53 #include "btstack_audio.h"
54 #include "btstack_run_loop_embedded.h"
55 #include "hal_audio.h"
56 
57 #include <stddef.h>
58 
59 #define DRIVER_POLL_INTERVAL_MS          5
60 
61 // client
62 static void (*playback_callback)(int16_t * buffer, uint16_t num_samples);
63 static void (*recording_callback)(const int16_t * buffer, uint16_t num_samples);
64 
65 // timer to fill output ring buffer
66 static btstack_timer_source_t  driver_timer_sink;
67 static btstack_timer_source_t  driver_timer_source;
68 
69 static volatile unsigned int output_buffer_to_play;
70 static          unsigned int output_buffer_to_fill;
71 static          unsigned int output_buffer_count;
72 static          unsigned int output_buffer_samples;
73 
74 static volatile int       input_buffer_ready;
75 static volatile const int16_t * input_buffer_samples;
76 static volatile uint16_t  input_buffer_num_samples;
77 
78 static int source_active;
79 static int sink_active;
80 
81 // Support for stereo-only audio hal
82 #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
83 static bool output_duplicate_samples;
84 #endif
85 
btstack_audio_audio_played(uint8_t buffer_index)86 static void btstack_audio_audio_played(uint8_t buffer_index){
87     output_buffer_to_play = (buffer_index + 1) % output_buffer_count;
88 }
89 
btstack_audio_audio_recorded(const int16_t * samples,uint16_t num_samples)90 static void btstack_audio_audio_recorded(const int16_t * samples, uint16_t num_samples){
91     input_buffer_samples     = samples;
92     input_buffer_num_samples = num_samples;
93     input_buffer_ready       = 1;
94 }
95 
driver_timer_handler_sink(btstack_timer_source_t * ts)96 static void driver_timer_handler_sink(btstack_timer_source_t * ts){
97 
98     // playback buffer ready to fill
99     if (output_buffer_to_play != output_buffer_to_fill){
100         int16_t * buffer = hal_audio_sink_get_output_buffer(output_buffer_to_fill);
101         (*playback_callback)(buffer, output_buffer_samples);
102 
103 #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
104         if (output_duplicate_samples){
105             unsigned int i = output_buffer_samples;
106             do {
107                 i--;
108                 int16_t sample = buffer[i];
109                 buffer[2*i + 0] = sample;
110                 buffer[2*i + 1] = sample;
111             } while ( i > 0 );
112         }
113 #endif
114 
115         // next
116         output_buffer_to_fill = (output_buffer_to_fill + 1 ) % output_buffer_count;
117     }
118 
119     // re-set timer
120     btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
121     btstack_run_loop_add_timer(ts);
122 }
123 
driver_timer_handler_source(btstack_timer_source_t * ts)124 static void driver_timer_handler_source(btstack_timer_source_t * ts){
125     // deliver samples if ready
126     if (input_buffer_ready){
127         (*recording_callback)((const int16_t *)input_buffer_samples, input_buffer_num_samples);
128         input_buffer_ready = 0;
129     }
130 
131     // re-set timer
132     btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
133     btstack_run_loop_add_timer(ts);
134 }
135 
136 
137 static uint32_t sink_samplerate = 0;
138 
btstack_audio_embedded_sink_init(uint8_t channels,uint32_t samplerate,void (* playback)(int16_t * buffer,uint16_t num_samples))139 static int btstack_audio_embedded_sink_init(
140     uint8_t channels,
141     uint32_t samplerate,
142     void (*playback)(int16_t * buffer, uint16_t num_samples)
143 ){
144     btstack_assert(playback != NULL);
145     btstack_assert(channels != 0);
146 
147 #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
148     // always use stereo from hal, duplicate samples if needed
149     output_duplicate_samples = channels != 2;
150     channels = 2;
151 #endif
152 
153     playback_callback  = playback;
154     sink_samplerate = samplerate;
155     hal_audio_sink_init(channels, samplerate, &btstack_audio_audio_played);
156 
157     return 0;
158 }
159 
btstack_audio_embedded_sink_get_samplerate()160 static uint32_t btstack_audio_embedded_sink_get_samplerate() {
161     return BSP_AUDIO_OUT_GetFrequency();
162 }
163 
164 static uint32_t source_samplerate = 0;
165 
btstack_audio_embedded_source_init(uint8_t channels,uint32_t samplerate,void (* recording)(const int16_t * buffer,uint16_t num_samples))166 static int btstack_audio_embedded_source_init(
167     uint8_t channels,
168     uint32_t samplerate,
169     void (*recording)(const int16_t * buffer, uint16_t num_samples)
170 ){
171     if (!recording){
172         log_error("No recording callback");
173         return 1;
174     }
175 
176     recording_callback  = recording;
177     source_samplerate = samplerate;
178     hal_audio_source_init(channels, samplerate, &btstack_audio_audio_recorded);
179 
180     return 0;
181 }
182 
btstack_audio_embedded_source_get_samplerate()183 static uint32_t btstack_audio_embedded_source_get_samplerate() {
184     return source_samplerate;
185 }
186 
btstack_audio_embedded_sink_set_volume(uint8_t volume)187 static void btstack_audio_embedded_sink_set_volume(uint8_t volume){
188     UNUSED(volume);
189 }
190 
btstack_audio_embedded_source_set_gain(uint8_t gain)191 static void btstack_audio_embedded_source_set_gain(uint8_t gain){
192     UNUSED(gain);
193 }
194 
btstack_audio_embedded_sink_start_stream(void)195 static void btstack_audio_embedded_sink_start_stream(void){
196     output_buffer_count   = hal_audio_sink_get_num_output_buffers();
197     output_buffer_samples = hal_audio_sink_get_num_output_buffer_samples();
198 
199     btstack_assert(output_buffer_samples > 0);
200 
201     // pre-fill HAL buffers
202     uint16_t i;
203     for (i=0;i<output_buffer_count;i++){
204         int16_t * buffer = hal_audio_sink_get_output_buffer(i);
205         (*playback_callback)(buffer, output_buffer_samples);
206     }
207 
208     output_buffer_to_play = 0;
209     output_buffer_to_fill = 0;
210 
211     // start playback
212     hal_audio_sink_start();
213 
214     // start timer
215     btstack_run_loop_set_timer_handler(&driver_timer_sink, &driver_timer_handler_sink);
216     btstack_run_loop_set_timer(&driver_timer_sink, DRIVER_POLL_INTERVAL_MS);
217     btstack_run_loop_add_timer(&driver_timer_sink);
218 
219     // state
220     sink_active = 1;
221 }
222 
btstack_audio_embedded_source_start_stream(void)223 static void btstack_audio_embedded_source_start_stream(void){
224     // just started, no data ready
225     input_buffer_ready = 0;
226 
227     // start recording
228     hal_audio_source_start();
229 
230     // start timer
231     btstack_run_loop_set_timer_handler(&driver_timer_source, &driver_timer_handler_source);
232     btstack_run_loop_set_timer(&driver_timer_source, DRIVER_POLL_INTERVAL_MS);
233     btstack_run_loop_add_timer(&driver_timer_source);
234 
235     // state
236     source_active = 1;
237 }
238 
btstack_audio_embedded_sink_stop_stream(void)239 static void btstack_audio_embedded_sink_stop_stream(void){
240     // stop stream
241     hal_audio_sink_stop();
242     // stop timer
243     btstack_run_loop_remove_timer(&driver_timer_sink);
244     // state
245     sink_active = 0;
246 }
247 
btstack_audio_embedded_source_stop_stream(void)248 static void btstack_audio_embedded_source_stop_stream(void){
249     // stop stream
250     hal_audio_source_stop();
251     // stop timer
252     btstack_run_loop_remove_timer(&driver_timer_source);
253     // state
254     source_active = 0;
255 }
256 
btstack_audio_embedded_sink_close(void)257 static void btstack_audio_embedded_sink_close(void){
258     // stop stream if needed
259     if (sink_active){
260         btstack_audio_embedded_sink_stop_stream();
261     }
262     // close HAL
263     hal_audio_sink_close();
264 }
265 
btstack_audio_embedded_source_close(void)266 static void btstack_audio_embedded_source_close(void){
267     // stop stream if needed
268     if (source_active){
269         btstack_audio_embedded_source_stop_stream();
270     }
271     // close HAL
272     hal_audio_source_close();
273 }
274 
275 static const btstack_audio_sink_t btstack_audio_embedded_sink = {
276         .init           = &btstack_audio_embedded_sink_init,
277         .get_samplerate = &btstack_audio_embedded_sink_get_samplerate,
278         .set_volume     = &btstack_audio_embedded_sink_set_volume,
279         .start_stream   = &btstack_audio_embedded_sink_start_stream,
280         .stop_stream    = &btstack_audio_embedded_sink_stop_stream,
281         .close          = &btstack_audio_embedded_sink_close
282 };
283 
284 static const btstack_audio_source_t btstack_audio_embedded_source = {
285         .init           = &btstack_audio_embedded_source_init,
286         .get_samplerate = &btstack_audio_embedded_source_get_samplerate,
287         .set_gain       = &btstack_audio_embedded_source_set_gain,
288         .start_stream   = &btstack_audio_embedded_source_start_stream,
289         .stop_stream    = &btstack_audio_embedded_source_stop_stream,
290         .close          = &btstack_audio_embedded_source_close
291 };
292 
btstack_audio_embedded_sink_get_instance(void)293 const btstack_audio_sink_t * btstack_audio_embedded_sink_get_instance(void){
294     return &btstack_audio_embedded_sink;
295 }
296 
btstack_audio_embedded_source_get_instance(void)297 const btstack_audio_source_t * btstack_audio_embedded_source_get_instance(void){
298     return &btstack_audio_embedded_source;
299 }
300 
301 #endif
302