xref: /btstack/example/audio_duplex.c (revision 04cbc450eae7c055b495efb5f35ee701878c3c22)
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 
121     const btstack_audio_sink_t   * audio_sink   = btstack_audio_sink_get_instance();
122     const btstack_audio_source_t * audio_source = btstack_audio_source_get_instance();
123 
124     // prepare audio buffer
125     btstack_ring_buffer_init(&audio_buffer, (uint8_t*) &audio_buffer_storage[0], sizeof(audio_buffer_storage));
126 
127     // setup audio: mono input -> stereo output
128     audio_sink->init(2, samplerate, &audio_playback);
129     audio_source->init(1, samplerate, &audio_recording);
130 
131     // start duplex
132     audio_sink->start_stream();
133     audio_source->start_stream();
134 
135     printf("Start Audio Loopback\n");
136 
137 #ifdef TEST_START_STOP_INTERVAL
138     // schedule stop
139     btstack_run_loop_set_timer_handler(ts, &stop_loopback);
140     btstack_run_loop_set_timer(ts, TEST_START_STOP_INTERVAL);
141     btstack_run_loop_add_timer(ts);
142 #endif
143 }
144 
145 #ifdef TEST_START_STOP_INTERVAL
146 static void stop_loopback(btstack_timer_source_t * ts){
147     const btstack_audio_sink_t   * audio_sink   = btstack_audio_sink_get_instance();
148     const btstack_audio_source_t * audio_source = btstack_audio_source_get_instance();
149 
150     // stop streams
151     audio_sink->stop_stream();
152     audio_source->stop_stream();
153 
154     // close audio
155     audio_sink->close();
156     audio_source->close();
157 
158     playback_started = 0;
159 
160     printf("Stop Audio Loopback\n");
161 
162     // schedule stop
163     btstack_run_loop_set_timer_handler(ts, &start_loopback);
164     btstack_run_loop_set_timer(ts, TEST_START_STOP_INTERVAL);
165     btstack_run_loop_add_timer(ts);
166 }
167 #endif
168 
169 int btstack_main(int argc, const char * argv[]);
170 int btstack_main(int argc, const char * argv[]){
171     (void)argc;
172     (void)argv;
173 
174     // check audio interface
175     const btstack_audio_sink_t * audio_sink   = btstack_audio_sink_get_instance();
176     if (!audio_sink){
177         printf("BTstack Audio Sink not setup\n");
178         return 10;
179     }
180 
181     const btstack_audio_source_t * audio_source = btstack_audio_source_get_instance();
182     if (!audio_source){
183         printf("BTstack Audio Source not setup\n");
184         return 10;
185     }
186 
187     start_loopback(&start_stop_timer);
188 
189     return 0;
190 }
191