xref: /btstack/platform/embedded/btstack_audio_embedded.c (revision f0b78f48e22dc8fc6c04986a7e0be8aa569355da)
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 // allow to compile all files in embedded folder even if there's no audio support
48 #ifdef HAVE_HAL_AUDIO
49 
50 #include "btstack_config.h"
51 #include "btstack_debug.h"
52 #include "btstack_audio.h"
53 #include "btstack_run_loop_embedded.h"
54 #include "hal_audio.h"
55 
56 #include <stddef.h>
57 
58 #define DRIVER_POLL_INTERVAL_MS          5
59 
60 // client
61 static void (*playback_callback)(int16_t * buffer, uint16_t num_samples);
62 static void (*recording_callback)(const int16_t * buffer, uint16_t num_samples);
63 
64 // timer to fill output ring buffer
65 static btstack_timer_source_t  driver_timer_sink;
66 static btstack_timer_source_t  driver_timer_source;
67 
68 static volatile unsigned int output_buffer_to_play;
69 static          unsigned int output_buffer_to_fill;
70 static          unsigned int output_buffer_count;
71 static          unsigned int output_buffer_samples;
72 
73 static volatile int       input_buffer_ready;
74 static volatile const int16_t * input_buffer_samples;
75 static volatile uint16_t  input_buffer_num_samples;
76 
77 static int source_active;
78 static int sink_active;
79 
80 // Support for stereo-only audio hal
81 #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
82 static bool output_duplicate_samples;
83 #endif
84 
85 static void btstack_audio_audio_played(uint8_t buffer_index){
86     output_buffer_to_play = (buffer_index + 1) % output_buffer_count;
87 }
88 
89 static void btstack_audio_audio_recorded(const int16_t * samples, uint16_t num_samples){
90     input_buffer_samples     = samples;
91     input_buffer_num_samples = num_samples;
92     input_buffer_ready       = 1;
93 }
94 
95 static void driver_timer_handler_sink(btstack_timer_source_t * ts){
96 
97     // playback buffer ready to fill
98     if (output_buffer_to_play != output_buffer_to_fill){
99         int16_t * buffer = hal_audio_sink_get_output_buffer(output_buffer_to_fill);
100         (*playback_callback)(buffer, output_buffer_samples);
101 
102 #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
103         if (output_duplicate_samples){
104             unsigned int i = output_buffer_samples;
105             do {
106                 i--;
107                 int16_t sample = buffer[i];
108                 buffer[2*i + 0] = sample;
109                 buffer[2*i + 1] = sample;
110             } while ( i > 0 );
111         }
112 #endif
113 
114         // next
115         output_buffer_to_fill = (output_buffer_to_fill + 1 ) % output_buffer_count;
116     }
117 
118     // re-set timer
119     btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
120     btstack_run_loop_add_timer(ts);
121 }
122 
123 static void driver_timer_handler_source(btstack_timer_source_t * ts){
124     // deliver samples if ready
125     if (input_buffer_ready){
126         (*recording_callback)((const int16_t *)input_buffer_samples, input_buffer_num_samples);
127         input_buffer_ready = 0;
128     }
129 
130     // re-set timer
131     btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
132     btstack_run_loop_add_timer(ts);
133 }
134 
135 static int btstack_audio_embedded_sink_init(
136     uint8_t channels,
137     uint32_t samplerate,
138     void (*playback)(int16_t * buffer, uint16_t num_samples)
139 ){
140     btstack_assert(playback != NULL);
141     btstack_assert(channels != 0);
142 
143 #ifdef HAVE_HAL_AUDIO_SINK_STEREO_ONLY
144     // always use stereo from hal, duplicate samples if needed
145     output_duplicate_samples = channels != 2;
146     channels = 2;
147 #endif
148 
149     playback_callback  = playback;
150 
151     hal_audio_sink_init(channels, samplerate, &btstack_audio_audio_played);
152 
153     return 0;
154 }
155 
156 static int btstack_audio_embedded_source_init(
157     uint8_t channels,
158     uint32_t samplerate,
159     void (*recording)(const int16_t * buffer, uint16_t num_samples)
160 ){
161     if (!recording){
162         log_error("No recording callback");
163         return 1;
164     }
165 
166     recording_callback  = recording;
167 
168     hal_audio_source_init(channels, samplerate, &btstack_audio_audio_recorded);
169 
170     return 0;
171 }
172 
173 static void btstack_audio_embedded_sink_set_volume(uint8_t volume){
174     UNUSED(volume);
175 }
176 
177 static void btstack_audio_embedded_source_set_gain(uint8_t gain){
178     UNUSED(gain);
179 }
180 
181 static void btstack_audio_embedded_sink_start_stream(void){
182     output_buffer_count   = hal_audio_sink_get_num_output_buffers();
183     output_buffer_samples = hal_audio_sink_get_num_output_buffer_samples();
184 
185     btstack_assert(output_buffer_samples > 0);
186 
187     // pre-fill HAL buffers
188     uint16_t i;
189     for (i=0;i<output_buffer_count;i++){
190         int16_t * buffer = hal_audio_sink_get_output_buffer(i);
191         (*playback_callback)(buffer, output_buffer_samples);
192     }
193 
194     output_buffer_to_play = 0;
195     output_buffer_to_fill = 0;
196 
197     // start playback
198     hal_audio_sink_start();
199 
200     // start timer
201     btstack_run_loop_set_timer_handler(&driver_timer_sink, &driver_timer_handler_sink);
202     btstack_run_loop_set_timer(&driver_timer_sink, DRIVER_POLL_INTERVAL_MS);
203     btstack_run_loop_add_timer(&driver_timer_sink);
204 
205     // state
206     sink_active = 1;
207 }
208 
209 static void btstack_audio_embedded_source_start_stream(void){
210     // just started, no data ready
211     input_buffer_ready = 0;
212 
213     // start recording
214     hal_audio_source_start();
215 
216     // start timer
217     btstack_run_loop_set_timer_handler(&driver_timer_source, &driver_timer_handler_source);
218     btstack_run_loop_set_timer(&driver_timer_source, DRIVER_POLL_INTERVAL_MS);
219     btstack_run_loop_add_timer(&driver_timer_source);
220 
221     // state
222     source_active = 1;
223 }
224 
225 static void btstack_audio_embedded_sink_stop_stream(void){
226     // stop stream
227     hal_audio_sink_stop();
228     // stop timer
229     btstack_run_loop_remove_timer(&driver_timer_sink);
230     // state
231     sink_active = 0;
232 }
233 
234 static void btstack_audio_embedded_source_stop_stream(void){
235     // stop stream
236     hal_audio_source_stop();
237     // stop timer
238     btstack_run_loop_remove_timer(&driver_timer_source);
239     // state
240     source_active = 0;
241 }
242 
243 static void btstack_audio_embedded_sink_close(void){
244     // stop stream if needed
245     if (sink_active){
246         btstack_audio_embedded_sink_stop_stream();
247     }
248     // close HAL
249     hal_audio_sink_close();
250 }
251 
252 static void btstack_audio_embedded_source_close(void){
253     // stop stream if needed
254     if (source_active){
255         btstack_audio_embedded_source_stop_stream();
256     }
257     // close HAL
258     hal_audio_source_close();
259 }
260 
261 static const btstack_audio_sink_t btstack_audio_embedded_sink = {
262     /* int (*init)(..);*/                                       &btstack_audio_embedded_sink_init,
263     /* void (*set_volume)(uint8_t volume); */                   &btstack_audio_embedded_sink_set_volume,
264     /* void (*start_stream(void));*/                            &btstack_audio_embedded_sink_start_stream,
265     /* void (*stop_stream)(void)  */                            &btstack_audio_embedded_sink_stop_stream,
266     /* void (*close)(void); */                                  &btstack_audio_embedded_sink_close
267 };
268 
269 static const btstack_audio_source_t btstack_audio_embedded_source = {
270     /* int (*init)(..);*/                                       &btstack_audio_embedded_source_init,
271     /* void (*set_gain)(uint8_t gain); */                       &btstack_audio_embedded_source_set_gain,
272     /* void (*start_stream(void));*/                            &btstack_audio_embedded_source_start_stream,
273     /* void (*stop_stream)(void)  */                            &btstack_audio_embedded_source_stop_stream,
274     /* void (*close)(void); */                                  &btstack_audio_embedded_source_close
275 };
276 
277 const btstack_audio_sink_t * btstack_audio_embedded_sink_get_instance(void){
278     return &btstack_audio_embedded_sink;
279 }
280 
281 const btstack_audio_source_t * btstack_audio_embedded_source_get_instance(void){
282     return &btstack_audio_embedded_source;
283 }
284 
285 #endif
286