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