diff --git a/chipset/README.md b/chipset/README.md index a23c4a323..010f5ad96 100644 --- a/chipset/README.md +++ b/chipset/README.md @@ -33,8 +33,8 @@ CSR, which has been acquired by Qualcomm, provides all relevant information on t Chipset | Type | HCI Transport | BD_ADDR (1) | SCO over HCI (2) | LE DLE | Multiple LE Roles | BTstack folder | Comment -------------------- |-----------| ---------------|--------------|------------------|--------|----------------------|----------------|--------- -Broadcom UART | Dual mode | H4, H5 | rarely | No (didn't work) | No | Maybe (3) | bcm | Max UART baudrate 3 mbps -Broadcom USB Dongles | Dual mode | USB | Yes | No (didn't work) | No | No | bcm | +Broadcom UART | Dual mode | H4, H5 | rarely | Maybe | No | Maybe (3) | bcm | Max UART baudrate 3 mbps +Broadcom USB Dongles | Dual mode | USB | Yes | Yes | No | No | bcm | CSR UART | Dual mode | H4, H5 | rarely | No (didn't work) | No | No | csr | CSR USB Dongles | Dual mode | USB | Mostly | Yes | No | No | csr | Dialog DA14581 | LE | H4, SPI | ? | n.a. | No | No | | Waiting for dev kit diff --git a/example/hfp_ag_demo.c b/example/hfp_ag_demo.c index e0d690a1b..328448dda 100644 --- a/example/hfp_ag_demo.c +++ b/example/hfp_ag_demo.c @@ -622,17 +622,18 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * even } break; case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED: - printf("\n** Audio connection released **\n"); + printf("Audio connection released\n"); sco_handle = 0; + sco_demo_close(); break; case HFP_SUBEVENT_START_RINGINIG: - printf("\n** Start Ringing **\n"); + printf("Start Ringing\n"); break; case HFP_SUBEVENT_STOP_RINGINIG: - printf("\n** Stop Ringing **\n"); + printf("Stop Ringing\n"); break; case HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER: - printf("\n** Outgoing call '%s' **\n", hfp_subevent_place_call_with_number_get_number(event)); + printf("Outgoing call '%s'\n", hfp_subevent_place_call_with_number_get_number(event)); // validate number if ( strcmp("1234567", hfp_subevent_place_call_with_number_get_number(event)) == 0 || strcmp("7654321", hfp_subevent_place_call_with_number_get_number(event)) == 0 @@ -646,11 +647,11 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * even break; case HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG: - printf("\n** Attach number to voice tag. Sending '1234567\n"); + printf("Attach number to voice tag. Sending '1234567\n"); hfp_ag_send_phone_number_for_voice_tag(acl_handle, "1234567"); break; case HFP_SUBEVENT_TRANSMIT_DTMF_CODES: - printf("\n** Send DTMF Codes: '%s'\n", hfp_subevent_transmit_dtmf_codes_get_dtmf(event)); + printf("Send DTMF Codes: '%s'\n", hfp_subevent_transmit_dtmf_codes_get_dtmf(event)); hfp_ag_send_dtmf_code_done(acl_handle); break; case HFP_SUBEVENT_CALL_ANSWERED: diff --git a/example/sco_demo_util.c b/example/sco_demo_util.c index 31f45ecf2..59f5db21e 100644 --- a/example/sco_demo_util.c +++ b/example/sco_demo_util.c @@ -39,7 +39,6 @@ * sco_demo_util.c - send/receive test data via SCO, used by hfp_*_demo and hsp_*_demo */ - #include #include "sco_demo_util.h" @@ -53,86 +52,93 @@ #include "wav_util.h" #endif -// configure test mode -#define SCO_DEMO_MODE_SINE 0 -#define SCO_DEMO_MODE_ASCII 1 -#define SCO_DEMO_MODE_COUNTER 2 -#define SCO_DEMO_MODE_55 3 -#define SCO_DEMO_MODE_00 4 - - -// SCO demo configuration -#define SCO_DEMO_MODE SCO_DEMO_MODE_SINE -#define SCO_REPORT_PERIOD 100 - -#ifdef HAVE_POSIX_FILE_IO -#define SCO_WAV_FILENAME "sco_input.wav" -#define SCO_MSBC_OUT_FILENAME "sco_output.msbc" -#define SCO_MSBC_IN_FILENAME "sco_input.msbc" - -#define SCO_WAV_DURATION_IN_SECONDS 15 -#endif - - -#if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) -#define USE_PORTAUDIO -#endif - - -#ifdef USE_PORTAUDIO +#ifdef HAVE_PORTAUDIO #include #include "btstack_ring_buffer.h" +#endif -// portaudio config + +// test modes +#define SCO_DEMO_MODE_SINE 0 +#define SCO_DEMO_MODE_ASCII 1 +#define SCO_DEMO_MODE_COUNTER 2 +#define SCO_DEMO_MODE_55 3 +#define SCO_DEMO_MODE_00 4 +#define SCO_DEMO_MODE_MICROPHONE 5 + +// SCO demo configuration +#define SCO_DEMO_MODE SCO_DEMO_MODE_SINE + +// number of sco packets until 'report' on console +#define SCO_REPORT_PERIOD 100 + +// length and name of wav file on disc +#define SCO_WAV_DURATION_IN_SECONDS 15 +#define SCO_WAV_FILENAME "sco_input.wav" + +// name of sbc test files +#define SCO_MSBC_OUT_FILENAME "sco_output.msbc" +#define SCO_MSBC_IN_FILENAME "sco_input.msbc" + +// pre-buffer for CVSD and mSBC - also defines latency +#define SCO_CVSD_PA_PREBUFFER_MS 50 +#define SCO_MSBC_PA_PREBUFFER_MS 50 + +// constants #define NUM_CHANNELS 1 - +#define CVSD_BYTES_PER_FRAME (2*NUM_CHANNELS) #define CVSD_SAMPLE_RATE 8000 -#define CVSD_FRAMES_PER_BUFFER 24 -#define CVSD_PA_SAMPLE_TYPE paInt8 -#define CVSD_BYTES_PER_FRAME (1*NUM_CHANNELS) -#define CVSD_PREBUFFER_MS 5 -#define CVSD_PREBUFFER_BYTES (CVSD_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME) - #define MSBC_SAMPLE_RATE 16000 -#define MSBC_FRAMES_PER_BUFFER 120 -#define MSBC_PA_SAMPLE_TYPE paInt16 #define MSBC_BYTES_PER_FRAME (2*NUM_CHANNELS) -#define MSBC_PREBUFFER_MS 50 -#define MSBC_PREBUFFER_BYTES (MSBC_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME) -// portaudio globals -static PaStream * stream; -static uint8_t pa_stream_started = 0; +#if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE || SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE) +#define USE_PORTAUDIO +#define CVSD_PA_PREBUFFER_BYTES (SCO_CVSD_PA_PREBUFFER_MS * CVSD_SAMPLE_RATE/1000 * CVSD_BYTES_PER_FRAME) +#define MSBC_PA_PREBUFFER_BYTES (SCO_MSBC_PA_PREBUFFER_MS * MSBC_SAMPLE_RATE/1000 * MSBC_BYTES_PER_FRAME) +#endif + +#ifdef USE_PORTAUDIO + +// bidirectional audio stream +static PaStream * pa_stream; + +// output +static int pa_output_started = 0; +static int pa_output_paused = 0; +static uint8_t pa_output_ring_buffer_storage[2*MSBC_PA_PREBUFFER_BYTES]; +static btstack_ring_buffer_t pa_output_ring_buffer; + +// input +#if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE +#define USE_PORTAUDIO_INPUT +static int pa_input_started = 0; +static int pa_input_paused = 0; +static uint8_t pa_input_ring_buffer_storage[2*8000]; // full second input buffer +static btstack_ring_buffer_t pa_input_ring_buffer; +static int pa_input_counter; +#endif -static uint8_t ring_buffer_storage[2*MSBC_PREBUFFER_BYTES]; -static btstack_ring_buffer_t ring_buffer; #endif static int dump_data = 1; static int count_sent = 0; static int count_received = 0; -static uint8_t negotiated_codec = 0; -#if SCO_DEMO_MODE != SCO_DEMO_MODE_55 -static int phase = 0; -#endif +static int negotiated_codec = -1; + +btstack_sbc_decoder_state_t decoder_state; +btstack_cvsd_plc_state_t cvsd_plc_state; FILE * msbc_file_in; FILE * msbc_file_out; +int num_samples_to_write; +int num_audio_frames; +int phase; + #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE -// input signal: pre-computed sine wave, at 8000 kz -static const uint8_t sine_uint8[] = { - 0, 15, 31, 46, 61, 74, 86, 97, 107, 114, - 120, 124, 126, 126, 124, 120, 114, 107, 97, 86, - 74, 61, 46, 31, 15, 0, 241, 225, 210, 195, - 182, 170, 159, 149, 142, 136, 132, 130, 130, 132, - 136, 142, 149, 159, 170, 182, 195, 210, 225, 241, -}; - - // input signal: pre-computed sine wave, 160 Hz at 16000 kHz -static const int16_t sine_int16[] = { +static const int16_t sine_int16_at_16000hz[] = { 0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557, 19260, 20886, 22431, 23886, 25247, 26509, 27666, 28714, 29648, 30466, 31163, 31738, 32187, 32509, 32702, 32767, 32702, 32509, 32187, 31738, @@ -145,41 +151,30 @@ static const int16_t sine_int16[] = { -19260, -17557, -15786, -13952, -12062, -10126, -8149, -6140, -4107, -2057, }; -static void sco_demo_sine_wave_int8(int num_samples, int8_t * data){ - int i; - for (i=0; i= sizeof(sine_uint8)) phase = 0; - } -} - -static void sco_demo_sine_wave_int16(int num_samples, int16_t * data){ +// ony use every second value from 16khz table +static void sco_demo_sine_wave_int16_at_8000_hz(int num_samples, int16_t * data){ int i; for (i=0; i < num_samples; i++){ - data[i] = sine_int16[phase++]; - if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){ + data[i] = sine_int16_at_16000hz[phase++]; + phase++; + if (phase >= (sizeof(sine_int16_at_16000hz) / sizeof(int16_t))){ phase = 0; } } } -static int num_audio_frames = 0; -static void sco_demo_fill_audio_frame(void){ +static void sco_demo_msbc_fill_sine_audio_frame(void){ if (!hfp_msbc_can_encode_audio_frame_now()) return; int num_samples = hfp_msbc_num_audio_samples_per_frame(); int16_t sample_buffer[num_samples]; - sco_demo_sine_wave_int16(num_samples, sample_buffer); + sco_demo_sine_wave_int16_at_8000_hz(num_samples, sample_buffer); hfp_msbc_encode_audio_frame(sample_buffer); num_audio_frames++; } -#ifdef SCO_WAV_FILENAME -static btstack_sbc_decoder_state_t decoder_state; -static btstack_cvsd_plc_state_t cvsd_plc_state; -static int num_samples_to_write; +#endif #ifdef USE_PORTAUDIO -static int patestCallback( const void *inputBuffer, void *outputBuffer, +static int portaudio_callback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, @@ -189,45 +184,166 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, (void) inputBuffer; (void) userData; - uint32_t bytes_read = 0; - int bytes_per_buffer = framesPerBuffer; - if (negotiated_codec == HFP_CODEC_MSBC){ - bytes_per_buffer *= MSBC_BYTES_PER_FRAME; - } else { - bytes_per_buffer *= CVSD_BYTES_PER_FRAME; +// output part + + // config based on codec + int bytes_to_copy; + int prebuffer_bytes; + switch (negotiated_codec){ + case HFP_CODEC_MSBC: + bytes_to_copy = framesPerBuffer * MSBC_BYTES_PER_FRAME; + prebuffer_bytes = MSBC_PA_PREBUFFER_BYTES; + break; + case HFP_CODEC_CVSD: + bytes_to_copy = framesPerBuffer * CVSD_BYTES_PER_FRAME; + prebuffer_bytes = CVSD_PA_PREBUFFER_BYTES; + break; + default: + bytes_to_copy = framesPerBuffer * 2; // assume 1 channel / 16 bit audio samples + prebuffer_bytes = 0xfffffff; + break; } - if (btstack_ring_buffer_bytes_available(&ring_buffer) >= bytes_per_buffer){ - btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_per_buffer, &bytes_read); - } else { - printf("NOT ENOUGH DATA!\n"); - memset(outputBuffer, 0, bytes_per_buffer); + // fill with silence while paused + if (pa_output_paused){ + if (btstack_ring_buffer_bytes_available(&pa_output_ring_buffer) < prebuffer_bytes){ + memset(outputBuffer, 0, bytes_to_copy); + return 0; + } else { + // resume playback + pa_output_paused = 0; + } } - // printf("bytes avail after read: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer)); + + // get data from ringbuffer + uint32_t bytes_read = 0; + btstack_ring_buffer_read(&pa_output_ring_buffer, outputBuffer, bytes_to_copy, &bytes_read); + bytes_to_copy -= bytes_read; + + // fill with 0 if not enough + if (bytes_to_copy){ + memset(outputBuffer + bytes_read, 0, bytes_to_copy); + pa_output_paused = 1; + } +// end of output part + +// input part -- just store in ring buffer +#ifdef USE_PORTAUDIO_INPUT + btstack_ring_buffer_write(&pa_input_ring_buffer, (uint8_t *)inputBuffer, framesPerBuffer * 2); + pa_input_counter += framesPerBuffer * 2; +#endif + return 0; } + +// return 1 if ok +static int portaudio_initialize(int sample_rate){ + PaError err; + + /* -- initialize PortAudio -- */ + printf("PortAudio: Initialize\n"); + err = Pa_Initialize(); + if( err != paNoError ) return 0; + + /* -- setup input and output -- */ + const PaDeviceInfo *deviceInfo; + PaStreamParameters * inputParameters = NULL; + PaStreamParameters outputParameters; + outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ + outputParameters.channelCount = NUM_CHANNELS; + outputParameters.sampleFormat = paInt16; + outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + deviceInfo = Pa_GetDeviceInfo( outputParameters.device ); + log_info("PortAudio: Output device: %s", deviceInfo->name); +#ifdef USE_PORTAUDIO_INPUT + PaStreamParameters theInputParameters; + theInputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ + theInputParameters.channelCount = NUM_CHANNELS; + theInputParameters.sampleFormat = paInt16; + theInputParameters.suggestedLatency = Pa_GetDeviceInfo( theInputParameters.device )->defaultHighOutputLatency; + theInputParameters.hostApiSpecificStreamInfo = NULL; + inputParameters = &theInputParameters; + deviceInfo = Pa_GetDeviceInfo( inputParameters->device ); + log_info("PortAudio: Input device: %s", deviceInfo->name); #endif + /* -- setup output stream -- */ + printf("PortAudio: Open stream\n"); + err = Pa_OpenStream( + &pa_stream, + inputParameters, + &outputParameters, + sample_rate, + 0, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + portaudio_callback, + NULL ); + if (err != paNoError){ + printf("Error opening portaudio stream: \"%s\"\n", Pa_GetErrorText(err)); + return 0; + } + memset(pa_output_ring_buffer_storage, 0, sizeof(pa_output_ring_buffer_storage)); + btstack_ring_buffer_init(&pa_output_ring_buffer, pa_output_ring_buffer_storage, sizeof(pa_output_ring_buffer_storage)); +#ifdef USE_PORTAUDIO_INPUT + memset(pa_input_ring_buffer_storage, 0, sizeof(pa_input_ring_buffer_storage)); + btstack_ring_buffer_init(&pa_input_ring_buffer, pa_input_ring_buffer_storage, sizeof(pa_input_ring_buffer_storage)); + printf("PortAudio: Input buffer size %u\n", btstack_ring_buffer_bytes_free(&pa_input_ring_buffer)); +#endif + + /* -- start stream -- */ + err = Pa_StartStream(pa_stream); + if (err != paNoError){ + printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); + return 0; + } + pa_output_started = 1; + pa_output_paused = 1; +#ifdef USE_PORTAUDIO_INPUT + pa_input_started = 1; + pa_input_paused = 1; +#endif + + return 1; +} + +static void portaudio_terminate(void){ + if (!pa_stream) return; + + PaError err; + printf("PortAudio: Stop Stream\n"); + err = Pa_StopStream(pa_stream); + if (err != paNoError){ + printf("Error stopping the stream: \"%s\"\n", Pa_GetErrorText(err)); + return; + } + printf("PortAudio: Close Stream\n"); + err = Pa_CloseStream(pa_stream); + if (err != paNoError){ + printf("Error closing the stream: \"%s\"\n", Pa_GetErrorText(err)); + return; + } + pa_stream = NULL; + printf("PortAudio: Terminate\n"); + err = Pa_Terminate(); + if (err != paNoError){ + printf("Error terminating portaudio: \"%s\"\n", Pa_GetErrorText(err)); + return; + } +} +#endif + + static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){ UNUSED(context); UNUSED(sample_rate); // printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels); -#ifdef USE_PORTAUDIO - if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= MSBC_PREBUFFER_BYTES){ - /* -- start stream -- */ - PaError err = Pa_StartStream(stream); - if (err != paNoError){ - printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - pa_stream_started = 1; - } - btstack_ring_buffer_write(&ring_buffer, (uint8_t *)data, num_samples*num_channels*2); - // printf("bytes avail after write: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer)); +#ifdef HAVE_PORTAUDIO + btstack_ring_buffer_write(&pa_output_ring_buffer, (uint8_t *)data, num_samples*num_channels*2); #else UNUSED(num_channels); -#endif +#endif if (!num_samples_to_write) return; @@ -242,54 +358,30 @@ static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, i } static void sco_demo_init_mSBC(void){ - int sample_rate = 16000; - wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate); + printf("SCO Demo: Init mSBC\n"); + + wav_writer_open(SCO_WAV_FILENAME, 1, MSBC_SAMPLE_RATE); btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, NULL); - num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS; + num_samples_to_write = MSBC_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS; hfp_msbc_init(); - sco_demo_fill_audio_frame(); +#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE + sco_demo_msbc_fill_sine_audio_frame(); +#endif #ifdef SCO_MSBC_IN_FILENAME msbc_file_in = fopen(SCO_MSBC_IN_FILENAME, "wb"); printf("SCO Demo: creating mSBC in file %s, %p\n", SCO_MSBC_IN_FILENAME, msbc_file_in); #endif + #ifdef SCO_MSBC_OUT_FILENAME msbc_file_out = fopen(SCO_MSBC_OUT_FILENAME, "wb"); printf("SCO Demo: creating mSBC out file %s, %p\n", SCO_MSBC_OUT_FILENAME, msbc_file_out); #endif #ifdef USE_PORTAUDIO - PaError err; - PaStreamParameters outputParameters; - - /* -- initialize PortAudio -- */ - err = Pa_Initialize(); - if( err != paNoError ) return; - /* -- setup input and output -- */ - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = NUM_CHANNELS; - outputParameters.sampleFormat = MSBC_PA_SAMPLE_TYPE; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - /* -- setup stream -- */ - err = Pa_OpenStream( - &stream, - NULL, // &inputParameters, - &outputParameters, - MSBC_SAMPLE_RATE, - MSBC_FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, /* no callback, use blocking API */ - NULL ); /* no callback, so no callback userData */ - if (err != paNoError){ - printf("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage)); - btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage)); - pa_stream_started = 0; + portaudio_initialize(MSBC_SAMPLE_RATE); #endif } @@ -304,116 +396,65 @@ static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){ } static void sco_demo_init_CVSD(void){ - int sample_rate = 8000; - wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate); + printf("SCO Demo: Init CVSD\n"); + + wav_writer_open(SCO_WAV_FILENAME, 1, CVSD_SAMPLE_RATE); btstack_cvsd_plc_init(&cvsd_plc_state); - num_samples_to_write = sample_rate * SCO_WAV_DURATION_IN_SECONDS; + + num_samples_to_write = CVSD_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS; #ifdef USE_PORTAUDIO - PaError err; - PaStreamParameters outputParameters; - - /* -- initialize PortAudio -- */ - err = Pa_Initialize(); - if( err != paNoError ) return; - /* -- setup input and output -- */ - outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ - outputParameters.channelCount = NUM_CHANNELS; - outputParameters.sampleFormat = CVSD_PA_SAMPLE_TYPE; - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; - outputParameters.hostApiSpecificStreamInfo = NULL; - /* -- setup stream -- */ - err = Pa_OpenStream( - &stream, - NULL, // &inputParameters, - &outputParameters, - CVSD_SAMPLE_RATE, - CVSD_FRAMES_PER_BUFFER, - paClipOff, /* we won't output out of range samples so don't bother clipping them */ - patestCallback, /* no callback, use blocking API */ - NULL ); /* no callback, so no callback userData */ - if (err != paNoError){ - printf("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage)); - btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage)); - pa_stream_started = 0; + portaudio_initialize(CVSD_SAMPLE_RATE); #endif } static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){ if (!num_samples_to_write) return; + int16_t audio_frame_out[255]; // - const int num_samples = size - 3; + if (size > sizeof(audio_frame_out)){ + printf("sco_demo_receive_CVSD: SCO packet larger than local output buffer - dropping data.\n"); + return; + } + const int audio_bytes_read = size - 3; + const int num_samples = audio_bytes_read / CVSD_BYTES_PER_FRAME; const int samples_to_write = btstack_min(num_samples, num_samples_to_write); - int8_t audio_frame_out[24]; - - // memcpy(audio_frame_out, (int8_t*)(packet+3), 24); +#if 0 btstack_cvsd_plc_process_data(&cvsd_plc_state, (int8_t *)(packet+3), num_samples, audio_frame_out); - // int8_t * audio_frame_out = (int8_t*)&packet[3]; +#else + memcpy(audio_frame_out, packet+3, audio_bytes_read); +#endif - wav_writer_write_int8(samples_to_write, audio_frame_out); + wav_writer_write_int16(samples_to_write, audio_frame_out); num_samples_to_write -= samples_to_write; if (num_samples_to_write == 0){ sco_demo_close(); } #ifdef USE_PORTAUDIO - if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= CVSD_PREBUFFER_BYTES){ - /* -- start stream -- */ - PaError err = Pa_StartStream(stream); - if (err != paNoError){ - printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - pa_stream_started = 1; - } - btstack_ring_buffer_write(&ring_buffer, (uint8_t *)audio_frame_out, samples_to_write); + btstack_ring_buffer_write(&pa_output_ring_buffer, (uint8_t *)audio_frame_out, audio_bytes_read); #endif } -#endif -#endif - void sco_demo_close(void){ -#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE + printf("SCO demo close\n"); +#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE) + #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME) wav_writer_close(); +#endif printf("SCO demo statistics: "); if (negotiated_codec == HFP_CODEC_MSBC){ 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); } else { 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); } -#endif -#endif #ifdef HAVE_PORTAUDIO - if (pa_stream_started){ - PaError err = Pa_StopStream(stream); - if (err != paNoError){ - printf("Error stopping the stream: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - pa_stream_started = 0; - err = Pa_CloseStream(stream); - if (err != paNoError){ - printf("Error closing the stream: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - - err = Pa_Terminate(); - if (err != paNoError){ - printf("Error terminating portaudio: \"%s\"\n", Pa_GetErrorText(err)); - return; - } - } + portaudio_terminate(); #endif -#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE #ifdef SCO_WAV_FILENAME - #if 0 printf("SCO Demo: closing wav file\n"); if (negotiated_codec == HFP_CODEC_MSBC){ @@ -426,13 +467,16 @@ void sco_demo_close(void){ } #endif #endif + + negotiated_codec = -1; + #endif } void sco_demo_set_codec(uint8_t codec){ if (negotiated_codec == codec) return; negotiated_codec = codec; -#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE +#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE) #if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME) if (negotiated_codec == HFP_CODEC_MSBC){ sco_demo_init_mSBC(); @@ -444,8 +488,10 @@ void sco_demo_set_codec(uint8_t codec){ } void sco_demo_init(void){ - // status +#if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE + printf("SCO Demo: Sending and receiving audio via portaudio.\n"); +#endif #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE #ifdef HAVE_PORTAUDIO printf("SCO Demo: Sending sine wave, audio output via portaudio.\n"); @@ -460,7 +506,9 @@ void sco_demo_init(void){ printf("SCO Demo: Sending counter value, hexdump received data.\n"); #endif -#if SCO_DEMO_MODE != SCO_DEMO_MODE_SINE +#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE) + hci_set_sco_voice_setting(0x60); // linear, unsigned, 16-bit, CVSD +#else hci_set_sco_voice_setting(0x03); // linear, unsigned, 8-bit, transparent #endif @@ -478,19 +526,16 @@ void sco_demo_send(hci_con_handle_t sco_handle){ if (!sco_handle) return; - const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length(); - const int sco_payload_length = sco_packet_length - 3; + int sco_packet_length = hci_get_sco_packet_length(); + int sco_payload_length = sco_packet_length - 3; hci_reserve_packet_buffer(); uint8_t * sco_packet = hci_get_outgoing_packet_buffer(); - // set handle + flags - little_endian_store_16(sco_packet, 0, sco_handle); - // set len - sco_packet[2] = sco_payload_length; - const int audio_samples_per_packet = sco_payload_length; // for 8-bit data. for 16-bit data it's /2 - #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE if (negotiated_codec == HFP_CODEC_MSBC){ + // overwrite + sco_payload_length = 24; + sco_packet_length = sco_payload_length + 3; if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){ log_error("mSBC stream is empty."); @@ -501,31 +546,111 @@ void sco_demo_send(hci_con_handle_t sco_handle){ fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out); } - sco_demo_fill_audio_frame(); + sco_demo_msbc_fill_sine_audio_frame(); } else { - sco_demo_sine_wave_int8(audio_samples_per_packet, (int8_t *) (sco_packet+3)); + const int audio_samples_per_packet = sco_payload_length / CVSD_BYTES_PER_FRAME; + sco_demo_sine_wave_int16_at_8000_hz(audio_samples_per_packet, (int16_t *) (sco_packet+3)); } #endif + +#if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE + +#ifdef HAVE_PORTAUDIO + if (negotiated_codec == HFP_CODEC_MSBC){ + // MSBC + + // overwrite + sco_payload_length = 24; + sco_packet_length = sco_payload_length + 3; + + if (pa_input_paused){ + if (btstack_ring_buffer_bytes_available(&pa_input_ring_buffer) >= MSBC_PA_PREBUFFER_BYTES){ + // resume sending + pa_input_paused = 0; + } + } + + if (!pa_input_paused){ + int num_samples = hfp_msbc_num_audio_samples_per_frame(); + if (hfp_msbc_can_encode_audio_frame_now() && btstack_ring_buffer_bytes_available(&pa_input_ring_buffer) >= (num_samples * MSBC_BYTES_PER_FRAME)){ + int16_t sample_buffer[num_samples]; + uint32_t bytes_read; + btstack_ring_buffer_read(&pa_input_ring_buffer, (uint8_t*) sample_buffer, num_samples * MSBC_BYTES_PER_FRAME, &bytes_read); + hfp_msbc_encode_audio_frame(sample_buffer); + num_audio_frames++; + } + } + + if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){ + log_error("mSBC stream should not be empty."); + memset(sco_packet + 3, 0, sco_payload_length); + pa_input_paused = 1; + } else { + hfp_msbc_read_from_stream(sco_packet + 3, sco_payload_length); + if (msbc_file_out){ + // log outgoing mSBC data for testing + fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out); + } + } + + } else { + // CVSD + + log_info("send: bytes avail %u, free %u, counter %u", btstack_ring_buffer_bytes_available(&pa_input_ring_buffer), btstack_ring_buffer_bytes_free(&pa_input_ring_buffer), pa_input_counter); + // fill with silence while paused + int bytes_to_copy = sco_payload_length; + if (pa_input_paused){ + if (btstack_ring_buffer_bytes_available(&pa_input_ring_buffer) >= CVSD_PA_PREBUFFER_BYTES){ + // resume sending + pa_input_paused = 0; + } + } + + // get data from ringbuffer + uint16_t pos = 0; + if (!pa_input_paused){ + uint32_t bytes_read = 0; + btstack_ring_buffer_read(&pa_input_ring_buffer, sco_packet + 3, bytes_to_copy, &bytes_read); + bytes_to_copy -= bytes_read; + pos += bytes_read; + } + + // fill with 0 if not enough + if (bytes_to_copy){ + memset(sco_packet + 3 + pos, 0, bytes_to_copy); + pa_input_paused = 1; + } + } +#else + // just send '0's + if (negotiated_codec == HFP_CODEC_MSBC){ + sco_payload_length = 24; + sco_packet_length = sco_payload_length + 3; + } + memset(sco_packet + 3, 0, sco_payload_length); +#endif +#endif + #if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII - memset(&sco_packet[3], phase++, audio_samples_per_packet); + memset(&sco_packet[3], phase++, sco_payload_length); if (phase > 'z') phase = 'a'; #endif #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER int j; - for (j=0;j support only a single SCO connection -#define ALT_SETTING (2) +// #define ALT_SETTING (1) + +// alt setting for 1-3 connections and 8/16 bit +static const int alt_setting_8_bit[] = {1,2,3}; +static const int alt_setting_16_bit[] = {2,4,5}; // for ALT_SETTING >= 1 and 8-bit channel, we need the following isochronous packets // One complete SCO packet with 24 frames every 3 frames (== 3 ms) #define NUM_ISO_PACKETS (3) -// results in 9 bytes per frame -#define ISO_PACKET_SIZE (9) + +const uint16_t iso_packet_size_for_alt_setting[] = { + 0, + 9, + 17, + 25, + 33, + 49, + 63, +}; // 49 bytes is the max usb packet size for alternate setting 5 (Three 8 kHz 16-bit channels or one 8 kHz 16-bit channel and one 16 kHz 16-bit channel) // note: alt setting 6 has max packet size of 63 every 7.5 ms = 472.5 bytes / HCI packet, while max SCO packet has 255 byte payload -#define SCO_PACKET_SIZE (49) +#define SCO_PACKET_SIZE (49 * NUM_ISO_PACKETS) // Outgoing SCO packet queue // simplified ring buffer implementation @@ -162,6 +174,15 @@ static int sco_ring_write; // packet idx static int sco_out_transfers_active; static struct libusb_transfer *sco_out_transfers[SCO_OUT_BUFFER_COUNT]; static int sco_out_transfers_in_flight[SCO_OUT_BUFFER_COUNT]; + +// pause/resume +static uint16_t sco_voice_setting; +static int sco_num_connections; +static int sco_shutdown; + +// dynamic SCO configuration +static uint16_t iso_packet_size; + #endif // outgoing buffer for HCI Command packets @@ -240,22 +261,8 @@ LIBUSB_CALL static void async_callback(struct libusb_transfer *transfer){ int c; // identify and free transfers as part of shutdown - if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) { - for (c=0;cregister_packet_handler = usb_register_packet_handler; hci_transport_usb->can_send_packet_now = usb_can_send_packet_now; hci_transport_usb->send_packet = usb_send_packet; +#ifdef ENABLE_SCO_OVER_HCI + hci_transport_usb->set_sco_config = usb_set_sco_config; +#endif } return hci_transport_usb; } diff --git a/platform/windows/hci_transport_h2_winusb.c b/platform/windows/hci_transport_h2_winusb.c index 5bd4897dd..6653eae7c 100644 --- a/platform/windows/hci_transport_h2_winusb.c +++ b/platform/windows/hci_transport_h2_winusb.c @@ -167,16 +167,27 @@ static WinUsb_GetCurrentFrameNumber_t WinUsb_GetCurrentFrameNumber; // --> support only a single SCO connection #define ALT_SETTING (1) +// alt setting for 1-3 connections and 8/16 bit +const int alt_setting_8_bit[] = {1,2,3}; +const int alt_setting_16_bit[] = {2,4,5}; + // for ALT_SETTING >= 1 and 8-bit channel, we need the following isochronous packets // One complete SCO packet with 24 frames every 3 frames (== 3 ms) #define NUM_ISO_PACKETS (3) -// results in 9 bytes per frame -#define ISO_PACKET_SIZE (9) +const uint16_t iso_packet_size_for_alt_setting[] = { + 0, + 9, + 17, + 25, + 33, + 49, + 63, +}; // 49 bytes is the max usb packet size for alternate setting 5 (Three 8 kHz 16-bit channels or one 8 kHz 16-bit channel and one 16 kHz 16-bit channel) // note: alt setting 6 has max packet size of 63 every 7.5 ms = 472.5 bytes / HCI packet, while max SCO packet has 255 byte payload -#define SCO_PACKET_SIZE (NUM_ISO_PACKETS * ISO_PACKET_SIZE) +#define SCO_PACKET_SIZE (49 * NUM_ISO_PACKETS) #define ISOC_BUFFERS 8 @@ -278,6 +289,12 @@ static btstack_data_source_t usb_data_source_sco_out[SCO_RING_BUFFER_COUNT]; static uint8_t sco_ring_buffer[SCO_RING_BUFFER_SIZE]; static int sco_ring_write; // packet idx +// SCO Reconfiguration - pause/resume +static uint16_t sco_voice_setting; +static int sco_num_connections; +static int sco_shutdown; + +static uint16_t iso_packet_size; #endif #if 0 @@ -310,6 +327,30 @@ static void sco_ring_init(void){ static int sco_ring_have_space(void){ return sco_ring_transfers_active < SCO_RING_BUFFER_COUNT; } +static void usb_sco_register_buffers(void){ + BOOL result; + result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, sizeof(hci_sco_in_buffer), &hci_sco_in_buffer_handle); + if (!result) { + log_error("usb_sco_register_buffers: register in buffer failed, error %lu", GetLastError()); + } + log_info("hci_sco_in_buffer_handle %p", hci_sco_in_buffer_handle); + + result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_out_addr, sco_ring_buffer, sizeof(sco_ring_buffer), &hci_sco_out_buffer_handle); + if (!result) { + log_error("usb_sco_unregister_buffers: register out buffer failed, error %lu", GetLastError()); + } + log_info("hci_sco_out_buffer_handle %p", hci_sco_out_buffer_handle); +} +static void usb_sco_unregister_buffers(void){ + if (hci_sco_in_buffer_handle){ + WinUsb_UnregisterIsochBuffer(hci_sco_in_buffer_handle); + hci_sco_in_buffer_handle = NULL; + } + if (hci_sco_out_buffer_handle){ + WinUsb_UnregisterIsochBuffer(hci_sco_out_buffer_handle); + hci_sco_out_buffer_handle = NULL; + } +} #endif static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){ @@ -340,14 +381,7 @@ static void usb_free_resources(void){ } #ifdef ENABLE_SCO_OVER_HCI - if (hci_sco_in_buffer_handle){ - WinUsb_UnregisterIsochBuffer(hci_sco_in_buffer_handle); - hci_sco_in_buffer_handle = NULL; - } - if (hci_sco_out_buffer_handle){ - WinUsb_UnregisterIsochBuffer(hci_sco_out_buffer_handle); - hci_sco_out_buffer_handle = NULL; - } + usb_sco_unregister_buffers(); #endif } @@ -387,16 +421,21 @@ exit_on_error: // frame number gets updated static void usb_submit_sco_in_transfer_at_frame(int i, ULONG * frame_number){ + if (sco_shutdown){ + log_info("USB SCO Shutdown:: usb_submit_sco_in_transfer_at_frame called"); + return; + } + LARGE_INTEGER timestamp; ULONG current_frame_number; WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); ULONG frame_before = *frame_number; - BOOL result = WinUsb_ReadIsochPipe(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE, + BOOL result = WinUsb_ReadIsochPipe(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, iso_packet_size * NUM_ISO_PACKETS, frame_number, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]); - log_info("WinUsb_ReadIsochPipe #%02u: current %lu, planned %lu - buffer %lu", i, current_frame_number, frame_before, frame_before - current_frame_number); + // log_info("WinUsb_ReadIsochPipe #%02u: current %lu, planned %lu - buffer %lu", i, current_frame_number, frame_before, frame_before - current_frame_number); if (!result) { if (GetLastError() == ERROR_IO_PENDING) { @@ -415,13 +454,18 @@ exit_on_error: static void usb_submit_sco_in_transfer_asap(int i, int continue_stream){ + if (sco_shutdown){ + log_info("USB SCO Shutdown:: usb_submit_sco_in_transfer_at_frame called"); + return; + } + LARGE_INTEGER timestamp; ULONG current_frame_number; WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp); // log_info("usb_submit_sco_in_transfer[%02u]: current frame %lu", i, current_frame_number); - BOOL result = WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, SCO_PACKET_SIZE, + BOOL result = WinUsb_ReadIsochPipeAsap(hci_sco_in_buffer_handle, i * SCO_PACKET_SIZE, iso_packet_size * NUM_ISO_PACKETS, continue_stream, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]); if (!result) { @@ -530,6 +574,11 @@ static void usb_process_sco_out(btstack_data_source_t *ds, btstack_data_source_ btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE); + if (sco_shutdown){ + log_info("USB SCO Shutdown:: usb_process_sco_out called"); + return; + } + // get current frame number ULONG current_frame_number; LARGE_INTEGER timestamp; @@ -541,11 +590,11 @@ static void usb_process_sco_out(btstack_data_source_t *ds, btstack_data_source_ if (ds == &usb_data_source_sco_out[transfer_index]) break; } - log_info("usb_process_sco_out[%02u] -- current frame %lu", transfer_index, current_frame_number); + // log_info("usb_process_sco_out[%02u] -- current frame %lu", transfer_index, current_frame_number); DWORD bytes_transferred; BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_out[transfer_index], &bytes_transferred, FALSE); - log_info("usb_process_sco_out_done: #%u result %u, bytes %u, state %u", transfer_index, ok, (int) bytes_transferred, sco_state); + // log_info("usb_process_sco_out_done: #%u result %u, bytes %u, state %u", transfer_index, ok, (int) bytes_transferred, sco_state); if(!ok){ DWORD err = GetLastError(); if (err == ERROR_IO_INCOMPLETE){ @@ -563,11 +612,11 @@ static void usb_process_sco_out(btstack_data_source_t *ds, btstack_data_source_ if (sco_ring_transfers_active){ // update expected and wait for completion usb_sco_out_expected_transfer = (transfer_index+ 1) % SCO_RING_BUFFER_COUNT; - log_info("usb_process_sco_out_done[%02u]: wait for transfer %02u", transfer_index, usb_sco_out_expected_transfer); + // log_info("usb_process_sco_out_done[%02u]: wait for transfer %02u", transfer_index, usb_sco_out_expected_transfer); btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_out[usb_sco_out_expected_transfer], DATA_SOURCE_CALLBACK_WRITE); } - log_info("usb_process_sco_out_done: transfers active %u", sco_ring_transfers_active); + // log_info("usb_process_sco_out_done: transfers active %u", sco_ring_transfers_active); // mark free if (sco_ring_have_space()) { @@ -580,6 +629,10 @@ static void usb_process_sco_in(btstack_data_source_t *ds, btstack_data_source_c btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_READ); + if (sco_shutdown){ + log_info("USB SCO Shutdown: usb_process_sco_out called"); + return; + } // find index int i; @@ -705,19 +758,20 @@ static BOOL usb_scan_for_bluetooth_endpoints(void) { sco_out_addr = 0; sco_in_addr = 0; - // look for SCO pipes on Interface #1, Alt Setting ALT_SETTING - result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, ALT_SETTING, &usb_interface_descriptor); + // look for SCO pipes on Interface #1, Alt Setting 1 + int alt_setting = 1; + result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, alt_setting, &usb_interface_descriptor); if (!result) goto exit_on_error; for (i=0;i 0; BOOL ok = WinUsb_WriteIsochPipeAsap(hci_sco_out_buffer_handle, transfer_index * SCO_PACKET_SIZE, size, continue_stream, &usb_overlapped_sco_out[transfer_index]); - log_info("usb_send_sco_packet: using slot #%02u, current frame %lu, continue stream %u, ok %u", transfer_index, current_frame_number, continue_stream, ok); + // log_info("usb_send_sco_packet: using slot #%02u, current frame %lu, continue stream %u, ok %u", transfer_index, current_frame_number, continue_stream, ok); if (!ok) { if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error; } @@ -1251,7 +1312,7 @@ static int usb_send_sco_packet(uint8_t *packet, int size){ uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0}; packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event)); - log_info("usb_send_sco_packet: transfers active %u", sco_ring_transfers_active); + // log_info("usb_send_sco_packet: transfers active %u", sco_ring_transfers_active); // and if we have more space for SCO packets if (sco_ring_have_space()) { @@ -1281,6 +1342,23 @@ static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){ } } +#ifdef ENABLE_SCO_OVER_HCI +static void usb_set_sco_config(uint16_t voice_setting, int num_connections){ + log_info("usb_set_sco_config: voice settings 0x%04x, num connections %u", voice_setting, num_connections); + + if (num_connections != sco_num_connections){ + sco_voice_setting = voice_setting; + if (sco_num_connections){ + usb_sco_stop(); + } + sco_num_connections = num_connections; + if (num_connections){ + usb_sco_start(); + } + } +} +#endif + // get usb singleton static const hci_transport_t hci_transport_usb = { /* const char * name; */ "H2_WINUSB", @@ -1292,6 +1370,7 @@ static const hci_transport_t hci_transport_usb = { /* int (*send_packet)(...); */ &usb_send_packet, /* int (*set_baudrate)(uint32_t baudrate); */ NULL, /* void (*reset_link)(void); */ NULL, + /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ usb_set_sco_config, }; const hci_transport_t * hci_transport_usb_instance(void) { diff --git a/src/btstack_ring_buffer.c b/src/btstack_ring_buffer.c index 8663c784a..1c78f6cde 100644 --- a/src/btstack_ring_buffer.c +++ b/src/btstack_ring_buffer.c @@ -42,8 +42,10 @@ #include #include +#include #include "btstack_ring_buffer.h" +#include "btstack_util.h" #define ERROR_CODE_MEMORY_CAPACITY_EXCEEDED 0x07 @@ -79,15 +81,27 @@ int btstack_ring_buffer_write(btstack_ring_buffer_t * ring_buffer, uint8_t * dat if (btstack_ring_buffer_bytes_free(ring_buffer) < data_length){ return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; } - int count = 0; - while (count < data_length){ - if (ring_buffer->last_written_index < ring_buffer->size - 1){ - ring_buffer->last_written_index++; - } else { - ring_buffer->last_written_index = 0; - } - ring_buffer->storage[ring_buffer->last_written_index] = data[count++]; + + // copy first chunk + unsigned int bytes_until_end = ring_buffer->size - ring_buffer->last_written_index; + unsigned int bytes_to_copy = btstack_min(bytes_until_end, data_length); + memcpy(&ring_buffer->storage[ring_buffer->last_written_index], data, bytes_to_copy); + data_length -= bytes_to_copy; + data += bytes_to_copy; + + // update last written index + ring_buffer->last_written_index += bytes_to_copy; + if (ring_buffer->last_written_index == ring_buffer->size){ + ring_buffer->last_written_index = 0; } + + // copy second chunk + if (data_length) { + memcpy(&ring_buffer->storage[0], data, data_length); + ring_buffer->last_written_index += data_length; + } + + // mark buffer as full if (ring_buffer->last_written_index == ring_buffer->last_read_index){ ring_buffer->full = 1; } @@ -96,20 +110,30 @@ int btstack_ring_buffer_write(btstack_ring_buffer_t * ring_buffer, uint8_t * dat // fetch data_length bytes from ring buffer void btstack_ring_buffer_read(btstack_ring_buffer_t * ring_buffer, uint8_t * data, uint32_t data_length, uint32_t * number_of_bytes_read){ - uint32_t count = 0; - while (count < data_length && btstack_ring_buffer_bytes_available(ring_buffer)){ - if (ring_buffer->last_read_index < ring_buffer->last_written_index ) { - ring_buffer->last_read_index++; - } else { - if (ring_buffer->last_read_index < ring_buffer->size - 1){ - ring_buffer->last_read_index++; - } else { - ring_buffer->last_read_index = 0; - } - } - ring_buffer->full = 0; - data[count++] = ring_buffer->storage[ring_buffer->last_read_index]; + // limit data to get and report + data_length = btstack_min(data_length, btstack_ring_buffer_bytes_available(ring_buffer)); + *number_of_bytes_read = data_length; + + // copy first chunk + unsigned int bytes_until_end = ring_buffer->size - ring_buffer->last_read_index; + unsigned int bytes_to_copy = btstack_min(bytes_until_end, data_length); + memcpy(data, &ring_buffer->storage[ring_buffer->last_read_index], bytes_to_copy); + data_length -= bytes_to_copy; + data += bytes_to_copy; + + // update last read index + ring_buffer->last_read_index += bytes_to_copy; + if (ring_buffer->last_read_index == ring_buffer->size){ + ring_buffer->last_read_index = 0; } - *number_of_bytes_read = count; + + // copy second chunk + if (data_length) { + memcpy(data, &ring_buffer->storage[0], data_length); + ring_buffer->last_read_index += data_length; + } + + // clear full flag + ring_buffer->full = 0; } diff --git a/src/classic/btstack_cvsd_plc.c b/src/classic/btstack_cvsd_plc.c index 8c52ad36b..7bbae3eb4 100644 --- a/src/classic/btstack_cvsd_plc.c +++ b/src/classic/btstack_cvsd_plc.c @@ -49,11 +49,13 @@ #include "btstack_cvsd_plc.h" #include "btstack_debug.h" +#define SAMPLE_FORMAT int16_t + static float rcos[CVSD_OLAL] = { - 0.99148655,0.96623611,0.92510857,0.86950446, - 0.80131732,0.72286918,0.63683150,0.54613418, - 0.45386582,0.36316850,0.27713082,0.19868268, - 0.13049554,0.07489143,0.03376389,0.00851345}; + 0.99148655f,0.96623611f,0.92510857f,0.86950446f, + 0.80131732f,0.72286918f,0.63683150f,0.54613418f, + 0.45386582f,0.36316850f,0.27713082f,0.19868268f, + 0.13049554f,0.07489143f,0.03376389f,0.00851345f}; // taken from http://www.codeproject.com/Articles/69941/Best-Square-Root-Method-Algorithm-Function-Precisi // Algorithm: Babylonian Method + some manipulations on IEEE 32 bit floating point representation @@ -79,7 +81,7 @@ static float absolute(float x){ return x; } -static float CrossCorrelation(int8_t *x, int8_t *y){ +static float CrossCorrelation(SAMPLE_FORMAT *x, SAMPLE_FORMAT *y){ float num = 0; float den = 0; float x2 = 0; @@ -94,7 +96,7 @@ static float CrossCorrelation(int8_t *x, int8_t *y){ return num/den; } -static int PatternMatch(int8_t *y){ +static int PatternMatch(SAMPLE_FORMAT *y){ float maxCn = -999999.0; // large negative number int bestmatch = 0; float Cn; @@ -109,7 +111,7 @@ static int PatternMatch(int8_t *y){ return bestmatch; } -static float AmplitudeMatch(int8_t *y, int8_t bestmatch) { +static float AmplitudeMatch(SAMPLE_FORMAT *y, SAMPLE_FORMAT bestmatch) { int i; float sumx = 0; float sumy = 0.000001f; @@ -126,19 +128,18 @@ static float AmplitudeMatch(int8_t *y, int8_t bestmatch) { return sf; } -static int8_t crop_to_int8(float val){ +static SAMPLE_FORMAT crop_sample(float val){ float croped_val = val; - if (croped_val > 127.0) croped_val= 127.0; - if (croped_val < -128.0) croped_val=-128.0; - return (int8_t) croped_val; + if (croped_val > 32767.0) croped_val= 32767.0; + if (croped_val < -32768.0) croped_val=-32768.0; + return (SAMPLE_FORMAT) croped_val; } - void btstack_cvsd_plc_init(btstack_cvsd_plc_state_t *plc_state){ memset(plc_state, 0, sizeof(btstack_cvsd_plc_state_t)); } -void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *out){ +void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, SAMPLE_FORMAT *out){ float val; int i = 0; float sf = 1; @@ -154,19 +155,19 @@ void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *out sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag); for (i=0;ihist[plc_state->bestlag+i]; - plc_state->hist[CVSD_LHIST+i] = crop_to_int8(val); + plc_state->hist[CVSD_LHIST+i] = crop_sample(val); } for (;ihist[plc_state->bestlag+i]; - plc_state->hist[CVSD_LHIST+i] = crop_to_int8(val); + plc_state->hist[CVSD_LHIST+i] = crop_sample(val); } for (;ihist[plc_state->bestlag+i]; float right = plc_state->hist[plc_state->bestlag+i]; val = left*rcos[i-CVSD_FS] + right*rcos[CVSD_OLAL-1-i+CVSD_FS]; - plc_state->hist[CVSD_LHIST+i] = crop_to_int8(val); + plc_state->hist[CVSD_LHIST+i] = crop_sample(val); } for (;inbf>0){ @@ -199,7 +200,7 @@ void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *in float left = plc_state->hist[CVSD_LHIST+i]; float right = in[i]; val = left * rcos[i-CVSD_RT] + right *rcos[CVSD_OLAL+CVSD_RT-1-i]; - out[i] = (int8_t)val; + out[i] = (SAMPLE_FORMAT)val; } } @@ -217,7 +218,7 @@ void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *in plc_state->nbf=0; } -static int count_equal_bytes(int8_t * packet, uint16_t size){ +static int count_equal_samples(SAMPLE_FORMAT * packet, uint16_t size){ int count = 0; int temp_count = 1; int i; @@ -237,13 +238,15 @@ static int count_equal_bytes(int8_t * packet, uint16_t size){ return count; } -static int bad_frame(int8_t * frame, uint16_t size){ - return count_equal_bytes(frame, size) > 20; +// @assumption frame len 24 samples +static int bad_frame(SAMPLE_FORMAT * frame, uint16_t size){ + return count_equal_samples(frame, size) > 20; } -void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out){ +void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, SAMPLE_FORMAT * in, uint16_t size, SAMPLE_FORMAT * out){ if (size != 24){ log_error("btstack_cvsd_plc_process_data: audio frame size is incorrect. Expected %d, got %d", CVSD_FS, size); + return; } state->frame_count++; if (bad_frame(in,size)){ @@ -263,27 +266,6 @@ void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int8_t * in } } -void btstack_cvsd_plc_mark_bad_frame(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out){ - if (size != 24){ - log_error("btstack_cvsd_plc_mark_bad_frame: audio frame size is incorrect. Expected %d, got %d", CVSD_FS, size); - } - state->frame_count++; - - if (bad_frame(in,size)){ - memcpy(out, in, size); - if (state->good_frames_nr > CVSD_LHIST/CVSD_FS){ - memset(out, 50, size); - state->bad_frames_nr++; - } - } else { - memcpy(out, in, size); - state->good_frames_nr++; - if (state->good_frames_nr == 1){ - log_info("First good frame at index %d\n", state->frame_count-1); - } - } -} - void btstack_cvsd_dump_statistics(btstack_cvsd_plc_state_t * state){ log_info("Good frames: %d\n", state->good_frames_nr); log_info("Bad frames: %d\n", state->bad_frames_nr); diff --git a/src/classic/btstack_cvsd_plc.h b/src/classic/btstack_cvsd_plc.h index a3f777f0d..40d53a86a 100644 --- a/src/classic/btstack_cvsd_plc.h +++ b/src/classic/btstack_cvsd_plc.h @@ -58,7 +58,7 @@ extern "C" { /* PLC State Information */ typedef struct cvsd_plc_state { - int8_t hist[CVSD_LHIST+CVSD_FS+CVSD_RT+CVSD_OLAL]; + int16_t hist[CVSD_LHIST+CVSD_FS+CVSD_RT+CVSD_OLAL]; int16_t bestlag; int nbf; @@ -69,11 +69,9 @@ typedef struct cvsd_plc_state { } btstack_cvsd_plc_state_t; void btstack_cvsd_plc_init(btstack_cvsd_plc_state_t *plc_state); -void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *out); -void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int8_t *in, int8_t *out); -uint8_t * btstack_cvsd_plc_zero_signal_frame(void); -void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out); -void btstack_cvsd_plc_mark_bad_frame(btstack_cvsd_plc_state_t * state, int8_t * in, uint16_t size, int8_t * out); +void btstack_cvsd_plc_bad_frame(btstack_cvsd_plc_state_t *plc_state, int16_t *out); +void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int16_t *in, int16_t *out); +void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int16_t * in, uint16_t size, int16_t * out); void btstack_cvsd_dump_statistics(btstack_cvsd_plc_state_t * state); #if defined __cplusplus diff --git a/src/classic/btstack_sbc_plc.c b/src/classic/btstack_sbc_plc.c index 07b77fcb0..b6f104c7f 100644 --- a/src/classic/btstack_sbc_plc.c +++ b/src/classic/btstack_sbc_plc.c @@ -47,6 +47,8 @@ #include "btstack_sbc_plc.h" +#define SAMPLE_FORMAT int16_t + static uint8_t indices0[] = { 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77, 0x6d, @@ -84,7 +86,7 @@ static float absolute(float x){ return x; } -static float CrossCorrelation(int16_t *x, int16_t *y){ +static float CrossCorrelation(SAMPLE_FORMAT *x, SAMPLE_FORMAT *y){ float num = 0; float den = 0; float x2 = 0; @@ -99,13 +101,13 @@ static float CrossCorrelation(int16_t *x, int16_t *y){ return num/den; } -static int PatternMatch(int16_t *y){ - float maxCn = -999999.0; /* large negative number */ +static int PatternMatch(SAMPLE_FORMAT *y){ + float maxCn = -999999.0; // large negative number int bestmatch = 0; float Cn; int n; for (n=0;nmaxCn){ bestmatch=n; maxCn = Cn; @@ -114,8 +116,7 @@ static int PatternMatch(int16_t *y){ return bestmatch; } - -static float AmplitudeMatch(int16_t *y, int16_t bestmatch) { +static float AmplitudeMatch(SAMPLE_FORMAT *y, SAMPLE_FORMAT bestmatch) { int i; float sumx = 0; float sumy = 0.000001f; @@ -126,17 +127,17 @@ static float AmplitudeMatch(int16_t *y, int16_t bestmatch) { sumy += absolute(y[bestmatch+i]); } sf = sumx/sumy; - /* This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts */ + // This is not in the paper, but limit the scaling factor to something reasonable to avoid creating artifacts if (sf<0.75f) sf=0.75f; if (sf>1.2f) sf=1.2f; return sf; } -static int16_t crop_to_int16(float val){ +static SAMPLE_FORMAT crop_sample(float val){ float croped_val = val; if (croped_val > 32767.0) croped_val= 32767.0; if (croped_val < -32768.0) croped_val=-32768.0; - return (int16_t) croped_val; + return (SAMPLE_FORMAT) croped_val; } uint8_t * btstack_sbc_plc_zero_signal_frame(void){ @@ -149,46 +150,44 @@ void btstack_sbc_plc_init(btstack_sbc_plc_state_t *plc_state){ memset(plc_state->hist,0,sizeof(plc_state->hist)); } -void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, int16_t *ZIRbuf, int16_t *out){ +void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, SAMPLE_FORMAT *ZIRbuf, SAMPLE_FORMAT *out){ float val; int i = 0; float sf = 1; - plc_state->nbf++; if (plc_state->nbf==1){ - /* Perform pattern matching to find where to replicate */ + // Perform pattern matching to find where to replicate plc_state->bestlag = PatternMatch(plc_state->hist); - /* the replication begins after the template match */ + // the replication begins after the template match plc_state->bestlag += SBC_M; - /* Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet */ + // Compute Scale Factor to Match Amplitude of Substitution Packet to that of Preceding Packet sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag); for (i=0;ihist[plc_state->bestlag+i]; val = left*rcos[i] + right*rcos[SBC_OLAL-1-i]; - plc_state->hist[SBC_LHIST+i] = crop_to_int16(val); + plc_state->hist[SBC_LHIST+i] = crop_sample(val); } for (;ihist[plc_state->bestlag+i]; - plc_state->hist[SBC_LHIST+i] = crop_to_int16(val); + plc_state->hist[SBC_LHIST+i] = crop_sample(val); } for (;ihist[plc_state->bestlag+i]; float right = plc_state->hist[plc_state->bestlag+i]; - val = left*rcos[i-SBC_FS]+right*rcos[SBC_FS+SBC_OLAL-1-i]; - plc_state->hist[SBC_LHIST+i] = crop_to_int16(val); + val = left*rcos[i-SBC_FS]+right*rcos[SBC_OLAL-1-i+SBC_FS]; + plc_state->hist[SBC_LHIST+i] = crop_sample(val); } for (;ihist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i]; } - } else { - for (i=0;ihist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i]; } } @@ -196,14 +195,13 @@ void btstack_sbc_plc_bad_frame(btstack_sbc_plc_state_t *plc_state, int16_t *ZIRb out[i] = plc_state->hist[SBC_LHIST+i]; } - /* shift the history buffer */ + // shift the history buffer for (i=0;ihist[i] = plc_state->hist[i+SBC_FS]; } - } -void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, int16_t *in, int16_t *out){ +void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, SAMPLE_FORMAT *in, SAMPLE_FORMAT *out){ float val; int i = 0; if (plc_state->nbf>0){ @@ -211,22 +209,22 @@ void btstack_sbc_plc_good_frame(btstack_sbc_plc_state_t *plc_state, int16_t *in, out[i] = plc_state->hist[SBC_LHIST+i]; } - for (;ihist[SBC_LHIST+i]; float right = in[i]; - val = left*rcos[i-SBC_RT] + right*rcos[SBC_OLAL-1-i+SBC_RT]; - out[i] = (int16_t)val; + val = left*rcos[i-SBC_RT] + right*rcos[SBC_OLAL+SBC_RT-1-i]; + out[i] = (SAMPLE_FORMAT)val; } } + for (;ihist[SBC_LHIST+i] = out[i]; } - /* shift the history buffer */ + // shift the history buffer for (i=0;ihist[i] = plc_state->hist[i+SBC_FS]; } diff --git a/src/classic/hfp.c b/src/classic/hfp.c index 9bb8c2308..b3786d923 100644 --- a/src/classic/hfp.c +++ b/src/classic/hfp.c @@ -539,7 +539,7 @@ void hfp_handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet hfp_connection_t * hfp_connection = NULL; uint8_t status; - log_info("AG packet_handler type %u, event type %x, size %u", packet_type, hci_event_packet_get_type(packet), size); + log_debug("HFP packet_handler type %u, event type %x, size %u", packet_type, hci_event_packet_get_type(packet), size); switch (hci_event_packet_get_type(packet)) { case HCI_EVENT_CONNECTION_REQUEST: diff --git a/src/classic/hfp_ag.c b/src/classic/hfp_ag.c index 901823dca..7902c0799 100644 --- a/src/classic/hfp_ag.c +++ b/src/classic/hfp_ag.c @@ -650,7 +650,7 @@ static void hfp_ag_slc_established(hfp_connection_t * hfp_connection){ } static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * hfp_connection){ - log_info("hfp_ag_run_for_context_service_level_connection state %u, command %u", hfp_connection->state, hfp_connection->command); + // log_info("hfp_ag_run_for_context_service_level_connection state %u, command %u", hfp_connection->state, hfp_connection->command); if (hfp_connection->state >= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return 0; int done = 0; switch(hfp_connection->command){ @@ -1641,7 +1641,7 @@ static void hfp_ag_send_call_status(hfp_connection_t * hfp_connection, int call_ static void hfp_run_for_context(hfp_connection_t *hfp_connection){ - log_info("hfp_run_for_context %p", hfp_connection); + // log_info("hfp_run_for_context %p", hfp_connection); if (!hfp_connection) return; diff --git a/src/hci.c b/src/hci.c index 8dd43ec5f..c31224997 100644 --- a/src/hci.c +++ b/src/hci.c @@ -219,6 +219,20 @@ hci_connection_t * hci_connection_for_bd_addr_and_type(bd_addr_t addr, bd_addr_ #ifdef ENABLE_CLASSIC +#ifdef ENABLE_SCO_OVER_HCI +static int hci_number_sco_connections(void){ + int connections = 0; + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, &hci_stack->connections); + while (btstack_linked_list_iterator_has_next(&it)){ + hci_connection_t * connection = (hci_connection_t *) btstack_linked_list_iterator_next(&it); + if (connection->address_type != BD_ADDR_TYPE_SCO) continue; + connections++; + } + return connections; +} +#endif + static void hci_connection_timeout_handler(btstack_timer_source_t *timer){ hci_connection_t * connection = (hci_connection_t *) btstack_run_loop_get_timer_context(timer); #ifdef HAVE_EMBEDDED_TICK @@ -749,6 +763,10 @@ static void acl_handler(uint8_t *packet, int size){ static void hci_shutdown_connection(hci_connection_t *conn){ log_info("Connection closed: handle 0x%x, %s", conn->con_handle, bd_addr_to_str(conn->address)); +#ifdef ENABLE_SCO_OVER_HCI + int addr_type = conn->address_type; +#endif + btstack_run_loop_remove_timer(&conn->timeout); btstack_linked_list_remove(&hci_stack->connections, (btstack_linked_item_t *) conn); @@ -756,6 +774,13 @@ static void hci_shutdown_connection(hci_connection_t *conn){ // now it's gone hci_emit_nr_connections_changed(); + +#ifdef ENABLE_SCO_OVER_HCI + // update SCO + if (addr_type == BD_ADDR_TYPE_SCO && hci_stack->hci_transport && hci_stack->hci_transport->set_sco_config){ + hci_stack->hci_transport->set_sco_config(hci_stack->sco_voice_setting_active, hci_number_sco_connections()); + } +#endif } #ifdef ENABLE_CLASSIC @@ -1157,6 +1182,13 @@ static void hci_initializing_run(void){ hci_stack->substate = HCI_INIT_W4_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING; hci_send_cmd(&hci_write_default_erroneous_data_reporting, 1); break; + // only sent if ENABLE_SCO_OVER_HCI and manufacturer is Broadcom + case HCI_INIT_BCM_WRITE_SCO_PCM_INT: + hci_stack->substate = HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT; + log_info("BCM: Route SCO data via HCI transport"); + hci_send_cmd(&hci_bcm_write_sco_pcm_int, 1, 0, 0, 0, 0); + break; + #endif #ifdef ENABLE_BLE // LE INIT @@ -1402,14 +1434,15 @@ static void hci_initializing_event_handler(uint8_t * packet, uint16_t size){ case HCI_INIT_W4_SET_EVENT_MASK: // skip Classic init commands for LE only chipsets if (!hci_classic_supported()){ +#ifdef ENABLE_BLE if (hci_le_supported()){ hci_stack->substate = HCI_INIT_LE_READ_BUFFER_SIZE; // skip all classic command return; - } else { - log_error("Neither BR/EDR nor LE supported"); - hci_init_done(); - return; } +#endif + log_error("Neither BR/EDR nor LE supported"); + hci_init_done(); + return; } if (!gap_ssp_supported()){ hci_stack->substate = HCI_INIT_WRITE_PAGE_TIMEOUT; @@ -1447,6 +1480,12 @@ static void hci_initializing_event_handler(uint8_t * packet, uint16_t size){ // explicit fall through to reduce repetitions case HCI_INIT_W4_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING: + // skip bcm set sco pcm config on non-Broadcom chipsets + if (hci_stack->manufacturer == COMPANY_ID_BROADCOM_CORPORATION) break; + hci_stack->substate = HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT; + // explicit fall through to reduce repetitions + + case HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT: if (!hci_le_supported()){ // SKIP LE init for Classic only configuration hci_init_done(); @@ -1724,6 +1763,13 @@ static void event_handler(uint8_t *packet, int size){ } conn->state = OPEN; conn->con_handle = little_endian_read_16(packet, 3); + +#ifdef ENABLE_SCO_OVER_HCI + // update SCO + if (conn->address_type == BD_ADDR_TYPE_SCO && hci_stack->hci_transport && hci_stack->hci_transport->set_sco_config){ + hci_stack->hci_transport->set_sco_config(hci_stack->sco_voice_setting_active, hci_number_sco_connections()); + } +#endif break; case HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE: @@ -2175,8 +2221,8 @@ void hci_init(const hci_transport_t *transport, const void *config){ hci_stack->ssp_authentication_requirement = SSP_IO_AUTHREQ_MITM_PROTECTION_NOT_REQUIRED_GENERAL_BONDING; hci_stack->ssp_auto_accept = 1; - // voice setting - signed 8 bit pcm data with CVSD over the air - hci_stack->sco_voice_setting = 0x40; + // voice setting - signed 16 bit pcm data with CVSD over the air + hci_stack->sco_voice_setting = 0x60; hci_state_reset(); } @@ -3074,6 +3120,19 @@ int hci_send_cmd_packet(uint8_t *packet, int size){ connectionClearAuthenticationFlags(conn, SSP_PAIRING_ACTIVE); } } + +#ifdef ENABLE_SCO_OVER_HCI + // setup_synchronous_connection? Voice setting at offset 22 + if (IS_COMMAND(packet, hci_setup_synchronous_connection)){ + // TODO: compare to current setting if sco connection already active + hci_stack->sco_voice_setting_active = little_endian_read_16(packet, 15); + } + // accept_synchronus_connection? Voice setting at offset 18 + if (IS_COMMAND(packet, hci_accept_synchronous_connection)){ + // TODO: compare to current setting if sco connection already active + hci_stack->sco_voice_setting_active = little_endian_read_16(packet, 19); + } +#endif #endif #ifdef ENABLE_BLE diff --git a/src/hci.h b/src/hci.h index 47f5d8eb8..107e36b71 100644 --- a/src/hci.h +++ b/src/hci.h @@ -550,6 +550,10 @@ typedef enum hci_init_state{ HCI_INIT_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING, HCI_INIT_W4_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING, + // SCO over HCI Broadcom + HCI_INIT_BCM_WRITE_SCO_PCM_INT, + HCI_INIT_W4_BCM_WRITE_SCO_PCM_INT, + #ifdef ENABLE_BLE HCI_INIT_LE_READ_BUFFER_SIZE, HCI_INIT_W4_LE_READ_BUFFER_SIZE, @@ -694,6 +698,7 @@ typedef struct { uint8_t new_scan_enable_value; uint16_t sco_voice_setting; + uint16_t sco_voice_setting_active; uint8_t loopback_mode; diff --git a/src/hci_cmd.c b/src/hci_cmd.c index f85794e49..fb7db4914 100644 --- a/src/hci_cmd.c +++ b/src/hci_cmd.c @@ -1061,3 +1061,18 @@ OPCODE(OGF_LE_CONTROLLER, 0x26), "QQ" }; #endif + +// Broadcom / Cypress specific HCI commands + +/** + * @brief Configure SCO Routing (BCM) + * @param sco_routing is 0 for PCM, 1 for Transport, 2 for Codec and 3 for I2S + * @param pcm_interface_rate is 0 for 128KBps, 1 for 256 KBps, 2 for 512KBps, 3 for 1024KBps, and 4 for 2048Kbps + * @param frame_type is 0 for short and 1 for long + * @param sync_mode is 0 for slave and 1 for master + * @param clock_mode is 0 for slabe and 1 for master + */ +const hci_cmd_t hci_bcm_write_sco_pcm_int = { +OPCODE(0x3f, 0x1c), "11111" +// return: status +}; diff --git a/src/hci_cmd.h b/src/hci_cmd.h index a2ca927a2..8a0ef5381 100644 --- a/src/hci_cmd.h +++ b/src/hci_cmd.h @@ -187,6 +187,10 @@ extern const hci_cmd_t hci_le_set_scan_response_data; extern const hci_cmd_t hci_le_start_encryption; extern const hci_cmd_t hci_le_test_end; extern const hci_cmd_t hci_le_transmitter_test; + +// Broadcom / Cypress specific HCI commands +extern const hci_cmd_t hci_bcm_write_sco_pcm_int; + /** * construct HCI Command based on template * diff --git a/src/hci_transport.h b/src/hci_transport.h index 60f6f93e0..2a8cb67aa 100644 --- a/src/hci_transport.h +++ b/src/hci_transport.h @@ -104,6 +104,11 @@ typedef struct { */ void (*reset_link)(void); + /** + * extension for USB transport implementations: config SCO connections + */ + void (*set_sco_config)(uint16_t voice_setting, int num_connections); + } hci_transport_t; typedef enum { diff --git a/src/hci_transport_h4.c b/src/hci_transport_h4.c index fbdb5949b..e1f2deb26 100644 --- a/src/hci_transport_h4.c +++ b/src/hci_transport_h4.c @@ -549,6 +549,7 @@ static const hci_transport_t hci_transport_h4 = { /* int (*send_packet)(...); */ &hci_transport_h4_send_packet, /* int (*set_baudrate)(uint32_t baudrate); */ &hci_transport_h4_set_baudrate, /* void (*reset_link)(void); */ NULL, + /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL, }; // configure and return h4 singleton diff --git a/src/hci_transport_h5.c b/src/hci_transport_h5.c index 58f4b52ca..eff1cbf20 100644 --- a/src/hci_transport_h5.c +++ b/src/hci_transport_h5.c @@ -773,6 +773,7 @@ static const hci_transport_t hci_transport_h5 = { /* int (*send_packet)(...); */ &hci_transport_h5_send_packet, /* int (*set_baudrate)(uint32_t baudrate); */ &hci_transport_h5_set_baudrate, /* void (*reset_link)(void); */ &hci_transport_h5_reset_link, + /* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL, }; // configure and return h5 singleton diff --git a/test/hfp/cvsd_plc_test.c b/test/hfp/cvsd_plc_test.c index 0dbaaeb38..65849c726 100644 --- a/test/hfp/cvsd_plc_test.c +++ b/test/hfp/cvsd_plc_test.c @@ -11,10 +11,10 @@ #include "btstack_cvsd_plc.h" #include "wav_util.h" -const int audio_samples_per_frame = 24; -static int8_t audio_frame_in[audio_samples_per_frame]; +const int audio_samples_per_frame = 24; +static int16_t audio_frame_in[audio_samples_per_frame]; -static uint8_t test_data[][audio_samples_per_frame] = { +static int16_t test_data[][audio_samples_per_frame] = { { 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, { 0xff, 0xff, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05 }, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05 }, @@ -23,16 +23,6 @@ static uint8_t test_data[][audio_samples_per_frame] = { static btstack_cvsd_plc_state_t plc_state; -// input signal: pre-computed sine wave, at 8000 kz -static const uint8_t sine_uint8[] = { - 0, 15, 31, 46, 61, 74, 86, 97, 107, 114, - 120, 124, 126, 126, 124, 120, 114, 107, 97, 86, - 74, 61, 46, 31, 15, 0, 241, 225, 210, 195, - 182, 170, 159, 149, 142, 136, 132, 130, 130, 132, - 136, 142, 149, 159, 170, 182, 195, 210, 225, 241, -}; - - // input signal: pre-computed sine wave, 160 Hz at 16000 kHz static const int16_t sine_int16[] = { 0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557, @@ -47,27 +37,7 @@ static const int16_t sine_int16[] = { -19260, -17557, -15786, -13952, -12062, -10126, -8149, -6140, -4107, -2057, }; -static int phase = 0; -void sco_demo_sine_wave_int8(int num_samples, int8_t * data){ - int i; - for (i=0; i= sizeof(sine_uint8)) phase = 0; - } -} - -void sco_demo_sine_wave_int16(int num_samples, int16_t * data){ - int i; - for (i=0; i < num_samples; i++){ - data[i] = sine_int16[phase++]; - if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){ - phase = 0; - } - } -} - -static int count_equal_bytes(uint8_t * packet, uint16_t size){ +static int count_equal_samples(int16_t * packet, uint16_t size){ int count = 0; int temp_count = 1; int i; @@ -87,61 +57,120 @@ static int count_equal_bytes(uint8_t * packet, uint16_t size){ return count; } -static void create_sine_wav(const char * out_filename){ +// @assumption frame len 24 samples +static int bad_frame(int16_t * frame, uint16_t size){ + return count_equal_samples(frame, size) > 20; +} + +static void btstack_cvsd_plc_mark_bad_frame(btstack_cvsd_plc_state_t * state, int16_t * in, uint16_t size, int16_t * out){ + if (size != 24){ + printf("btstack_cvsd_plc_mark_bad_frame: audio frame size is incorrect. Expected %d, got %d\n", CVSD_FS, size); + return; + } + state->frame_count++; + + if (bad_frame(in,size)){ + memcpy(out, in, size * 2); + if (state->good_frames_nr > CVSD_LHIST/CVSD_FS){ + memset(out, 0x33, size * 2); + state->bad_frames_nr++; + } + } else { + memcpy(out, in, size); + state->good_frames_nr++; + if (state->good_frames_nr == 1){ + printf("First good frame at index %d\n", state->frame_count-1); + } + } +} + +static int phase = 0; +static void create_sine_wave_int16_data(int num_samples, int16_t * data){ + int i; + for (i=0; i < num_samples; i++){ + data[i] = sine_int16[phase++]; + phase++; + if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){ + phase = 0; + } + } +} + +static int count_equal_bytes(int16_t * packet, uint16_t size){ + int count = 0; + int temp_count = 1; + int i; + for (i = 0; i < size-1; i++){ + if (packet[i] == packet[i+1]){ + temp_count++; + continue; + } + if (count < temp_count){ + count = temp_count; + } + temp_count = 1; + } + if (temp_count > count + 1){ + count = temp_count; + } + return count; +} + +void create_sine_wav(const char * out_filename){ btstack_cvsd_plc_init(&plc_state); wav_writer_open(out_filename, 1, 8000); int i; for (i=0; i<2000; i++){ - sco_demo_sine_wave_int8(audio_samples_per_frame, audio_frame_in); - wav_writer_write_int8(audio_samples_per_frame, audio_frame_in); + create_sine_wave_int16_data(audio_samples_per_frame, audio_frame_in); + wav_writer_write_int16(audio_samples_per_frame, audio_frame_in); } wav_writer_close(); } -static void introduce_bad_frames_to_wav_file(const char * in_filename, const char * out_filename, int corruption_step){ +void introduce_bad_frames_to_wav_file(const char * in_filename, const char * out_filename, int corruption_step){ btstack_cvsd_plc_init(&plc_state); wav_writer_open(out_filename, 1, 8000); wav_reader_open(in_filename); int fc = 0; - while (wav_reader_read_int8(audio_samples_per_frame, audio_frame_in)){ + while (wav_reader_read_int16(audio_samples_per_frame, audio_frame_in)){ if (corruption_step > 0 && fc >= corruption_step && fc%corruption_step == 0){ - memset(audio_frame_in, 50, audio_samples_per_frame); + memset(audio_frame_in, 50, audio_samples_per_frame * 2); } - wav_writer_write_int8(audio_samples_per_frame, audio_frame_in); + wav_writer_write_int16(audio_samples_per_frame, audio_frame_in); fc++; } wav_reader_close(); wav_writer_close(); } -static void process_wav_file_with_plc(const char * in_filename, const char * out_filename){ +void process_wav_file_with_plc(const char * in_filename, const char * out_filename){ // printf("\nProcess %s -> %s\n", in_filename, out_filename); btstack_cvsd_plc_init(&plc_state); wav_writer_open(out_filename, 1, 8000); wav_reader_open(in_filename); - while (wav_reader_read_int8(audio_samples_per_frame, audio_frame_in)){ - int8_t audio_frame_out[audio_samples_per_frame]; + while (wav_reader_read_int16(audio_samples_per_frame, audio_frame_in)){ + int16_t audio_frame_out[audio_samples_per_frame]; btstack_cvsd_plc_process_data(&plc_state, audio_frame_in, audio_samples_per_frame, audio_frame_out); - wav_writer_write_int8(audio_samples_per_frame, audio_frame_out); + wav_writer_write_int16(audio_samples_per_frame, audio_frame_out); } wav_reader_close(); wav_writer_close(); btstack_cvsd_dump_statistics(&plc_state); } -static void mark_bad_frames_wav_file(const char * in_filename, const char * out_filename){ +void mark_bad_frames_wav_file(const char * in_filename, const char * out_filename){ // printf("\nMark bad frame %s -> %s\n", in_filename, out_filename); btstack_cvsd_plc_init(&plc_state); CHECK_EQUAL(wav_writer_open(out_filename, 1, 8000), 0); CHECK_EQUAL(wav_reader_open(in_filename), 0); - while (wav_reader_read_int8(audio_samples_per_frame, audio_frame_in)){ - int8_t audio_frame_out[audio_samples_per_frame]; + while (wav_reader_read_int16(audio_samples_per_frame, audio_frame_in)){ + int16_t audio_frame_out[audio_samples_per_frame]; btstack_cvsd_plc_mark_bad_frame(&plc_state, audio_frame_in, audio_samples_per_frame, audio_frame_out); - wav_writer_write_int8(audio_samples_per_frame, audio_frame_out); + wav_writer_write_int16(audio_samples_per_frame, audio_frame_out); } wav_reader_close(); wav_writer_close(); @@ -161,8 +190,8 @@ TEST(CVSD_PLC, CountEqBytes){ TEST(CVSD_PLC, TestLiveWavFile){ int corruption_step = 10; - introduce_bad_frames_to_wav_file("data/sco_input.wav", "results/sco_input.wav", 0); - introduce_bad_frames_to_wav_file("data/sco_input.wav", "results/sco_input_with_bad_frames.wav", corruption_step); + introduce_bad_frames_to_wav_file("data/sco_input-16bit.wav", "results/sco_input.wav", 0); + introduce_bad_frames_to_wav_file("data/sco_input-16bit.wav", "results/sco_input_with_bad_frames.wav", corruption_step); mark_bad_frames_wav_file("results/sco_input.wav", "results/sco_input_detected_frames.wav"); process_wav_file_with_plc("results/sco_input.wav", "results/sco_input_after_plc.wav"); diff --git a/test/hfp/data/sco_input-16bit.wav b/test/hfp/data/sco_input-16bit.wav new file mode 100644 index 000000000..b002632bb Binary files /dev/null and b/test/hfp/data/sco_input-16bit.wav differ diff --git a/test/hfp/data/sco_input.wav b/test/hfp/data/sco_input-8bit.wav similarity index 100% rename from test/hfp/data/sco_input.wav rename to test/hfp/data/sco_input-8bit.wav diff --git a/test/ring_buffer/btstack_ring_buffer_test.c b/test/ring_buffer/btstack_ring_buffer_test.c index eee95e6ca..1074eaeb7 100644 --- a/test/ring_buffer/btstack_ring_buffer_test.c +++ b/test/ring_buffer/btstack_ring_buffer_test.c @@ -1,9 +1,14 @@ #include "CppUTest/TestHarness.h" #include "CppUTest/CommandLineTestRunner.h" #include "btstack_ring_buffer.h" +#include "btstack_util.h" static uint8_t storage[10]; +uint32_t btstack_min(uint32_t a, uint32_t b){ + return a < b ? a : b; +} + TEST_GROUP(RingBuffer){ btstack_ring_buffer_t ring_buffer; int storage_size; diff --git a/tool/bluetooth_data_types.py b/tool/bluetooth_data_types.py index 55e8688a3..1161c7e2d 100755 --- a/tool/bluetooth_data_types.py +++ b/tool/bluetooth_data_types.py @@ -48,7 +48,7 @@ def scrape_page(fout, url): print('-' * 70) # get all elements in - rows = tree.xpath('//div[@class="table-time"]/table/tbody/tr') + rows = tree.xpath('//div[@class="copy-block ta-left"]/table/tbody/tr') for row in rows: children = row.getchildren() data_type_value = children[0].text_content() diff --git a/tool/bluetooth_gatt.py b/tool/bluetooth_gatt.py index f2648eda0..256767db5 100755 --- a/tool/bluetooth_gatt.py +++ b/tool/bluetooth_gatt.py @@ -40,7 +40,7 @@ def scrape_page(fout, url): page = requests.get(url) tree = html.fromstring(page.content) # get all elements in
- rows = tree.xpath('//table[@id="gattTable"]/tr') + rows = tree.xpath('//table[@id="gattTable"]/tbody/tr') for row in rows: children = row.getchildren() summary = children[0].text_content() diff --git a/tool/convert_gatt_service.py b/tool/convert_gatt_service.py index f851068a3..fc44e6412 100755 --- a/tool/convert_gatt_service.py +++ b/tool/convert_gatt_service.py @@ -38,7 +38,7 @@ def list_services(): page = requests.get(url) tree = html.fromstring(page.content) # get all elements in
- rows = tree.xpath('//table[@id="gattTable"]/tr') + rows = tree.xpath('//table[@id="gattTable"]/tbody/tr') print("%-55s| %-30s| %s" % ('Specification Type', 'Specification Name', 'UUID')) print('-'*55 + '+-' + '-' * 30 + '+-' + '-'*10) maxlen_type = 0 @@ -65,9 +65,14 @@ def parse_properties(element): property_list_human = [] for property in properties: property_name = property.tag + if property_name == "WriteWithoutResponse": + property_name = "Write_Without_Response" + if property_name == "InformationText": + continue property_requirement = property.text - if (property_requirement != "Excluded"): - property_list_human.append(property_name) + if (property_requirement == "Excluded"): + continue + property_list_human.append(property_name) return (property_list_human) print("Characteristic %s - properties %s" % (name, property_list_human)) @@ -163,7 +168,8 @@ def convert_service(fout, specification_type): continue if (type == 'org.bluetooth.descriptor.report_reference'): - print('-- Descriptor %-40s - WARNING: REPORT_REFERENCE not supported yet' % name) + print('-- Descriptor %-40s' % name) + fout.write('REPORT_REFERENCE, %s,\n' % properties) continue if (type == 'org.bluetooth.descriptor.number_of_digitals'):