xref: /btstack/platform/embedded/btstack_audio_embedded.c (revision 8613e0eb5b9621b90c6c50e3d739bfa544af1c63)
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 MATTHIAS
24  * RINGWALD 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 
75 static int source_active;
76 static int sink_active;
77 
78 static void btstack_audio_audio_played(uint8_t buffer_index){
79     output_buffer_to_play = (buffer_index + 1) % output_buffer_count;
80 }
81 
82 static void btstack_audio_audio_recorded(const int16_t * samples, uint16_t num_samples){
83     input_buffer_samples     = samples;
84     input_buffer_num_samples = num_samples;
85     input_buffer_ready       = 1;
86 }
87 
88 static void driver_timer_handler_sink(btstack_timer_source_t * ts){
89 
90     // playback buffer ready to fill
91     if (output_buffer_to_play != output_buffer_to_fill){
92         int16_t * buffer = hal_audio_sink_get_output_buffer(output_buffer_to_fill);
93         (*playback_callback)(buffer, output_buffer_samples);
94 
95         // next
96         output_buffer_to_fill = (output_buffer_to_fill + 1 ) % output_buffer_count;
97     }
98 
99     // re-set timer
100     btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
101     btstack_run_loop_add_timer(ts);
102 }
103 
104 static void driver_timer_handler_source(btstack_timer_source_t * ts){
105     // deliver samples if ready
106     if (input_buffer_ready){
107         (*recording_callback)((const int16_t *)input_buffer_samples, input_buffer_num_samples);
108         input_buffer_ready = 0;
109     }
110 
111     // re-set timer
112     btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
113     btstack_run_loop_add_timer(ts);
114 }
115 
116 static int btstack_audio_embedded_sink_init(
117     uint8_t channels,
118     uint32_t samplerate,
119     void (*playback)(int16_t * buffer, uint16_t num_samples)
120 ){
121     if (!playback){
122         log_error("No playback callback");
123         return 1;
124     }
125 
126     playback_callback  = playback;
127 
128     hal_audio_sink_init(channels, samplerate, &btstack_audio_audio_played);
129 
130     return 0;
131 }
132 
133 static int btstack_audio_embedded_source_init(
134     uint8_t channels,
135     uint32_t samplerate,
136     void (*recording)(const int16_t * buffer, uint16_t num_samples)
137 ){
138     if (!recording){
139         log_error("No recording callback");
140         return 1;
141     }
142 
143     recording_callback  = recording;
144 
145     hal_audio_source_init(channels, samplerate, &btstack_audio_audio_recorded);
146 
147     return 0;
148 }
149 
150 static void btstack_audio_embedded_sink_set_volume(uint8_t volume){
151     UNUSED(volume);
152 }
153 
154 static void btstack_audio_embedded_source_set_gain(uint8_t gain){
155     UNUSED(gain);
156 }
157 
158 static void btstack_audio_embedded_sink_start_stream(void){
159     output_buffer_count   = hal_audio_sink_get_num_output_buffers();
160     output_buffer_samples = hal_audio_sink_get_num_output_buffer_samples();
161 
162     // pre-fill HAL buffers
163     uint16_t i;
164     for (i=0;i<output_buffer_count;i++){
165         int16_t * buffer = hal_audio_sink_get_output_buffer(i);
166         (*playback_callback)(buffer, output_buffer_samples);
167     }
168 
169     output_buffer_to_play = 0;
170     output_buffer_to_fill = 0;
171 
172     // start playback
173     hal_audio_sink_start();
174 
175     // start timer
176     btstack_run_loop_set_timer_handler(&driver_timer_sink, &driver_timer_handler_sink);
177     btstack_run_loop_set_timer(&driver_timer_sink, DRIVER_POLL_INTERVAL_MS);
178     btstack_run_loop_add_timer(&driver_timer_sink);
179 
180     // state
181     sink_active = 1;
182 }
183 
184 static void btstack_audio_embedded_source_start_stream(void){
185     // just started, no data ready
186     input_buffer_ready = 0;
187 
188     // start recording
189     hal_audio_source_start();
190 
191     // start timer
192     btstack_run_loop_set_timer_handler(&driver_timer_source, &driver_timer_handler_source);
193     btstack_run_loop_set_timer(&driver_timer_source, DRIVER_POLL_INTERVAL_MS);
194     btstack_run_loop_add_timer(&driver_timer_source);
195 
196     // state
197     source_active = 1;
198 }
199 
200 static void btstack_audio_embedded_sink_stop_stream(void){
201     // stop stream
202     hal_audio_sink_stop();
203     // stop timer
204     btstack_run_loop_remove_timer(&driver_timer_sink);
205     // state
206     sink_active = 0;
207 }
208 
209 static void btstack_audio_embedded_source_stop_stream(void){
210     // stop stream
211     hal_audio_source_stop();
212     // stop timer
213     btstack_run_loop_remove_timer(&driver_timer_source);
214     // state
215     source_active = 0;
216 }
217 
218 static void btstack_audio_embedded_sink_close(void){
219     // stop stream if needed
220     if (sink_active){
221         btstack_audio_embedded_sink_stop_stream();
222     }
223     // close HAL
224     hal_audio_sink_close();
225 }
226 
227 static void btstack_audio_embedded_source_close(void){
228     // stop stream if needed
229     if (source_active){
230         btstack_audio_embedded_source_stop_stream();
231     }
232     // close HAL
233     hal_audio_source_close();
234 }
235 
236 static const btstack_audio_sink_t btstack_audio_embedded_sink = {
237     /* int (*init)(..);*/                                       &btstack_audio_embedded_sink_init,
238     /* void (*set_volume)(uint8_t volume); */                   &btstack_audio_embedded_sink_set_volume,
239     /* void (*start_stream(void));*/                            &btstack_audio_embedded_sink_start_stream,
240     /* void (*stop_stream)(void)  */                            &btstack_audio_embedded_sink_stop_stream,
241     /* void (*close)(void); */                                  &btstack_audio_embedded_sink_close
242 };
243 
244 static const btstack_audio_source_t btstack_audio_embedded_source = {
245     /* int (*init)(..);*/                                       &btstack_audio_embedded_source_init,
246     /* void (*set_gain)(uint8_t gain); */                       &btstack_audio_embedded_source_set_gain,
247     /* void (*start_stream(void));*/                            &btstack_audio_embedded_source_start_stream,
248     /* void (*stop_stream)(void)  */                            &btstack_audio_embedded_source_stop_stream,
249     /* void (*close)(void); */                                  &btstack_audio_embedded_source_close
250 };
251 
252 const btstack_audio_sink_t * btstack_audio_embedded_sink_get_instance(void){
253     return &btstack_audio_embedded_sink;
254 }
255 
256 const btstack_audio_source_t * btstack_audio_embedded_source_get_instance(void){
257     return &btstack_audio_embedded_source;
258 }
259 
260 #endif
261