xref: /btstack/example/sco_demo_util.c (revision 35fd3fb9bcfee6840fc3705481bf7cc5d468cd11)
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"
47*35fd3fb9SMatthias Ringwald #include "classic/btstack_sbc.h"
48*35fd3fb9SMatthias Ringwald #include "classic/btstack_cvsd_plc.h"
49*35fd3fb9SMatthias Ringwald #include "classic/hfp_msbc.h"
50*35fd3fb9SMatthias Ringwald #include "classic/hfp.h"
51fcb08cdbSMilanka Ringwald 
52*35fd3fb9SMatthias Ringwald #ifdef HAVE_POSIX_FILE_IO
53fbc7c9f2SMilanka Ringwald #include "wav_util.h"
54*35fd3fb9SMatthias Ringwald #endif
55fbc7c9f2SMilanka Ringwald 
56f7c85330SMatthias Ringwald // configure test mode
57f7c85330SMatthias Ringwald #define SCO_DEMO_MODE_SINE		0
58f7c85330SMatthias Ringwald #define SCO_DEMO_MODE_ASCII		1
59f7c85330SMatthias Ringwald #define SCO_DEMO_MODE_COUNTER	2
60f7c85330SMatthias Ringwald 
618b29cfc6SMatthias Ringwald 
62f7c85330SMatthias Ringwald // SCO demo configuration
63fcb08cdbSMilanka Ringwald #define SCO_DEMO_MODE SCO_DEMO_MODE_SINE
64f7c85330SMatthias Ringwald #define SCO_REPORT_PERIOD 100
65f7c85330SMatthias Ringwald 
668b29cfc6SMatthias Ringwald #ifdef HAVE_POSIX_FILE_IO
678b29cfc6SMatthias Ringwald #define SCO_WAV_FILENAME      "sco_input.wav"
68d5e5f834SMatthias Ringwald #define SCO_MSBC_OUT_FILENAME "sco_output.msbc"
69d5e5f834SMatthias Ringwald #define SCO_MSBC_IN_FILENAME  "sco_input.mscb"
70220eb563SMilanka Ringwald 
71202da317SMilanka Ringwald #define SCO_WAV_DURATION_IN_SECONDS 15
728b29cfc6SMatthias Ringwald #endif
738b29cfc6SMatthias Ringwald 
74f7c85330SMatthias Ringwald 
75f7c85330SMatthias Ringwald #if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE)
76f7c85330SMatthias Ringwald #define USE_PORTAUDIO
77f7c85330SMatthias Ringwald #endif
78f7c85330SMatthias Ringwald 
79f7c85330SMatthias Ringwald #ifdef USE_PORTAUDIO
80f7c85330SMatthias Ringwald #include <portaudio.h>
81dbb41bfeSMilanka Ringwald #include "btstack_ring_buffer.h"
82dbb41bfeSMilanka Ringwald 
838b29cfc6SMatthias Ringwald // portaudio config
848b29cfc6SMatthias Ringwald #define NUM_CHANNELS            1
85dbb41bfeSMilanka Ringwald 
86dbb41bfeSMilanka Ringwald #define CVSD_SAMPLE_RATE        8000
87dbb41bfeSMilanka Ringwald #define CVSD_FRAMES_PER_BUFFER  24
88dbb41bfeSMilanka Ringwald #define CVSD_PA_SAMPLE_TYPE     paInt8
89dbb41bfeSMilanka Ringwald #define CVSD_BYTES_PER_FRAME    (1*NUM_CHANNELS)
90dbb41bfeSMilanka Ringwald #define CVSD_PREBUFFER_MS       5
91dbb41bfeSMilanka Ringwald #define CVSD_PREBUFFER_BYTES    (CVSD_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME)
92dbb41bfeSMilanka Ringwald 
93dbb41bfeSMilanka Ringwald #define MSBC_SAMPLE_RATE        16000
94dbb41bfeSMilanka Ringwald #define MSBC_FRAMES_PER_BUFFER  120
95dbb41bfeSMilanka Ringwald #define MSBC_PA_SAMPLE_TYPE     paInt16
96dbb41bfeSMilanka Ringwald #define MSBC_BYTES_PER_FRAME    (2*NUM_CHANNELS)
97dbb41bfeSMilanka Ringwald #define MSBC_PREBUFFER_MS       50
98dbb41bfeSMilanka Ringwald #define MSBC_PREBUFFER_BYTES    (MSBC_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME)
99dbb41bfeSMilanka Ringwald 
100f7c85330SMatthias Ringwald // portaudio globals
101f7c85330SMatthias Ringwald static  PaStream * stream;
102dbb41bfeSMilanka Ringwald static uint8_t pa_stream_started = 0;
103dbb41bfeSMilanka Ringwald 
104dbb41bfeSMilanka Ringwald static uint8_t ring_buffer_storage[2*MSBC_PREBUFFER_BYTES];
105dbb41bfeSMilanka Ringwald static btstack_ring_buffer_t ring_buffer;
106f7c85330SMatthias Ringwald #endif
107f7c85330SMatthias Ringwald 
108fcb08cdbSMilanka Ringwald 
109fcb08cdbSMilanka Ringwald static int dump_data = 1;
110fcb08cdbSMilanka Ringwald static int count_sent = 0;
111fcb08cdbSMilanka Ringwald static int count_received = 0;
112d76591efSMatthias Ringwald static uint8_t negotiated_codec = 0;
113220eb563SMilanka Ringwald static int num_audio_frames = 0;
114fcb08cdbSMilanka Ringwald 
115d5e5f834SMatthias Ringwald FILE * msbc_file_in;
116d5e5f834SMatthias Ringwald FILE * msbc_file_out;
1177294d009SMatthias Ringwald 
118f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
119d6a06398SMatthias Ringwald 
120*35fd3fb9SMatthias Ringwald // input signal: pre-computed sine wave, at 8000 kz
121*35fd3fb9SMatthias Ringwald static const uint8_t sine_uint8[] = {
122*35fd3fb9SMatthias Ringwald       0,  15,  31,  46,  61,  74,  86,  97, 107, 114,
123*35fd3fb9SMatthias Ringwald     120, 124, 126, 126, 124, 120, 114, 107,  97,  86,
124*35fd3fb9SMatthias Ringwald      74,  61,  46,  31,  15,   0, 241, 225, 210, 195,
125*35fd3fb9SMatthias Ringwald     182, 170, 159, 149, 142, 136, 132, 130, 130, 132,
126*35fd3fb9SMatthias Ringwald     136, 142, 149, 159, 170, 182, 195, 210, 225, 241,
127*35fd3fb9SMatthias Ringwald };
128*35fd3fb9SMatthias Ringwald 
129*35fd3fb9SMatthias Ringwald 
130*35fd3fb9SMatthias Ringwald // input signal: pre-computed sine wave, 160 Hz at 16000 kHz
131*35fd3fb9SMatthias Ringwald static const int16_t sine_int16[] = {
132*35fd3fb9SMatthias Ringwald      0,    2057,    4107,    6140,    8149,   10126,   12062,   13952,   15786,   17557,
133*35fd3fb9SMatthias Ringwald  19260,   20886,   22431,   23886,   25247,   26509,   27666,   28714,   29648,   30466,
134*35fd3fb9SMatthias Ringwald  31163,   31738,   32187,   32509,   32702,   32767,   32702,   32509,   32187,   31738,
135*35fd3fb9SMatthias Ringwald  31163,   30466,   29648,   28714,   27666,   26509,   25247,   23886,   22431,   20886,
136*35fd3fb9SMatthias Ringwald  19260,   17557,   15786,   13952,   12062,   10126,    8149,    6140,    4107,    2057,
137*35fd3fb9SMatthias Ringwald      0,   -2057,   -4107,   -6140,   -8149,  -10126,  -12062,  -13952,  -15786,  -17557,
138*35fd3fb9SMatthias Ringwald -19260,  -20886,  -22431,  -23886,  -25247,  -26509,  -27666,  -28714,  -29648,  -30466,
139*35fd3fb9SMatthias Ringwald -31163,  -31738,  -32187,  -32509,  -32702,  -32767,  -32702,  -32509,  -32187,  -31738,
140*35fd3fb9SMatthias Ringwald -31163,  -30466,  -29648,  -28714,  -27666,  -26509,  -25247,  -23886,  -22431,  -20886,
141*35fd3fb9SMatthias Ringwald -19260,  -17557,  -15786,  -13952,  -12062,  -10126,   -8149,   -6140,   -4107,   -2057,
142*35fd3fb9SMatthias Ringwald };
143*35fd3fb9SMatthias Ringwald 
144*35fd3fb9SMatthias Ringwald static int phase = 0;
145*35fd3fb9SMatthias Ringwald static void sco_demo_sine_wave_int8(int num_samples, int8_t * data){
146*35fd3fb9SMatthias Ringwald     int i;
147*35fd3fb9SMatthias Ringwald     for (i=0; i<num_samples; i++){
148*35fd3fb9SMatthias Ringwald         data[i] = (int8_t)sine_uint8[phase];
149*35fd3fb9SMatthias Ringwald         phase++;
150*35fd3fb9SMatthias Ringwald         if (phase >= sizeof(sine_uint8)) phase = 0;
151*35fd3fb9SMatthias Ringwald     }
152*35fd3fb9SMatthias Ringwald }
153*35fd3fb9SMatthias Ringwald 
154*35fd3fb9SMatthias Ringwald static void sco_demo_sine_wave_int16(int num_samples, int16_t * data){
155*35fd3fb9SMatthias Ringwald     int i;
156*35fd3fb9SMatthias Ringwald     for (i=0; i < num_samples; i++){
157*35fd3fb9SMatthias Ringwald         data[i] = sine_int16[phase++];
158*35fd3fb9SMatthias Ringwald         if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){
159*35fd3fb9SMatthias Ringwald             phase = 0;
160*35fd3fb9SMatthias Ringwald         }
161*35fd3fb9SMatthias Ringwald     }
162*35fd3fb9SMatthias Ringwald }
163*35fd3fb9SMatthias Ringwald 
164*35fd3fb9SMatthias Ringwald static void sco_demo_fill_audio_frame(void){
165*35fd3fb9SMatthias Ringwald     if (!hfp_msbc_can_encode_audio_frame_now()) return;
166*35fd3fb9SMatthias Ringwald     int num_samples = hfp_msbc_num_audio_samples_per_frame();
167*35fd3fb9SMatthias Ringwald     int16_t sample_buffer[num_samples];
168*35fd3fb9SMatthias Ringwald     sco_demo_sine_wave_int16(num_samples, sample_buffer);
169*35fd3fb9SMatthias Ringwald     hfp_msbc_encode_audio_frame(sample_buffer);
170*35fd3fb9SMatthias Ringwald     num_audio_frames++;
171*35fd3fb9SMatthias Ringwald }
172f7c85330SMatthias Ringwald 
1738b29cfc6SMatthias Ringwald #ifdef SCO_WAV_FILENAME
1742afeea7fSMilanka Ringwald static btstack_sbc_decoder_state_t decoder_state;
17582e01da0SMilanka Ringwald static btstack_cvsd_plc_state_t cvsd_plc_state;
176dbb41bfeSMilanka Ringwald static int num_samples_to_write;
177dbb41bfeSMilanka Ringwald 
178dbb41bfeSMilanka Ringwald #ifdef USE_PORTAUDIO
179dbb41bfeSMilanka Ringwald static int patestCallback( const void *inputBuffer, void *outputBuffer,
180dbb41bfeSMilanka Ringwald                            unsigned long framesPerBuffer,
181dbb41bfeSMilanka Ringwald                            const PaStreamCallbackTimeInfo* timeInfo,
182dbb41bfeSMilanka Ringwald                            PaStreamCallbackFlags statusFlags,
183dbb41bfeSMilanka Ringwald                            void *userData ) {
184dbb41bfeSMilanka Ringwald     (void) timeInfo; /* Prevent unused variable warnings. */
185dbb41bfeSMilanka Ringwald     (void) statusFlags;
186dbb41bfeSMilanka Ringwald     (void) inputBuffer;
187dbb41bfeSMilanka Ringwald 
188dbb41bfeSMilanka Ringwald     uint32_t bytes_read = 0;
189dbb41bfeSMilanka Ringwald     int bytes_per_buffer = framesPerBuffer;
190dbb41bfeSMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
191dbb41bfeSMilanka Ringwald         bytes_per_buffer *= MSBC_BYTES_PER_FRAME;
192dbb41bfeSMilanka Ringwald     } else {
193dbb41bfeSMilanka Ringwald         bytes_per_buffer *= CVSD_BYTES_PER_FRAME;
194dbb41bfeSMilanka Ringwald     }
195dbb41bfeSMilanka Ringwald 
196dbb41bfeSMilanka Ringwald     if (btstack_ring_buffer_bytes_available(&ring_buffer) >= bytes_per_buffer){
197dbb41bfeSMilanka Ringwald         btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_per_buffer, &bytes_read);
198dbb41bfeSMilanka Ringwald     } else {
199dbb41bfeSMilanka Ringwald         printf("NOT ENOUGH DATA!\n");
200dbb41bfeSMilanka Ringwald         memset(outputBuffer, 0, bytes_per_buffer);
201dbb41bfeSMilanka Ringwald     }
202dbb41bfeSMilanka Ringwald     // printf("bytes avail after read: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer));
203dbb41bfeSMilanka Ringwald     return 0;
204dbb41bfeSMilanka Ringwald }
205dbb41bfeSMilanka Ringwald #endif
2068b29cfc6SMatthias Ringwald 
207fcb08cdbSMilanka Ringwald static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){
208dbb41bfeSMilanka Ringwald     // printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels);
209dbb41bfeSMilanka Ringwald #ifdef USE_PORTAUDIO
210dbb41bfeSMilanka Ringwald     if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= MSBC_PREBUFFER_BYTES){
211dbb41bfeSMilanka Ringwald         /* -- start stream -- */
212dbb41bfeSMilanka Ringwald         PaError err = Pa_StartStream(stream);
213dbb41bfeSMilanka Ringwald         if (err != paNoError){
214dbb41bfeSMilanka Ringwald             printf("Error starting the stream: \"%s\"\n",  Pa_GetErrorText(err));
215dbb41bfeSMilanka Ringwald             return;
216dbb41bfeSMilanka Ringwald         }
217dbb41bfeSMilanka Ringwald         pa_stream_started = 1;
218dbb41bfeSMilanka Ringwald     }
219dbb41bfeSMilanka Ringwald     btstack_ring_buffer_write(&ring_buffer, (uint8_t *)data, num_samples*num_channels*2);
220dbb41bfeSMilanka Ringwald     // printf("bytes avail after write: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer));
221dbb41bfeSMilanka Ringwald #endif
222dbb41bfeSMilanka Ringwald 
223fcb08cdbSMilanka Ringwald     if (!num_samples_to_write) return;
224fcb08cdbSMilanka Ringwald 
225fcb08cdbSMilanka Ringwald     num_samples = btstack_min(num_samples, num_samples_to_write);
226fcb08cdbSMilanka Ringwald     num_samples_to_write -= num_samples;
227fcb08cdbSMilanka Ringwald 
228fbc7c9f2SMilanka Ringwald     wav_writer_write_int16(num_samples, data);
229fcb08cdbSMilanka Ringwald 
230fcb08cdbSMilanka Ringwald     if (num_samples_to_write == 0){
231fcb08cdbSMilanka Ringwald         sco_demo_close();
232fcb08cdbSMilanka Ringwald     }
233fcb08cdbSMilanka Ringwald }
234fcb08cdbSMilanka Ringwald 
235fcb08cdbSMilanka Ringwald static void sco_demo_init_mSBC(void){
236fbc7c9f2SMilanka Ringwald     int sample_rate = 16000;
237fbc7c9f2SMilanka Ringwald     wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate);
238fbc7c9f2SMilanka Ringwald     btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, NULL);
239fcb08cdbSMilanka Ringwald 
240fbc7c9f2SMilanka Ringwald     num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
241220eb563SMilanka Ringwald 
242220eb563SMilanka Ringwald     hfp_msbc_init();
243220eb563SMilanka Ringwald     sco_demo_fill_audio_frame();
244973d7173SMatthias Ringwald 
245d5e5f834SMatthias Ringwald #ifdef SCO_MSBC_IN_FILENAME
246d5e5f834SMatthias Ringwald     msbc_file_in = fopen(SCO_MSBC_IN_FILENAME, "wb");
247d5e5f834SMatthias Ringwald     printf("SCO Demo: creating mSBC in file %s, %p\n", SCO_MSBC_IN_FILENAME, msbc_file_in);
248d5e5f834SMatthias Ringwald #endif
2497294d009SMatthias Ringwald #ifdef SCO_MSBC_OUT_FILENAME
250d5e5f834SMatthias Ringwald     msbc_file_out = fopen(SCO_MSBC_OUT_FILENAME, "wb");
251d5e5f834SMatthias Ringwald     printf("SCO Demo: creating mSBC out file %s, %p\n", SCO_MSBC_OUT_FILENAME, msbc_file_out);
2527294d009SMatthias Ringwald #endif
253dbb41bfeSMilanka Ringwald 
254dbb41bfeSMilanka Ringwald #ifdef USE_PORTAUDIO
255dbb41bfeSMilanka Ringwald     PaError err;
256dbb41bfeSMilanka Ringwald     PaStreamParameters outputParameters;
257dbb41bfeSMilanka Ringwald 
258dbb41bfeSMilanka Ringwald     /* -- initialize PortAudio -- */
259dbb41bfeSMilanka Ringwald     err = Pa_Initialize();
260dbb41bfeSMilanka Ringwald     if( err != paNoError ) return;
261dbb41bfeSMilanka Ringwald     /* -- setup input and output -- */
262dbb41bfeSMilanka Ringwald     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
263dbb41bfeSMilanka Ringwald     outputParameters.channelCount = NUM_CHANNELS;
264dbb41bfeSMilanka Ringwald     outputParameters.sampleFormat = MSBC_PA_SAMPLE_TYPE;
265dbb41bfeSMilanka Ringwald     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
266dbb41bfeSMilanka Ringwald     outputParameters.hostApiSpecificStreamInfo = NULL;
267dbb41bfeSMilanka Ringwald     /* -- setup stream -- */
268dbb41bfeSMilanka Ringwald     err = Pa_OpenStream(
269dbb41bfeSMilanka Ringwald            &stream,
270dbb41bfeSMilanka Ringwald            NULL, // &inputParameters,
271dbb41bfeSMilanka Ringwald            &outputParameters,
272dbb41bfeSMilanka Ringwald            MSBC_SAMPLE_RATE,
273dbb41bfeSMilanka Ringwald            MSBC_FRAMES_PER_BUFFER,
274dbb41bfeSMilanka Ringwald            paClipOff, /* we won't output out of range samples so don't bother clipping them */
275dbb41bfeSMilanka Ringwald            patestCallback,      /* no callback, use blocking API */
276dbb41bfeSMilanka Ringwald            NULL );    /* no callback, so no callback userData */
277dbb41bfeSMilanka Ringwald     if (err != paNoError){
278dbb41bfeSMilanka Ringwald         printf("Error initializing portaudio: \"%s\"\n",  Pa_GetErrorText(err));
279dbb41bfeSMilanka Ringwald         return;
280dbb41bfeSMilanka Ringwald     }
281dbb41bfeSMilanka Ringwald     memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage));
282dbb41bfeSMilanka Ringwald     btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));
283dbb41bfeSMilanka Ringwald     pa_stream_started = 0;
284dbb41bfeSMilanka Ringwald #endif
285fcb08cdbSMilanka Ringwald }
286fcb08cdbSMilanka Ringwald 
287fcb08cdbSMilanka Ringwald static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){
288fcb08cdbSMilanka Ringwald     if (num_samples_to_write){
289d5e5f834SMatthias Ringwald         if (msbc_file_in){
290d5e5f834SMatthias Ringwald             // log incoming mSBC data for testing
291d5e5f834SMatthias Ringwald             fwrite(packet+3, size-3, 1, msbc_file_in);
292d5e5f834SMatthias Ringwald         }
293fcb08cdbSMilanka Ringwald     }
294dbb41bfeSMilanka Ringwald     btstack_sbc_decoder_process_data(&decoder_state, (packet[1] >> 4) & 3, packet+3, size-3);
295fcb08cdbSMilanka Ringwald }
296fcb08cdbSMilanka Ringwald 
297fbc7c9f2SMilanka Ringwald static void sco_demo_init_CVSD(void){
298fbc7c9f2SMilanka Ringwald     int sample_rate = 8000;
299fbc7c9f2SMilanka Ringwald     wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate);
300fbc7c9f2SMilanka Ringwald     btstack_cvsd_plc_init(&cvsd_plc_state);
301fbc7c9f2SMilanka Ringwald     num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS;
302dbb41bfeSMilanka Ringwald 
303dbb41bfeSMilanka Ringwald #ifdef USE_PORTAUDIO
304dbb41bfeSMilanka Ringwald     PaError err;
305dbb41bfeSMilanka Ringwald     PaStreamParameters outputParameters;
306dbb41bfeSMilanka Ringwald 
307dbb41bfeSMilanka Ringwald     /* -- initialize PortAudio -- */
308dbb41bfeSMilanka Ringwald     err = Pa_Initialize();
309dbb41bfeSMilanka Ringwald     if( err != paNoError ) return;
310dbb41bfeSMilanka Ringwald     /* -- setup input and output -- */
311dbb41bfeSMilanka Ringwald     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
312dbb41bfeSMilanka Ringwald     outputParameters.channelCount = NUM_CHANNELS;
313dbb41bfeSMilanka Ringwald     outputParameters.sampleFormat = CVSD_PA_SAMPLE_TYPE;
314dbb41bfeSMilanka Ringwald     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
315dbb41bfeSMilanka Ringwald     outputParameters.hostApiSpecificStreamInfo = NULL;
316dbb41bfeSMilanka Ringwald     /* -- setup stream -- */
317dbb41bfeSMilanka Ringwald     err = Pa_OpenStream(
318dbb41bfeSMilanka Ringwald            &stream,
319dbb41bfeSMilanka Ringwald            NULL, // &inputParameters,
320dbb41bfeSMilanka Ringwald            &outputParameters,
321dbb41bfeSMilanka Ringwald            CVSD_SAMPLE_RATE,
322dbb41bfeSMilanka Ringwald            CVSD_FRAMES_PER_BUFFER,
323dbb41bfeSMilanka Ringwald            paClipOff, /* we won't output out of range samples so don't bother clipping them */
324dbb41bfeSMilanka Ringwald            patestCallback,      /* no callback, use blocking API */
325dbb41bfeSMilanka Ringwald            NULL );    /* no callback, so no callback userData */
326dbb41bfeSMilanka Ringwald     if (err != paNoError){
327dbb41bfeSMilanka Ringwald         printf("Error initializing portaudio: \"%s\"\n",  Pa_GetErrorText(err));
328dbb41bfeSMilanka Ringwald         return;
329dbb41bfeSMilanka Ringwald     }
330dbb41bfeSMilanka Ringwald     memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage));
331dbb41bfeSMilanka Ringwald     btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));
332dbb41bfeSMilanka Ringwald     pa_stream_started = 0;
333dbb41bfeSMilanka Ringwald #endif
334fbc7c9f2SMilanka Ringwald }
335fbc7c9f2SMilanka Ringwald 
336fcb08cdbSMilanka Ringwald static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){
337dbb41bfeSMilanka Ringwald     if (!num_samples_to_write) return;
338dbb41bfeSMilanka Ringwald 
339fcb08cdbSMilanka Ringwald     const int num_samples = size - 3;
340fcb08cdbSMilanka Ringwald     const int samples_to_write = btstack_min(num_samples, num_samples_to_write);
34182e01da0SMilanka Ringwald     int8_t audio_frame_out[24];
342379d044eSMilanka Ringwald 
343dbb41bfeSMilanka Ringwald 
344fbc7c9f2SMilanka Ringwald     // memcpy(audio_frame_out, (int8_t*)(packet+3), 24);
345fbc7c9f2SMilanka Ringwald     btstack_cvsd_plc_process_data(&cvsd_plc_state, (int8_t *)(packet+3), num_samples, audio_frame_out);
346379d044eSMilanka Ringwald 
347fbc7c9f2SMilanka Ringwald     wav_writer_write_int8(samples_to_write, audio_frame_out);
348fcb08cdbSMilanka Ringwald     num_samples_to_write -= samples_to_write;
349fcb08cdbSMilanka Ringwald     if (num_samples_to_write == 0){
350fcb08cdbSMilanka Ringwald         sco_demo_close();
351fcb08cdbSMilanka Ringwald     }
352dbb41bfeSMilanka Ringwald #ifdef USE_PORTAUDIO
353dbb41bfeSMilanka Ringwald     if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= CVSD_PREBUFFER_BYTES){
354dbb41bfeSMilanka Ringwald         /* -- start stream -- */
355dbb41bfeSMilanka Ringwald         PaError err = Pa_StartStream(stream);
356dbb41bfeSMilanka Ringwald         if (err != paNoError){
357dbb41bfeSMilanka Ringwald             printf("Error starting the stream: \"%s\"\n",  Pa_GetErrorText(err));
358dbb41bfeSMilanka Ringwald             return;
359fcb08cdbSMilanka Ringwald         }
360dbb41bfeSMilanka Ringwald         pa_stream_started = 1;
361dbb41bfeSMilanka Ringwald     }
362dbb41bfeSMilanka Ringwald     btstack_ring_buffer_write(&ring_buffer, (uint8_t *)audio_frame_out, samples_to_write);
363dbb41bfeSMilanka Ringwald #endif
364fcb08cdbSMilanka Ringwald }
365fcb08cdbSMilanka Ringwald 
3668b29cfc6SMatthias Ringwald #endif
3674a96141eSMatthias Ringwald #endif
3688b29cfc6SMatthias Ringwald 
369fcb08cdbSMilanka Ringwald void sco_demo_close(void){
370fcb08cdbSMilanka Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
37126463303SMilanka Ringwald #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
372fbc7c9f2SMilanka Ringwald     wav_writer_close();
37326463303SMilanka Ringwald     printf("SCO demo statistics: ");
37426463303SMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
37526463303SMilanka 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);
37626463303SMilanka Ringwald     } else {
37726463303SMilanka 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);
37826463303SMilanka Ringwald     }
37926463303SMilanka Ringwald #endif
38026463303SMilanka Ringwald #endif
38126463303SMilanka Ringwald 
382dbb41bfeSMilanka Ringwald #ifdef HAVE_PORTAUDIO
383dbb41bfeSMilanka Ringwald     if (pa_stream_started){
384dbb41bfeSMilanka Ringwald         PaError err = Pa_StopStream(stream);
385dbb41bfeSMilanka Ringwald         if (err != paNoError){
386dbb41bfeSMilanka Ringwald             printf("Error stopping the stream: \"%s\"\n",  Pa_GetErrorText(err));
387dbb41bfeSMilanka Ringwald             return;
388dbb41bfeSMilanka Ringwald         }
389dbb41bfeSMilanka Ringwald         pa_stream_started = 0;
390dbb41bfeSMilanka Ringwald         err = Pa_CloseStream(stream);
391dbb41bfeSMilanka Ringwald         if (err != paNoError){
392dbb41bfeSMilanka Ringwald             printf("Error closing the stream: \"%s\"\n",  Pa_GetErrorText(err));
393dbb41bfeSMilanka Ringwald             return;
394dbb41bfeSMilanka Ringwald         }
395dbb41bfeSMilanka Ringwald 
396dbb41bfeSMilanka Ringwald         err = Pa_Terminate();
397dbb41bfeSMilanka Ringwald         if (err != paNoError){
398dbb41bfeSMilanka Ringwald             printf("Error terminating portaudio: \"%s\"\n",  Pa_GetErrorText(err));
399dbb41bfeSMilanka Ringwald             return;
400dbb41bfeSMilanka Ringwald         }
401dbb41bfeSMilanka Ringwald     }
402dbb41bfeSMilanka Ringwald #endif
403dbb41bfeSMilanka Ringwald 
40426463303SMilanka Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
405fcb08cdbSMilanka Ringwald #ifdef SCO_WAV_FILENAME
406fcb08cdbSMilanka Ringwald 
407613518d1SMilanka Ringwald #if 0
408fcb08cdbSMilanka Ringwald     printf("SCO Demo: closing wav file\n");
409220eb563SMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
4106e046a36SMatthias Ringwald         wav_writer_state_t * writer_state = &wav_writer_state;
411fcb08cdbSMilanka Ringwald         if (!writer_state->wav_file) return;
412fcb08cdbSMilanka Ringwald         rewind(writer_state->wav_file);
4132afeea7fSMilanka 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);
414fcb08cdbSMilanka Ringwald         fclose(writer_state->wav_file);
415fcb08cdbSMilanka Ringwald         writer_state->wav_file = NULL;
416fcb08cdbSMilanka Ringwald     }
417613518d1SMilanka Ringwald #endif
418fcb08cdbSMilanka Ringwald #endif
419fcb08cdbSMilanka Ringwald #endif
420fcb08cdbSMilanka Ringwald }
421fcb08cdbSMilanka Ringwald 
422fcb08cdbSMilanka Ringwald void sco_demo_set_codec(uint8_t codec){
423fcb08cdbSMilanka Ringwald     if (negotiated_codec == codec) return;
424fcb08cdbSMilanka Ringwald     negotiated_codec = codec;
425fcb08cdbSMilanka Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
42617cd946eSMatthias Ringwald #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
427220eb563SMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
428fcb08cdbSMilanka Ringwald         sco_demo_init_mSBC();
429fcb08cdbSMilanka Ringwald     } else {
430fcb08cdbSMilanka Ringwald         sco_demo_init_CVSD();
431fcb08cdbSMilanka Ringwald     }
432fcb08cdbSMilanka Ringwald #endif
433fcb08cdbSMilanka Ringwald #endif
434fcb08cdbSMilanka Ringwald }
435fcb08cdbSMilanka Ringwald 
436f7c85330SMatthias Ringwald void sco_demo_init(void){
437f7c85330SMatthias Ringwald 
438f7c85330SMatthias Ringwald 	// status
439f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
440f7c85330SMatthias Ringwald #ifdef HAVE_PORTAUDIO
441f7c85330SMatthias Ringwald 	printf("SCO Demo: Sending sine wave, audio output via portaudio.\n");
442f7c85330SMatthias Ringwald #else
443f7c85330SMatthias Ringwald 	printf("SCO Demo: Sending sine wave, hexdump received data.\n");
444f7c85330SMatthias Ringwald #endif
445f7c85330SMatthias Ringwald #endif
446f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
447f7c85330SMatthias Ringwald 	printf("SCO Demo: Sending ASCII blocks, print received data.\n");
448f7c85330SMatthias Ringwald #endif
449f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
450f7c85330SMatthias Ringwald 	printf("SCO Demo: Sending counter value, hexdump received data.\n");
451f7c85330SMatthias Ringwald #endif
452f7c85330SMatthias Ringwald 
4537294d009SMatthias Ringwald #if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE
454f7c85330SMatthias Ringwald     hci_set_sco_voice_setting(0x03);    // linear, unsigned, 8-bit, transparent
4557294d009SMatthias Ringwald #endif
456f7c85330SMatthias Ringwald 
457f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
458f7c85330SMatthias Ringwald     phase = 'a';
459f7c85330SMatthias Ringwald #endif
460f7c85330SMatthias Ringwald }
461f7c85330SMatthias Ringwald 
4624a96141eSMatthias Ringwald static void sco_report(void){
4634a96141eSMatthias Ringwald     printf("SCO: sent %u, received %u\n", count_sent, count_received);
4644a96141eSMatthias Ringwald }
465f7c85330SMatthias Ringwald 
466f7c85330SMatthias Ringwald void sco_demo_send(hci_con_handle_t sco_handle){
467f7c85330SMatthias Ringwald 
468f7c85330SMatthias Ringwald     if (!sco_handle) return;
469f7c85330SMatthias Ringwald 
470f7c85330SMatthias Ringwald     const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length();
471f7c85330SMatthias Ringwald     const int sco_payload_length = sco_packet_length - 3;
472f7c85330SMatthias Ringwald 
473f7c85330SMatthias Ringwald     hci_reserve_packet_buffer();
474f7c85330SMatthias Ringwald     uint8_t * sco_packet = hci_get_outgoing_packet_buffer();
475f7c85330SMatthias Ringwald     // set handle + flags
476f7c85330SMatthias Ringwald     little_endian_store_16(sco_packet, 0, sco_handle);
477f7c85330SMatthias Ringwald     // set len
478f7c85330SMatthias Ringwald     sco_packet[2] = sco_payload_length;
479220eb563SMilanka Ringwald     const int audio_samples_per_packet = sco_payload_length;    // for 8-bit data. for 16-bit data it's /2
480f7c85330SMatthias Ringwald 
481f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
482220eb563SMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
483220eb563SMilanka Ringwald 
484220eb563SMilanka Ringwald         if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){
485220eb563SMilanka Ringwald             log_error("mSBC stream is empty.");
486220eb563SMilanka Ringwald         }
487220eb563SMilanka Ringwald         hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length);
488d5e5f834SMatthias Ringwald         if (msbc_file_out){
489d76591efSMatthias Ringwald             // log outgoing mSBC data for testing
490d5e5f834SMatthias Ringwald             fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out);
491d76591efSMatthias Ringwald         }
4927294d009SMatthias Ringwald 
493220eb563SMilanka Ringwald         sco_demo_fill_audio_frame();
494220eb563SMilanka Ringwald     } else {
495*35fd3fb9SMatthias Ringwald         sco_demo_sine_wave_int8(audio_samples_per_packet, (int8_t *) (sco_packet+3));
496220eb563SMilanka Ringwald     }
497f7c85330SMatthias Ringwald #else
498f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
499220eb563SMilanka Ringwald     memset(&sco_packet[3], phase++, audio_samples_per_packet);
500f7c85330SMatthias Ringwald     if (phase > 'z') phase = 'a';
501f7c85330SMatthias Ringwald #else
50238b2eaafSMatthias Ringwald     int j;
503220eb563SMilanka Ringwald     for (j=0;j<audio_samples_per_packet;j++){
50438b2eaafSMatthias Ringwald         sco_packet[3+j] = phase++;
505f7c85330SMatthias Ringwald     }
506f7c85330SMatthias Ringwald #endif
507f7c85330SMatthias Ringwald #endif
508220eb563SMilanka Ringwald 
509f7c85330SMatthias Ringwald     hci_send_sco_packet_buffer(sco_packet_length);
510f7c85330SMatthias Ringwald 
511f7c85330SMatthias Ringwald     // request another send event
512f7c85330SMatthias Ringwald     hci_request_sco_can_send_now_event();
513f7c85330SMatthias Ringwald 
5144a96141eSMatthias Ringwald     count_sent++;
5154a96141eSMatthias Ringwald     if ((count_sent % SCO_REPORT_PERIOD) == 0) sco_report();
516f7c85330SMatthias Ringwald }
517f7c85330SMatthias Ringwald 
518f7c85330SMatthias Ringwald /**
519f7c85330SMatthias Ringwald  * @brief Process received data
520f7c85330SMatthias Ringwald  */
521f7c85330SMatthias Ringwald void sco_demo_receive(uint8_t * packet, uint16_t size){
522f7c85330SMatthias Ringwald 
523fcb08cdbSMilanka Ringwald     dump_data = 1;
5248b29cfc6SMatthias Ringwald 
5254a96141eSMatthias Ringwald     count_received++;
5264a96141eSMatthias Ringwald     // if ((count_received % SCO_REPORT_PERIOD) == 0) sco_report();
5274a96141eSMatthias Ringwald 
5284a96141eSMatthias Ringwald 
5294a96141eSMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
5308b29cfc6SMatthias Ringwald #ifdef SCO_WAV_FILENAME
531220eb563SMilanka Ringwald     if (negotiated_codec == HFP_CODEC_MSBC){
532fcb08cdbSMilanka Ringwald         sco_demo_receive_mSBC(packet, size);
533fcb08cdbSMilanka Ringwald     } else {
534fcb08cdbSMilanka Ringwald         sco_demo_receive_CVSD(packet, size);
5358b29cfc6SMatthias Ringwald     }
536dbb41bfeSMilanka Ringwald     dump_data = 0;
5378b29cfc6SMatthias Ringwald #endif
5384a96141eSMatthias Ringwald #endif
5398b29cfc6SMatthias Ringwald 
540b3f76298SMilanka Ringwald     if (packet[1] & 0x30){
541b3f76298SMilanka Ringwald         printf("SCO CRC Error: %x - data: ", (packet[1] & 0x30) >> 4);
542b3f76298SMilanka Ringwald         log_info("SCO CRC Error: %x - data: ", (packet[1] & 0x30) >> 4);
543f7c85330SMatthias Ringwald         printf_hexdump(&packet[3], size-3);
544b3f76298SMilanka Ringwald 
545f7c85330SMatthias Ringwald         return;
546f7c85330SMatthias Ringwald     }
5478b29cfc6SMatthias Ringwald     if (dump_data){
548f7c85330SMatthias Ringwald         printf("data: ");
549f7c85330SMatthias Ringwald #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
550f7c85330SMatthias Ringwald         int i;
551f7c85330SMatthias Ringwald         for (i=3;i<size;i++){
552f7c85330SMatthias Ringwald             printf("%c", packet[i]);
553f7c85330SMatthias Ringwald         }
554f7c85330SMatthias Ringwald         printf("\n");
5558b29cfc6SMatthias Ringwald         dump_data = 0;
5568b29cfc6SMatthias Ringwald #else
557f7c85330SMatthias Ringwald         printf_hexdump(&packet[3], size-3);
558f7c85330SMatthias Ringwald #endif
5598b29cfc6SMatthias Ringwald     }
560f7c85330SMatthias Ringwald }
561