xref: /btstack/example/sco_demo_util.c (revision 2308e10812a99217dbf6b81db828f24e082ffed8)
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"
4735fd3fb9SMatthias Ringwald #include "classic/btstack_sbc.h"
4835fd3fb9SMatthias Ringwald #include "classic/btstack_cvsd_plc.h"
4935fd3fb9SMatthias Ringwald #include "classic/hfp_msbc.h"
5035fd3fb9SMatthias Ringwald #include "classic/hfp.h"
51fcb08cdbSMilanka Ringwald 
5235fd3fb9SMatthias Ringwald #ifdef HAVE_POSIX_FILE_IO
53fbc7c9f2SMilanka Ringwald #include "wav_util.h"
5435fd3fb9SMatthias 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"
69*2308e108SMilanka Ringwald #define SCO_MSBC_IN_FILENAME  "sco_input.msbc"
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 
12035fd3fb9SMatthias Ringwald // input signal: pre-computed sine wave, at 8000 kz
12135fd3fb9SMatthias Ringwald static const uint8_t sine_uint8[] = {
12235fd3fb9SMatthias Ringwald       0,  15,  31,  46,  61,  74,  86,  97, 107, 114,
12335fd3fb9SMatthias Ringwald     120, 124, 126, 126, 124, 120, 114, 107,  97,  86,
12435fd3fb9SMatthias Ringwald      74,  61,  46,  31,  15,   0, 241, 225, 210, 195,
12535fd3fb9SMatthias Ringwald     182, 170, 159, 149, 142, 136, 132, 130, 130, 132,
12635fd3fb9SMatthias Ringwald     136, 142, 149, 159, 170, 182, 195, 210, 225, 241,
12735fd3fb9SMatthias Ringwald };
12835fd3fb9SMatthias Ringwald 
12935fd3fb9SMatthias Ringwald 
13035fd3fb9SMatthias Ringwald // input signal: pre-computed sine wave, 160 Hz at 16000 kHz
13135fd3fb9SMatthias Ringwald static const int16_t sine_int16[] = {
13235fd3fb9SMatthias Ringwald      0,    2057,    4107,    6140,    8149,   10126,   12062,   13952,   15786,   17557,
13335fd3fb9SMatthias Ringwald  19260,   20886,   22431,   23886,   25247,   26509,   27666,   28714,   29648,   30466,
13435fd3fb9SMatthias Ringwald  31163,   31738,   32187,   32509,   32702,   32767,   32702,   32509,   32187,   31738,
13535fd3fb9SMatthias Ringwald  31163,   30466,   29648,   28714,   27666,   26509,   25247,   23886,   22431,   20886,
13635fd3fb9SMatthias Ringwald  19260,   17557,   15786,   13952,   12062,   10126,    8149,    6140,    4107,    2057,
13735fd3fb9SMatthias Ringwald      0,   -2057,   -4107,   -6140,   -8149,  -10126,  -12062,  -13952,  -15786,  -17557,
13835fd3fb9SMatthias Ringwald -19260,  -20886,  -22431,  -23886,  -25247,  -26509,  -27666,  -28714,  -29648,  -30466,
13935fd3fb9SMatthias Ringwald -31163,  -31738,  -32187,  -32509,  -32702,  -32767,  -32702,  -32509,  -32187,  -31738,
14035fd3fb9SMatthias Ringwald -31163,  -30466,  -29648,  -28714,  -27666,  -26509,  -25247,  -23886,  -22431,  -20886,
14135fd3fb9SMatthias Ringwald -19260,  -17557,  -15786,  -13952,  -12062,  -10126,   -8149,   -6140,   -4107,   -2057,
14235fd3fb9SMatthias Ringwald };
14335fd3fb9SMatthias Ringwald 
14435fd3fb9SMatthias Ringwald static int phase = 0;
14535fd3fb9SMatthias Ringwald static void sco_demo_sine_wave_int8(int num_samples, int8_t * data){
14635fd3fb9SMatthias Ringwald     int i;
14735fd3fb9SMatthias Ringwald     for (i=0; i<num_samples; i++){
14835fd3fb9SMatthias Ringwald         data[i] = (int8_t)sine_uint8[phase];
14935fd3fb9SMatthias Ringwald         phase++;
15035fd3fb9SMatthias Ringwald         if (phase >= sizeof(sine_uint8)) phase = 0;
15135fd3fb9SMatthias Ringwald     }
15235fd3fb9SMatthias Ringwald }
15335fd3fb9SMatthias Ringwald 
15435fd3fb9SMatthias Ringwald static void sco_demo_sine_wave_int16(int num_samples, int16_t * data){
15535fd3fb9SMatthias Ringwald     int i;
15635fd3fb9SMatthias Ringwald     for (i=0; i < num_samples; i++){
15735fd3fb9SMatthias Ringwald         data[i] = sine_int16[phase++];
15835fd3fb9SMatthias Ringwald         if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){
15935fd3fb9SMatthias Ringwald             phase = 0;
16035fd3fb9SMatthias Ringwald         }
16135fd3fb9SMatthias Ringwald     }
16235fd3fb9SMatthias Ringwald }
16335fd3fb9SMatthias Ringwald 
16435fd3fb9SMatthias Ringwald static void sco_demo_fill_audio_frame(void){
16535fd3fb9SMatthias Ringwald     if (!hfp_msbc_can_encode_audio_frame_now()) return;
16635fd3fb9SMatthias Ringwald     int num_samples = hfp_msbc_num_audio_samples_per_frame();
16735fd3fb9SMatthias Ringwald     int16_t sample_buffer[num_samples];
16835fd3fb9SMatthias Ringwald     sco_demo_sine_wave_int16(num_samples, sample_buffer);
16935fd3fb9SMatthias Ringwald     hfp_msbc_encode_audio_frame(sample_buffer);
17035fd3fb9SMatthias Ringwald     num_audio_frames++;
17135fd3fb9SMatthias 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 {
49535fd3fb9SMatthias 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