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