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