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