xref: /btstack/example/audio_duplex.c (revision 80e33422a96c028b3a9c308fc4b9b874712dafb4)
1 /*
2  * Copyright (C) 2014 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 /*
39  * Audio Duplex: forward audio from BTstack audio source to audio sink - test for audio interface
40  *
41  */
42 
43 #include "btstack.h"
44 
45 
46 // uncomment to test start/stop of loopback / audio driver
47 // #define TEST_START_STOP_INTERVAL 5000
48 
49 #ifdef TEST_START_STOP_INTERVAL
50 static void stop_loopback(btstack_timer_source_t * ts);
51 #endif
52 
53 static btstack_timer_source_t start_stop_timer;
54 
55 // samplerate
56 const uint32_t samplerate = 16000;
57 
58 // ring buffer for audio
59 #define BUFFER_SAMPLES 1024
60 static uint16_t              audio_buffer_storage[BUFFER_SAMPLES];
61 static btstack_ring_buffer_t audio_buffer;
62 
63 // mono buffer
64 #define MONO_BUFFER_LEN 128
65 static int16_t mono_buffer[MONO_BUFFER_LEN];
66 
67 // playback starts after audio_buffer is half full
68 static int playback_started;
69 
70 // sample couners
71 static int count_recording;
72 static int count_playback;
73 
74 static void audio_recording(const int16_t * pcm_buffer, uint16_t num_samples_to_write){
75     count_recording += num_samples_to_write;
76     int err = btstack_ring_buffer_write(&audio_buffer, (uint8_t *) pcm_buffer, num_samples_to_write * 2);
77     if (err){
78         printf("Failed to store %u samples\n", num_samples_to_write);
79     }
80 }
81 
82 static void audio_playback(int16_t * pcm_buffer, uint16_t num_samples_to_write){
83     int num_samples_in_buffer = btstack_ring_buffer_bytes_available(&audio_buffer) / 2;
84     if (playback_started == 0){
85         if ( num_samples_in_buffer < (BUFFER_SAMPLES / 2)) return;
86         playback_started = 1;
87     }
88     count_playback += num_samples_to_write;
89     while (num_samples_to_write){
90         num_samples_in_buffer = btstack_ring_buffer_bytes_available(&audio_buffer) / 2;
91         int num_samples_ready = btstack_min(num_samples_in_buffer, num_samples_to_write);
92         // limit by mono_buffer
93         int num_samples_from_buffer = btstack_min(num_samples_ready, MONO_BUFFER_LEN);
94         if (!num_samples_from_buffer) break;
95         uint32_t bytes_read;
96         btstack_ring_buffer_read(&audio_buffer, (uint8_t *) mono_buffer, num_samples_from_buffer * 2, &bytes_read);
97         // duplicate samples for stereo output
98         int i;
99         for (i=0; i < num_samples_from_buffer;i++){
100             *pcm_buffer++ = mono_buffer[i];
101             *pcm_buffer++ = mono_buffer[i];
102             num_samples_to_write--;
103         }
104     }
105 
106     // warn about underrun
107     if (num_samples_to_write){
108         printf("Buffer underrun - recording %u, playback %u - delta %d!\n", count_recording, count_playback, count_recording - count_playback);
109     }
110 
111     // fill rest with silence
112     while (num_samples_to_write){
113         *pcm_buffer++ = 0;
114         *pcm_buffer++ = 0;
115         num_samples_to_write--;
116     }
117 }
118 
119 static void start_loopback(btstack_timer_source_t * ts){
120     const btstack_audio_sink_t   * audio_sink   = btstack_audio_sink_get_instance();
121     const btstack_audio_source_t * audio_source = btstack_audio_source_get_instance();
122 
123     // prepare audio buffer
124     btstack_ring_buffer_init(&audio_buffer, (uint8_t*) &audio_buffer_storage[0], sizeof(audio_buffer_storage));
125 
126     // setup audio: mono input -> stereo output
127     audio_sink->init(2, samplerate, &audio_playback);
128     audio_source->init(1, samplerate, &audio_recording);
129 
130     // start duplex
131     audio_sink->start_stream();
132     audio_source->start_stream();
133 
134     printf("Start Audio Loopback\n");
135 
136 #ifdef TEST_START_STOP_INTERVAL
137     // schedule stop
138     btstack_run_loop_set_timer_handler(ts, &stop_loopback);
139     btstack_run_loop_set_timer(ts, TEST_START_STOP_INTERVAL);
140     btstack_run_loop_add_timer(ts);
141 #else
142     UNUSED(ts);
143 #endif
144 }
145 
146 #ifdef TEST_START_STOP_INTERVAL
147 static void stop_loopback(btstack_timer_source_t * ts){
148     const btstack_audio_sink_t   * audio_sink   = btstack_audio_sink_get_instance();
149     const btstack_audio_source_t * audio_source = btstack_audio_source_get_instance();
150 
151     // stop streams
152     audio_sink->stop_stream();
153     audio_source->stop_stream();
154 
155     // close audio
156     audio_sink->close();
157     audio_source->close();
158 
159     playback_started = 0;
160 
161     printf("Stop Audio Loopback\n");
162 
163     // schedule stop
164     btstack_run_loop_set_timer_handler(ts, &start_loopback);
165     btstack_run_loop_set_timer(ts, TEST_START_STOP_INTERVAL);
166     btstack_run_loop_add_timer(ts);
167 }
168 #endif
169 
170 int btstack_main(int argc, const char * argv[]);
171 int btstack_main(int argc, const char * argv[]){
172     (void)argc;
173     (void)argv;
174 
175     // check audio interface
176     const btstack_audio_sink_t * audio_sink   = btstack_audio_sink_get_instance();
177     if (!audio_sink){
178         printf("BTstack Audio Sink not setup\n");
179         return 10;
180     }
181 
182     const btstack_audio_source_t * audio_source = btstack_audio_source_get_instance();
183     if (!audio_source){
184         printf("BTstack Audio Source not setup\n");
185         return 10;
186     }
187 
188     start_loopback(&start_stop_timer);
189 
190     return 0;
191 }
192