xref: /btstack/example/sco_demo_util.c (revision dbb41bfe0be2271732b55efa4a68ae0cb2ab7b93)
1f7c85330SMatthias Ringwald /*
2f7c85330SMatthias Ringwald  * Copyright (C) 2016 BlueKitchen GmbH
3f7c85330SMatthias Ringwald  *
4f7c85330SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
5f7c85330SMatthias Ringwald  * modification, are permitted provided that the following conditions
6f7c85330SMatthias Ringwald  * are met:
7f7c85330SMatthias Ringwald  *
8f7c85330SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
9f7c85330SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
10f7c85330SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11f7c85330SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
12f7c85330SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
13f7c85330SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
14f7c85330SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
15f7c85330SMatthias Ringwald  *    from this software without specific prior written permission.
16f7c85330SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
17f7c85330SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
18f7c85330SMatthias Ringwald  *    monetary gain.
19f7c85330SMatthias Ringwald  *
20f7c85330SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21f7c85330SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22f7c85330SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23f7c85330SMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24f7c85330SMatthias Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25f7c85330SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26f7c85330SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27f7c85330SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28f7c85330SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29f7c85330SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30f7c85330SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31f7c85330SMatthias Ringwald  * SUCH DAMAGE.
32f7c85330SMatthias Ringwald  *
33f7c85330SMatthias Ringwald  * Please inquire about commercial licensing options at
34f7c85330SMatthias Ringwald  * [email protected]
35f7c85330SMatthias Ringwald  *
36f7c85330SMatthias Ringwald  */
37f7c85330SMatthias Ringwald 
38f7c85330SMatthias Ringwald /*
39f7c85330SMatthias Ringwald  * sco_demo_util.c - send/receive test data via SCO, used by hfp_*_demo and hsp_*_demo
40f7c85330SMatthias Ringwald  */
41f7c85330SMatthias Ringwald 
422ec72fbbSMilanka Ringwald 
432ec72fbbSMilanka Ringwald #include <stdio.h>
442ec72fbbSMilanka Ringwald 
45f7c85330SMatthias Ringwald #include "sco_demo_util.h"
46fcb08cdbSMilanka Ringwald #include "btstack_debug.h"
470c87db9eSMilanka Ringwald #include "btstack_sbc.h"
48379d044eSMilanka Ringwald #include "btstack_cvsd_plc.h"
49220eb563SMilanka Ringwald #include "hfp_msbc.h"
50220eb563SMilanka Ringwald #include "hfp.h"
51fcb08cdbSMilanka Ringwald 
52fbc7c9f2SMilanka Ringwald #include "wav_util.h"
53fbc7c9f2SMilanka Ringwald 
54f7c85330SMatthias Ringwald // configure test mode
55f7c85330SMatthias Ringwald #define SCO_DEMO_MODE_SINE		0
56f7c85330SMatthias Ringwald #define SCO_DEMO_MODE_ASCII		1
57f7c85330SMatthias Ringwald #define SCO_DEMO_MODE_COUNTER	2
58f7c85330SMatthias Ringwald 
598b29cfc6SMatthias Ringwald 
60f7c85330SMatthias Ringwald // SCO demo configuration
61fcb08cdbSMilanka Ringwald #define SCO_DEMO_MODE SCO_DEMO_MODE_SINE
62f7c85330SMatthias Ringwald #define SCO_REPORT_PERIOD 100
63f7c85330SMatthias Ringwald 
648b29cfc6SMatthias Ringwald #ifdef HAVE_POSIX_FILE_IO
658b29cfc6SMatthias Ringwald #define SCO_WAV_FILENAME      "sco_input.wav"
66d5e5f834SMatthias Ringwald #define SCO_MSBC_OUT_FILENAME "sco_output.msbc"
67d5e5f834SMatthias Ringwald #define SCO_MSBC_IN_FILENAME  "sco_input.mscb"
68220eb563SMilanka Ringwald 
69202da317SMilanka Ringwald #define SCO_WAV_DURATION_IN_SECONDS 15
708b29cfc6SMatthias Ringwald #endif
718b29cfc6SMatthias Ringwald 
72f7c85330SMatthias Ringwald 
73f7c85330SMatthias Ringwald #if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE)
74f7c85330SMatthias Ringwald #define USE_PORTAUDIO
75f7c85330SMatthias Ringwald #endif
76f7c85330SMatthias Ringwald 
77f7c85330SMatthias Ringwald #ifdef USE_PORTAUDIO
78f7c85330SMatthias Ringwald #include <portaudio.h>
79*dbb41bfeSMilanka Ringwald #include "btstack_ring_buffer.h"
80*dbb41bfeSMilanka Ringwald 
818b29cfc6SMatthias Ringwald // portaudio config
828b29cfc6SMatthias Ringwald #define NUM_CHANNELS            1
83*dbb41bfeSMilanka Ringwald 
84*dbb41bfeSMilanka Ringwald #define CVSD_SAMPLE_RATE        8000
85*dbb41bfeSMilanka Ringwald #define CVSD_FRAMES_PER_BUFFER  24
86*dbb41bfeSMilanka Ringwald #define CVSD_PA_SAMPLE_TYPE     paInt8
87*dbb41bfeSMilanka Ringwald #define CVSD_BYTES_PER_FRAME    (1*NUM_CHANNELS)
88*dbb41bfeSMilanka Ringwald #define CVSD_PREBUFFER_MS       5
89*dbb41bfeSMilanka Ringwald #define CVSD_PREBUFFER_BYTES    (CVSD_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME)
90*dbb41bfeSMilanka Ringwald 
91*dbb41bfeSMilanka Ringwald #define MSBC_SAMPLE_RATE        16000
92*dbb41bfeSMilanka Ringwald #define MSBC_FRAMES_PER_BUFFER  120
93*dbb41bfeSMilanka Ringwald #define MSBC_PA_SAMPLE_TYPE     paInt16
94*dbb41bfeSMilanka Ringwald #define MSBC_BYTES_PER_FRAME    (2*NUM_CHANNELS)
95*dbb41bfeSMilanka Ringwald #define MSBC_PREBUFFER_MS       50
96*dbb41bfeSMilanka Ringwald #define MSBC_PREBUFFER_BYTES    (MSBC_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME)
97*dbb41bfeSMilanka Ringwald 
98f7c85330SMatthias Ringwald // portaudio globals
99f7c85330SMatthias Ringwald static  PaStream * stream;
100*dbb41bfeSMilanka Ringwald static uint8_t pa_stream_started = 0;
101*dbb41bfeSMilanka Ringwald 
102*dbb41bfeSMilanka Ringwald static uint8_t ring_buffer_storage[2*MSBC_PREBUFFER_BYTES];
103*dbb41bfeSMilanka Ringwald static btstack_ring_buffer_t ring_buffer;
104f7c85330SMatthias Ringwald #endif
105f7c85330SMatthias Ringwald 
106fcb08cdbSMilanka Ringwald 
107fcb08cdbSMilanka Ringwald static int dump_data = 1;
108fcb08cdbSMilanka Ringwald static int count_sent = 0;
109fcb08cdbSMilanka Ringwald static int count_received = 0;
110d76591efSMatthias Ringwald static uint8_t negotiated_codec = 0;
111220eb563SMilanka Ringwald static int num_audio_frames = 0;
112fcb08cdbSMilanka Ringwald 
113d5e5f834SMatthias Ringwald FILE * msbc_file_in;
114d5e5f834SMatthias Ringwald FILE * msbc_file_out;
1157294d009SMatthias Ringwald 
116f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
117d6a06398SMatthias Ringwald 
118f7c85330SMatthias Ringwald 
1198b29cfc6SMatthias Ringwald #ifdef SCO_WAV_FILENAME
1202afeea7fSMilanka Ringwald static btstack_sbc_decoder_state_t decoder_state;
12182e01da0SMilanka Ringwald static btstack_cvsd_plc_state_t cvsd_plc_state;
122*dbb41bfeSMilanka Ringwald static int num_samples_to_write;
123*dbb41bfeSMilanka Ringwald 
124*dbb41bfeSMilanka Ringwald #ifdef USE_PORTAUDIO
125*dbb41bfeSMilanka Ringwald static int patestCallback( const void *inputBuffer, void *outputBuffer,
126*dbb41bfeSMilanka Ringwald                            unsigned long framesPerBuffer,
127*dbb41bfeSMilanka Ringwald                            const PaStreamCallbackTimeInfo* timeInfo,
128*dbb41bfeSMilanka Ringwald                            PaStreamCallbackFlags statusFlags,
129*dbb41bfeSMilanka Ringwald                            void *userData ) {
130*dbb41bfeSMilanka Ringwald     (void) timeInfo; /* Prevent unused variable warnings. */
131*dbb41bfeSMilanka Ringwald     (void) statusFlags;
132*dbb41bfeSMilanka Ringwald     (void) inputBuffer;
133*dbb41bfeSMilanka Ringwald 
134*dbb41bfeSMilanka Ringwald     uint32_t bytes_read = 0;
135*dbb41bfeSMilanka Ringwald     int bytes_per_buffer = framesPerBuffer;
136*dbb41bfeSMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
137*dbb41bfeSMilanka Ringwald         bytes_per_buffer *= MSBC_BYTES_PER_FRAME;
138*dbb41bfeSMilanka Ringwald     } else {
139*dbb41bfeSMilanka Ringwald         bytes_per_buffer *= CVSD_BYTES_PER_FRAME;
140*dbb41bfeSMilanka Ringwald     }
141*dbb41bfeSMilanka Ringwald 
142*dbb41bfeSMilanka Ringwald     if (btstack_ring_buffer_bytes_available(&ring_buffer) >= bytes_per_buffer){
143*dbb41bfeSMilanka Ringwald         btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_per_buffer, &bytes_read);
144*dbb41bfeSMilanka Ringwald     } else {
145*dbb41bfeSMilanka Ringwald         printf("NOT ENOUGH DATA!\n");
146*dbb41bfeSMilanka Ringwald         memset(outputBuffer, 0, bytes_per_buffer);
147*dbb41bfeSMilanka Ringwald     }
148*dbb41bfeSMilanka Ringwald     // printf("bytes avail after read: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer));
149*dbb41bfeSMilanka Ringwald     return 0;
150*dbb41bfeSMilanka Ringwald }
151*dbb41bfeSMilanka Ringwald #endif
1528b29cfc6SMatthias Ringwald 
153fcb08cdbSMilanka Ringwald static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){
154*dbb41bfeSMilanka Ringwald     // printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels);
155*dbb41bfeSMilanka Ringwald #ifdef USE_PORTAUDIO
156*dbb41bfeSMilanka Ringwald     if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= MSBC_PREBUFFER_BYTES){
157*dbb41bfeSMilanka Ringwald         /* -- start stream -- */
158*dbb41bfeSMilanka Ringwald         PaError err = Pa_StartStream(stream);
159*dbb41bfeSMilanka Ringwald         if (err != paNoError){
160*dbb41bfeSMilanka Ringwald             printf("Error starting the stream: \"%s\"\n",  Pa_GetErrorText(err));
161*dbb41bfeSMilanka Ringwald             return;
162*dbb41bfeSMilanka Ringwald         }
163*dbb41bfeSMilanka Ringwald         pa_stream_started = 1;
164*dbb41bfeSMilanka Ringwald     }
165*dbb41bfeSMilanka Ringwald     btstack_ring_buffer_write(&ring_buffer, (uint8_t *)data, num_samples*num_channels*2);
166*dbb41bfeSMilanka Ringwald     // printf("bytes avail after write: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer));
167*dbb41bfeSMilanka Ringwald #endif
168*dbb41bfeSMilanka Ringwald 
169fcb08cdbSMilanka Ringwald     if (!num_samples_to_write) return;
170fcb08cdbSMilanka Ringwald 
171fcb08cdbSMilanka Ringwald     num_samples = btstack_min(num_samples, num_samples_to_write);
172fcb08cdbSMilanka Ringwald     num_samples_to_write -= num_samples;
173fcb08cdbSMilanka Ringwald 
174fbc7c9f2SMilanka Ringwald     wav_writer_write_int16(num_samples, data);
175fcb08cdbSMilanka Ringwald 
176fcb08cdbSMilanka Ringwald     if (num_samples_to_write == 0){
177fcb08cdbSMilanka Ringwald         sco_demo_close();
178fcb08cdbSMilanka Ringwald     }
179fcb08cdbSMilanka Ringwald }
180fcb08cdbSMilanka Ringwald 
181220eb563SMilanka Ringwald static void sco_demo_fill_audio_frame(void){
182220eb563SMilanka Ringwald     if (!hfp_msbc_can_encode_audio_frame_now()) return;
183fbc7c9f2SMilanka Ringwald     int num_samples = hfp_msbc_num_audio_samples_per_frame();
184fbc7c9f2SMilanka Ringwald     int16_t sample_buffer[num_samples];
185fbc7c9f2SMilanka Ringwald     wav_synthesize_sine_wave_int16(num_samples, sample_buffer);
186d6a06398SMatthias Ringwald     hfp_msbc_encode_audio_frame(sample_buffer);
187220eb563SMilanka Ringwald     num_audio_frames++;
188220eb563SMilanka Ringwald }
189fcb08cdbSMilanka Ringwald 
190fcb08cdbSMilanka Ringwald static void sco_demo_init_mSBC(void){
191fbc7c9f2SMilanka Ringwald     int sample_rate = 16000;
192fbc7c9f2SMilanka Ringwald     wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate);
193fbc7c9f2SMilanka Ringwald     btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, NULL);
194fcb08cdbSMilanka Ringwald 
195fbc7c9f2SMilanka Ringwald     num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
196220eb563SMilanka Ringwald 
197220eb563SMilanka Ringwald     hfp_msbc_init();
198220eb563SMilanka Ringwald     sco_demo_fill_audio_frame();
199973d7173SMatthias Ringwald 
200d5e5f834SMatthias Ringwald #ifdef SCO_MSBC_IN_FILENAME
201d5e5f834SMatthias Ringwald     msbc_file_in = fopen(SCO_MSBC_IN_FILENAME, "wb");
202d5e5f834SMatthias Ringwald     printf("SCO Demo: creating mSBC in file %s, %p\n", SCO_MSBC_IN_FILENAME, msbc_file_in);
203d5e5f834SMatthias Ringwald #endif
2047294d009SMatthias Ringwald #ifdef SCO_MSBC_OUT_FILENAME
205d5e5f834SMatthias Ringwald     msbc_file_out = fopen(SCO_MSBC_OUT_FILENAME, "wb");
206d5e5f834SMatthias Ringwald     printf("SCO Demo: creating mSBC out file %s, %p\n", SCO_MSBC_OUT_FILENAME, msbc_file_out);
2077294d009SMatthias Ringwald #endif
208*dbb41bfeSMilanka Ringwald 
209*dbb41bfeSMilanka Ringwald #ifdef USE_PORTAUDIO
210*dbb41bfeSMilanka Ringwald     PaError err;
211*dbb41bfeSMilanka Ringwald     PaStreamParameters outputParameters;
212*dbb41bfeSMilanka Ringwald 
213*dbb41bfeSMilanka Ringwald     /* -- initialize PortAudio -- */
214*dbb41bfeSMilanka Ringwald     err = Pa_Initialize();
215*dbb41bfeSMilanka Ringwald     if( err != paNoError ) return;
216*dbb41bfeSMilanka Ringwald     /* -- setup input and output -- */
217*dbb41bfeSMilanka Ringwald     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
218*dbb41bfeSMilanka Ringwald     outputParameters.channelCount = NUM_CHANNELS;
219*dbb41bfeSMilanka Ringwald     outputParameters.sampleFormat = MSBC_PA_SAMPLE_TYPE;
220*dbb41bfeSMilanka Ringwald     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
221*dbb41bfeSMilanka Ringwald     outputParameters.hostApiSpecificStreamInfo = NULL;
222*dbb41bfeSMilanka Ringwald     /* -- setup stream -- */
223*dbb41bfeSMilanka Ringwald     err = Pa_OpenStream(
224*dbb41bfeSMilanka Ringwald            &stream,
225*dbb41bfeSMilanka Ringwald            NULL, // &inputParameters,
226*dbb41bfeSMilanka Ringwald            &outputParameters,
227*dbb41bfeSMilanka Ringwald            MSBC_SAMPLE_RATE,
228*dbb41bfeSMilanka Ringwald            MSBC_FRAMES_PER_BUFFER,
229*dbb41bfeSMilanka Ringwald            paClipOff, /* we won't output out of range samples so don't bother clipping them */
230*dbb41bfeSMilanka Ringwald            patestCallback,      /* no callback, use blocking API */
231*dbb41bfeSMilanka Ringwald            NULL );    /* no callback, so no callback userData */
232*dbb41bfeSMilanka Ringwald     if (err != paNoError){
233*dbb41bfeSMilanka Ringwald         printf("Error initializing portaudio: \"%s\"\n",  Pa_GetErrorText(err));
234*dbb41bfeSMilanka Ringwald         return;
235*dbb41bfeSMilanka Ringwald     }
236*dbb41bfeSMilanka Ringwald     memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage));
237*dbb41bfeSMilanka Ringwald     btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));
238*dbb41bfeSMilanka Ringwald     pa_stream_started = 0;
239*dbb41bfeSMilanka Ringwald #endif
240fcb08cdbSMilanka Ringwald }
241fcb08cdbSMilanka Ringwald 
242fcb08cdbSMilanka Ringwald static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){
243fcb08cdbSMilanka Ringwald     if (num_samples_to_write){
244d5e5f834SMatthias Ringwald         if (msbc_file_in){
245d5e5f834SMatthias Ringwald             // log incoming mSBC data for testing
246d5e5f834SMatthias Ringwald             fwrite(packet+3, size-3, 1, msbc_file_in);
247d5e5f834SMatthias Ringwald         }
248fcb08cdbSMilanka Ringwald     }
249*dbb41bfeSMilanka Ringwald     btstack_sbc_decoder_process_data(&decoder_state, (packet[1] >> 4) & 3, packet+3, size-3);
250fcb08cdbSMilanka Ringwald }
251fcb08cdbSMilanka Ringwald 
252fbc7c9f2SMilanka Ringwald static void sco_demo_init_CVSD(void){
253fbc7c9f2SMilanka Ringwald     int sample_rate = 8000;
254fbc7c9f2SMilanka Ringwald     wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate);
255fbc7c9f2SMilanka Ringwald     btstack_cvsd_plc_init(&cvsd_plc_state);
256fbc7c9f2SMilanka Ringwald     num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
257*dbb41bfeSMilanka Ringwald 
258*dbb41bfeSMilanka Ringwald #ifdef USE_PORTAUDIO
259*dbb41bfeSMilanka Ringwald     PaError err;
260*dbb41bfeSMilanka Ringwald     PaStreamParameters outputParameters;
261*dbb41bfeSMilanka Ringwald 
262*dbb41bfeSMilanka Ringwald     /* -- initialize PortAudio -- */
263*dbb41bfeSMilanka Ringwald     err = Pa_Initialize();
264*dbb41bfeSMilanka Ringwald     if( err != paNoError ) return;
265*dbb41bfeSMilanka Ringwald     /* -- setup input and output -- */
266*dbb41bfeSMilanka Ringwald     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
267*dbb41bfeSMilanka Ringwald     outputParameters.channelCount = NUM_CHANNELS;
268*dbb41bfeSMilanka Ringwald     outputParameters.sampleFormat = CVSD_PA_SAMPLE_TYPE;
269*dbb41bfeSMilanka Ringwald     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
270*dbb41bfeSMilanka Ringwald     outputParameters.hostApiSpecificStreamInfo = NULL;
271*dbb41bfeSMilanka Ringwald     /* -- setup stream -- */
272*dbb41bfeSMilanka Ringwald     err = Pa_OpenStream(
273*dbb41bfeSMilanka Ringwald            &stream,
274*dbb41bfeSMilanka Ringwald            NULL, // &inputParameters,
275*dbb41bfeSMilanka Ringwald            &outputParameters,
276*dbb41bfeSMilanka Ringwald            CVSD_SAMPLE_RATE,
277*dbb41bfeSMilanka Ringwald            CVSD_FRAMES_PER_BUFFER,
278*dbb41bfeSMilanka Ringwald            paClipOff, /* we won't output out of range samples so don't bother clipping them */
279*dbb41bfeSMilanka Ringwald            patestCallback,      /* no callback, use blocking API */
280*dbb41bfeSMilanka Ringwald            NULL );    /* no callback, so no callback userData */
281*dbb41bfeSMilanka Ringwald     if (err != paNoError){
282*dbb41bfeSMilanka Ringwald         printf("Error initializing portaudio: \"%s\"\n",  Pa_GetErrorText(err));
283*dbb41bfeSMilanka Ringwald         return;
284*dbb41bfeSMilanka Ringwald     }
285*dbb41bfeSMilanka Ringwald     memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage));
286*dbb41bfeSMilanka Ringwald     btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));
287*dbb41bfeSMilanka Ringwald     pa_stream_started = 0;
288*dbb41bfeSMilanka Ringwald #endif
289fbc7c9f2SMilanka Ringwald }
290fbc7c9f2SMilanka Ringwald 
291fcb08cdbSMilanka Ringwald static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){
292*dbb41bfeSMilanka Ringwald     if (!num_samples_to_write) return;
293*dbb41bfeSMilanka Ringwald 
294fcb08cdbSMilanka Ringwald     const int num_samples = size - 3;
295fcb08cdbSMilanka Ringwald     const int samples_to_write = btstack_min(num_samples, num_samples_to_write);
29682e01da0SMilanka Ringwald     int8_t audio_frame_out[24];
297379d044eSMilanka Ringwald 
298*dbb41bfeSMilanka Ringwald 
299fbc7c9f2SMilanka Ringwald     // memcpy(audio_frame_out, (int8_t*)(packet+3), 24);
300fbc7c9f2SMilanka Ringwald     btstack_cvsd_plc_process_data(&cvsd_plc_state, (int8_t *)(packet+3), num_samples, audio_frame_out);
301379d044eSMilanka Ringwald 
302fbc7c9f2SMilanka Ringwald     wav_writer_write_int8(samples_to_write, audio_frame_out);
303fcb08cdbSMilanka Ringwald     num_samples_to_write -= samples_to_write;
304fcb08cdbSMilanka Ringwald     if (num_samples_to_write == 0){
305fcb08cdbSMilanka Ringwald         sco_demo_close();
306fcb08cdbSMilanka Ringwald     }
307*dbb41bfeSMilanka Ringwald #ifdef USE_PORTAUDIO
308*dbb41bfeSMilanka Ringwald     if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= CVSD_PREBUFFER_BYTES){
309*dbb41bfeSMilanka Ringwald         /* -- start stream -- */
310*dbb41bfeSMilanka Ringwald         PaError err = Pa_StartStream(stream);
311*dbb41bfeSMilanka Ringwald         if (err != paNoError){
312*dbb41bfeSMilanka Ringwald             printf("Error starting the stream: \"%s\"\n",  Pa_GetErrorText(err));
313*dbb41bfeSMilanka Ringwald             return;
314fcb08cdbSMilanka Ringwald         }
315*dbb41bfeSMilanka Ringwald         pa_stream_started = 1;
316*dbb41bfeSMilanka Ringwald     }
317*dbb41bfeSMilanka Ringwald     btstack_ring_buffer_write(&ring_buffer, (uint8_t *)audio_frame_out, samples_to_write);
318*dbb41bfeSMilanka Ringwald #endif
319fcb08cdbSMilanka Ringwald }
320fcb08cdbSMilanka Ringwald 
3218b29cfc6SMatthias Ringwald #endif
3224a96141eSMatthias Ringwald #endif
3238b29cfc6SMatthias Ringwald 
324fcb08cdbSMilanka Ringwald void sco_demo_close(void){
325fcb08cdbSMilanka Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
32626463303SMilanka Ringwald #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
327fbc7c9f2SMilanka Ringwald     wav_writer_close();
32826463303SMilanka Ringwald     printf("SCO demo statistics: ");
32926463303SMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
33026463303SMilanka Ringwald         printf("Used mSBC with PLC, number of processed frames: \n - %d good frames, \n - %d zero frames, \n - %d bad frames.", decoder_state.good_frames_nr, decoder_state.zero_frames_nr, decoder_state.bad_frames_nr);
33126463303SMilanka Ringwald     } else {
33226463303SMilanka Ringwald         printf("Used CVSD with PLC, number of proccesed frames: \n - %d good frames, \n - %d bad frames.", cvsd_plc_state.good_frames_nr, cvsd_plc_state.bad_frames_nr);
33326463303SMilanka Ringwald     }
33426463303SMilanka Ringwald #endif
33526463303SMilanka Ringwald #endif
33626463303SMilanka Ringwald 
337*dbb41bfeSMilanka Ringwald #ifdef HAVE_PORTAUDIO
338*dbb41bfeSMilanka Ringwald     if (pa_stream_started){
339*dbb41bfeSMilanka Ringwald         PaError err = Pa_StopStream(stream);
340*dbb41bfeSMilanka Ringwald         if (err != paNoError){
341*dbb41bfeSMilanka Ringwald             printf("Error stopping the stream: \"%s\"\n",  Pa_GetErrorText(err));
342*dbb41bfeSMilanka Ringwald             return;
343*dbb41bfeSMilanka Ringwald         }
344*dbb41bfeSMilanka Ringwald         pa_stream_started = 0;
345*dbb41bfeSMilanka Ringwald         err = Pa_CloseStream(stream);
346*dbb41bfeSMilanka Ringwald         if (err != paNoError){
347*dbb41bfeSMilanka Ringwald             printf("Error closing the stream: \"%s\"\n",  Pa_GetErrorText(err));
348*dbb41bfeSMilanka Ringwald             return;
349*dbb41bfeSMilanka Ringwald         }
350*dbb41bfeSMilanka Ringwald 
351*dbb41bfeSMilanka Ringwald         err = Pa_Terminate();
352*dbb41bfeSMilanka Ringwald         if (err != paNoError){
353*dbb41bfeSMilanka Ringwald             printf("Error terminating portaudio: \"%s\"\n",  Pa_GetErrorText(err));
354*dbb41bfeSMilanka Ringwald             return;
355*dbb41bfeSMilanka Ringwald         }
356*dbb41bfeSMilanka Ringwald     }
357*dbb41bfeSMilanka Ringwald #endif
358*dbb41bfeSMilanka Ringwald 
35926463303SMilanka Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
360fcb08cdbSMilanka Ringwald #ifdef SCO_WAV_FILENAME
361fcb08cdbSMilanka Ringwald 
362613518d1SMilanka Ringwald #if 0
363fcb08cdbSMilanka Ringwald     printf("SCO Demo: closing wav file\n");
364220eb563SMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
3656e046a36SMatthias Ringwald         wav_writer_state_t * writer_state = &wav_writer_state;
366fcb08cdbSMilanka Ringwald         if (!writer_state->wav_file) return;
367fcb08cdbSMilanka Ringwald         rewind(writer_state->wav_file);
3682afeea7fSMilanka Ringwald         write_wav_header(writer_state->wav_file, writer_state->total_num_samples, btstack_sbc_decoder_num_channels(&decoder_state), btstack_sbc_decoder_sample_rate(&decoder_state),2);
369fcb08cdbSMilanka Ringwald         fclose(writer_state->wav_file);
370fcb08cdbSMilanka Ringwald         writer_state->wav_file = NULL;
371fcb08cdbSMilanka Ringwald     }
372613518d1SMilanka Ringwald #endif
373fcb08cdbSMilanka Ringwald #endif
374fcb08cdbSMilanka Ringwald #endif
375fcb08cdbSMilanka Ringwald }
376fcb08cdbSMilanka Ringwald 
377fcb08cdbSMilanka Ringwald void sco_demo_set_codec(uint8_t codec){
378fcb08cdbSMilanka Ringwald     if (negotiated_codec == codec) return;
379fcb08cdbSMilanka Ringwald     negotiated_codec = codec;
380fcb08cdbSMilanka Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
38117cd946eSMatthias Ringwald #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
382220eb563SMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
383fcb08cdbSMilanka Ringwald         sco_demo_init_mSBC();
384fcb08cdbSMilanka Ringwald     } else {
385fcb08cdbSMilanka Ringwald         sco_demo_init_CVSD();
386fcb08cdbSMilanka Ringwald     }
387fcb08cdbSMilanka Ringwald #endif
388fcb08cdbSMilanka Ringwald #endif
389fcb08cdbSMilanka Ringwald }
390fcb08cdbSMilanka Ringwald 
391f7c85330SMatthias Ringwald void sco_demo_init(void){
392f7c85330SMatthias Ringwald 
393f7c85330SMatthias Ringwald 	// status
394f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
395f7c85330SMatthias Ringwald #ifdef HAVE_PORTAUDIO
396f7c85330SMatthias Ringwald 	printf("SCO Demo: Sending sine wave, audio output via portaudio.\n");
397f7c85330SMatthias Ringwald #else
398f7c85330SMatthias Ringwald 	printf("SCO Demo: Sending sine wave, hexdump received data.\n");
399f7c85330SMatthias Ringwald #endif
400f7c85330SMatthias Ringwald #endif
401f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
402f7c85330SMatthias Ringwald 	printf("SCO Demo: Sending ASCII blocks, print received data.\n");
403f7c85330SMatthias Ringwald #endif
404f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
405f7c85330SMatthias Ringwald 	printf("SCO Demo: Sending counter value, hexdump received data.\n");
406f7c85330SMatthias Ringwald #endif
407f7c85330SMatthias Ringwald 
4087294d009SMatthias Ringwald #if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE
409f7c85330SMatthias Ringwald     hci_set_sco_voice_setting(0x03);    // linear, unsigned, 8-bit, transparent
4107294d009SMatthias Ringwald #endif
411f7c85330SMatthias Ringwald 
412f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
413f7c85330SMatthias Ringwald     phase = 'a';
414f7c85330SMatthias Ringwald #endif
415f7c85330SMatthias Ringwald }
416f7c85330SMatthias Ringwald 
4174a96141eSMatthias Ringwald static void sco_report(void){
4184a96141eSMatthias Ringwald     printf("SCO: sent %u, received %u\n", count_sent, count_received);
4194a96141eSMatthias Ringwald }
420f7c85330SMatthias Ringwald 
421f7c85330SMatthias Ringwald void sco_demo_send(hci_con_handle_t sco_handle){
422f7c85330SMatthias Ringwald 
423f7c85330SMatthias Ringwald     if (!sco_handle) return;
424f7c85330SMatthias Ringwald 
425f7c85330SMatthias Ringwald     const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length();
426f7c85330SMatthias Ringwald     const int sco_payload_length = sco_packet_length - 3;
427f7c85330SMatthias Ringwald 
428f7c85330SMatthias Ringwald     hci_reserve_packet_buffer();
429f7c85330SMatthias Ringwald     uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
430f7c85330SMatthias Ringwald     // set handle + flags
431f7c85330SMatthias Ringwald     little_endian_store_16(sco_packet, 0, sco_handle);
432f7c85330SMatthias Ringwald     // set len
433f7c85330SMatthias Ringwald     sco_packet[2] = sco_payload_length;
434220eb563SMilanka Ringwald     const int audio_samples_per_packet = sco_payload_length;    // for 8-bit data. for 16-bit data it's /2
435f7c85330SMatthias Ringwald 
436f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
437220eb563SMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
438220eb563SMilanka Ringwald 
439220eb563SMilanka Ringwald         if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){
440220eb563SMilanka Ringwald             log_error("mSBC stream is empty.");
441220eb563SMilanka Ringwald         }
442220eb563SMilanka Ringwald         hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length);
443d5e5f834SMatthias Ringwald         if (msbc_file_out){
444d76591efSMatthias Ringwald             // log outgoing mSBC data for testing
445d5e5f834SMatthias Ringwald             fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out);
446d76591efSMatthias Ringwald         }
4477294d009SMatthias Ringwald 
448220eb563SMilanka Ringwald         sco_demo_fill_audio_frame();
449220eb563SMilanka Ringwald     } else {
450fbc7c9f2SMilanka Ringwald         wav_synthesize_sine_wave_int8(audio_samples_per_packet, (int8_t *) (sco_packet+3));
451220eb563SMilanka Ringwald     }
452f7c85330SMatthias Ringwald #else
453f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
454220eb563SMilanka Ringwald     memset(&sco_packet[3], phase++, audio_samples_per_packet);
455f7c85330SMatthias Ringwald     if (phase > 'z') phase = 'a';
456f7c85330SMatthias Ringwald #else
45738b2eaafSMatthias Ringwald     int j;
458220eb563SMilanka Ringwald     for (j=0;j<audio_samples_per_packet;j++){
45938b2eaafSMatthias Ringwald         sco_packet[3+j] = phase++;
460f7c85330SMatthias Ringwald     }
461f7c85330SMatthias Ringwald #endif
462f7c85330SMatthias Ringwald #endif
463220eb563SMilanka Ringwald 
464f7c85330SMatthias Ringwald     hci_send_sco_packet_buffer(sco_packet_length);
465f7c85330SMatthias Ringwald 
466f7c85330SMatthias Ringwald     // request another send event
467f7c85330SMatthias Ringwald     hci_request_sco_can_send_now_event();
468f7c85330SMatthias Ringwald 
4694a96141eSMatthias Ringwald     count_sent++;
4704a96141eSMatthias Ringwald     if ((count_sent % SCO_REPORT_PERIOD) == 0) sco_report();
471f7c85330SMatthias Ringwald }
472f7c85330SMatthias Ringwald 
473f7c85330SMatthias Ringwald /**
474f7c85330SMatthias Ringwald  * @brief Process received data
475f7c85330SMatthias Ringwald  */
476f7c85330SMatthias Ringwald void sco_demo_receive(uint8_t * packet, uint16_t size){
477f7c85330SMatthias Ringwald 
478fcb08cdbSMilanka Ringwald     dump_data = 1;
4798b29cfc6SMatthias Ringwald 
4804a96141eSMatthias Ringwald     count_received++;
4814a96141eSMatthias Ringwald     // if ((count_received % SCO_REPORT_PERIOD) == 0) sco_report();
4824a96141eSMatthias Ringwald 
4834a96141eSMatthias Ringwald 
4844a96141eSMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
4858b29cfc6SMatthias Ringwald #ifdef SCO_WAV_FILENAME
486220eb563SMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
487fcb08cdbSMilanka Ringwald         sco_demo_receive_mSBC(packet, size);
488fcb08cdbSMilanka Ringwald     } else {
489fcb08cdbSMilanka Ringwald         sco_demo_receive_CVSD(packet, size);
4908b29cfc6SMatthias Ringwald     }
491*dbb41bfeSMilanka Ringwald     dump_data = 0;
4928b29cfc6SMatthias Ringwald #endif
4934a96141eSMatthias Ringwald #endif
4948b29cfc6SMatthias Ringwald 
495b3f76298SMilanka Ringwald     if (packet[1] & 0x30){
496b3f76298SMilanka Ringwald         printf("SCO CRC Error: %x - data: ", (packet[1] & 0x30) >> 4);
497b3f76298SMilanka Ringwald         log_info("SCO CRC Error: %x - data: ", (packet[1] & 0x30) >> 4);
498f7c85330SMatthias Ringwald         printf_hexdump(&packet[3], size-3);
499b3f76298SMilanka Ringwald 
500f7c85330SMatthias Ringwald         return;
501f7c85330SMatthias Ringwald     }
5028b29cfc6SMatthias Ringwald     if (dump_data){
503f7c85330SMatthias Ringwald         printf("data: ");
504f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
505f7c85330SMatthias Ringwald         int i;
506f7c85330SMatthias Ringwald         for (i=3;i<size;i++){
507f7c85330SMatthias Ringwald             printf("%c", packet[i]);
508f7c85330SMatthias Ringwald         }
509f7c85330SMatthias Ringwald         printf("\n");
5108b29cfc6SMatthias Ringwald         dump_data = 0;
5118b29cfc6SMatthias Ringwald #else
512f7c85330SMatthias Ringwald         printf_hexdump(&packet[3], size-3);
513f7c85330SMatthias Ringwald #endif
5148b29cfc6SMatthias Ringwald     }
515f7c85330SMatthias Ringwald }
516