mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-29 13:20:39 +00:00
Merge branch 'develop' into a2dp
This commit is contained in:
commit
6d5e5836ab
@ -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
|
||||
|
@ -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:
|
||||
|
@ -39,7 +39,6 @@
|
||||
* sco_demo_util.c - send/receive test data via SCO, used by hfp_*_demo and hsp_*_demo
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#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 <portaudio.h>
|
||||
#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<num_samples; i++){
|
||||
data[i] = (int8_t)sine_uint8[phase];
|
||||
phase++;
|
||||
if (phase >= 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<audio_samples_per_packet;j++){
|
||||
for (j=0;j<sco_payload_length;j++){
|
||||
sco_packet[3+j] = phase++;
|
||||
}
|
||||
#endif
|
||||
#if SCO_DEMO_MODE == SCO_DEMO_MODE_55
|
||||
int j;
|
||||
for (j=0;j<audio_samples_per_packet;j++){
|
||||
for (j=0;j<sco_payload_length;j++){
|
||||
// sco_packet[3+j] = j & 1 ? 0x35 : 0x53;
|
||||
sco_packet[3+j] = 0x55;
|
||||
}
|
||||
#endif
|
||||
#if SCO_DEMO_MODE == SCO_DEMO_MODE_00
|
||||
int j;
|
||||
for (j=0;j<audio_samples_per_packet;j++){
|
||||
for (j=0;j<sco_payload_length;j++){
|
||||
sco_packet[3+j] = 0x00;
|
||||
}
|
||||
// additional hack
|
||||
@ -533,6 +658,14 @@ void sco_demo_send(hci_con_handle_t sco_handle){
|
||||
(void) phase;
|
||||
#endif
|
||||
|
||||
// test silence
|
||||
// memset(sco_packet+3, 0, sco_payload_length);
|
||||
|
||||
// set handle + flags
|
||||
little_endian_store_16(sco_packet, 0, sco_handle);
|
||||
// set len
|
||||
sco_packet[2] = sco_payload_length;
|
||||
// finally send packet
|
||||
hci_send_sco_packet_buffer(sco_packet_length);
|
||||
|
||||
// request another send event
|
||||
@ -575,15 +708,18 @@ void sco_demo_receive(uint8_t * packet, uint16_t size){
|
||||
packets = 0;
|
||||
}
|
||||
|
||||
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
|
||||
#ifdef SCO_WAV_FILENAME
|
||||
if (negotiated_codec == HFP_CODEC_MSBC){
|
||||
sco_demo_receive_mSBC(packet, size);
|
||||
} else {
|
||||
sco_demo_receive_CVSD(packet, size);
|
||||
#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
|
||||
switch (negotiated_codec){
|
||||
case HFP_CODEC_MSBC:
|
||||
sco_demo_receive_mSBC(packet, size);
|
||||
break;
|
||||
case HFP_CODEC_CVSD:
|
||||
sco_demo_receive_CVSD(packet, size);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dump_data = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (packet[1] & 0x30){
|
||||
@ -617,7 +753,7 @@ void sco_demo_receive(uint8_t * packet, uint16_t size){
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
#if SCO_DEMO_MODE == SCO_DEMO_MODE_55 || SCO_DEMO_MODE_00
|
||||
#if SCO_DEMO_MODE == SCO_DEMO_MODE_55 || SCO_DEMO_MODE == SCO_DEMO_MODE_00
|
||||
int i;
|
||||
int contains_error = 0;
|
||||
for (i=3;i<size;i++){
|
||||
|
@ -85,17 +85,29 @@
|
||||
// 4: Two 8 kHz voice channels with 16-bit encoding or one 16 kHz voice channel with 16-bit encoding
|
||||
// 5: Three 8 kHz voice channels with 16-bit encoding or one 8 kHz voice channel with 16-bit encoding and one 16 kHz voice channel with 16-bit encoding
|
||||
// --> 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;c<EVENT_IN_BUFFER_COUNT;c++){
|
||||
if (transfer == event_in_transfer[c]){
|
||||
libusb_free_transfer(transfer);
|
||||
event_in_transfer[c] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (c=0;c<ACL_IN_BUFFER_COUNT;c++){
|
||||
if (transfer == acl_in_transfer[c]){
|
||||
libusb_free_transfer(transfer);
|
||||
acl_in_transfer[c] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_SCO_OVER_HCI
|
||||
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED || sco_shutdown) {
|
||||
for (c=0;c<SCO_IN_BUFFER_COUNT;c++){
|
||||
if (transfer == sco_in_transfer[c]){
|
||||
libusb_free_transfer(transfer);
|
||||
@ -272,8 +279,24 @@ LIBUSB_CALL static void async_callback(struct libusb_transfer *transfer){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED) {
|
||||
for (c=0;c<EVENT_IN_BUFFER_COUNT;c++){
|
||||
if (transfer == event_in_transfer[c]){
|
||||
libusb_free_transfer(transfer);
|
||||
event_in_transfer[c] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (c=0;c<ACL_IN_BUFFER_COUNT;c++){
|
||||
if (transfer == acl_in_transfer[c]){
|
||||
libusb_free_transfer(transfer);
|
||||
acl_in_transfer[c] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -327,9 +350,10 @@ static int usb_send_sco_packet(uint8_t *packet, int size){
|
||||
memcpy(data, packet, size);
|
||||
|
||||
// setup transfer
|
||||
// log_info("usb_send_sco_packet: size %u, max size %u, iso packet size %u", size, NUM_ISO_PACKETS * iso_packet_size, iso_packet_size);
|
||||
struct libusb_transfer * sco_transfer = sco_out_transfers[tranfer_index];
|
||||
libusb_fill_iso_transfer(sco_transfer, handle, sco_out_addr, data, size, NUM_ISO_PACKETS, async_callback, NULL, 0);
|
||||
libusb_set_iso_packet_lengths(sco_transfer, ISO_PACKET_SIZE);
|
||||
libusb_fill_iso_transfer(sco_transfer, handle, sco_out_addr, data, NUM_ISO_PACKETS * iso_packet_size, NUM_ISO_PACKETS, async_callback, NULL, 0);
|
||||
libusb_set_iso_packet_lengths(sco_transfer, iso_packet_size);
|
||||
r = libusb_submit_transfer(sco_transfer);
|
||||
if (r < 0) {
|
||||
log_error("Error submitting sco transfer, %d", r);
|
||||
@ -724,13 +748,6 @@ static int prepare_device(libusb_device_handle * aHandle){
|
||||
libusb_close(aHandle);
|
||||
return r;
|
||||
}
|
||||
log_info("Switching to setting %u on interface 1..", ALT_SETTING);
|
||||
r = libusb_set_interface_alt_setting(aHandle, 1, ALT_SETTING);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "Error setting alternative setting %u for interface 1: %s\n", ALT_SETTING, libusb_error_name(r));
|
||||
libusb_close(aHandle);
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
@ -760,14 +777,131 @@ static libusb_device_handle * try_open_device(libusb_device * device){
|
||||
return dev_handle;
|
||||
}
|
||||
|
||||
static int usb_open(void){
|
||||
int r;
|
||||
|
||||
#ifdef ENABLE_SCO_OVER_HCI
|
||||
|
||||
static int usb_sco_start(void){
|
||||
|
||||
printf("usb_sco_start\n");
|
||||
log_info("usb_sco_start");
|
||||
|
||||
sco_state_machine_init();
|
||||
sco_ring_init();
|
||||
|
||||
int alt_setting;
|
||||
if (sco_voice_setting & 0x0020){
|
||||
// 16-bit PCM
|
||||
alt_setting = alt_setting_16_bit[sco_num_connections-1];
|
||||
} else {
|
||||
// 8-bit PCM or mSBC
|
||||
alt_setting = alt_setting_8_bit[sco_num_connections-1];
|
||||
}
|
||||
// derive iso packet size from alt setting
|
||||
iso_packet_size = iso_packet_size_for_alt_setting[alt_setting];
|
||||
|
||||
log_info("Switching to setting %u on interface 1..", alt_setting);
|
||||
int r = libusb_set_interface_alt_setting(handle, 1, alt_setting);
|
||||
if (r < 0) {
|
||||
log_error("Error setting alternative setting %u for interface 1: %s\n", alt_setting, libusb_error_name(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
// incoming
|
||||
int c;
|
||||
for (c = 0 ; c < SCO_IN_BUFFER_COUNT ; c++) {
|
||||
sco_in_transfer[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // isochronous transfers SCO in
|
||||
if (!sco_in_transfer[c]) {
|
||||
usb_close();
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
// configure sco_in handlers
|
||||
libusb_fill_iso_transfer(sco_in_transfer[c], handle, sco_in_addr,
|
||||
hci_sco_in_buffer[c], NUM_ISO_PACKETS * iso_packet_size, NUM_ISO_PACKETS, async_callback, NULL, 0);
|
||||
libusb_set_iso_packet_lengths(sco_in_transfer[c], iso_packet_size);
|
||||
r = libusb_submit_transfer(sco_in_transfer[c]);
|
||||
if (r) {
|
||||
log_error("Error submitting isochronous in transfer %d", r);
|
||||
usb_close();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// outgoing
|
||||
for (c=0; c < SCO_OUT_BUFFER_COUNT ; c++){
|
||||
sco_out_transfers[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // 1 isochronous transfers SCO out - up to 3 parts
|
||||
sco_out_transfers_in_flight[c] = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_sco_stop(void){
|
||||
|
||||
printf("usb_sco_stop\n");
|
||||
|
||||
log_info("usb_sco_stop");
|
||||
sco_shutdown = 1;
|
||||
|
||||
libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_ERROR);
|
||||
|
||||
int c;
|
||||
for (c = 0 ; c < SCO_IN_BUFFER_COUNT ; c++) {
|
||||
libusb_cancel_transfer(sco_in_transfer[c]);
|
||||
}
|
||||
|
||||
for (c = 0; c < SCO_OUT_BUFFER_COUNT ; c++){
|
||||
if (sco_out_transfers_in_flight[c]) {
|
||||
libusb_cancel_transfer(sco_out_transfers[c]);
|
||||
} else {
|
||||
libusb_free_transfer(sco_out_transfers[c]);
|
||||
sco_out_transfers[c] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// wait until all transfers are completed
|
||||
int completed = 0;
|
||||
while (!completed){
|
||||
struct timeval tv;
|
||||
memset(&tv, 0, sizeof(struct timeval));
|
||||
libusb_handle_events_timeout(NULL, &tv);
|
||||
// check if all done
|
||||
completed = 1;
|
||||
|
||||
// Cancel all synchronous transfer
|
||||
for (c = 0 ; c < SCO_IN_BUFFER_COUNT ; c++) {
|
||||
if (sco_in_transfer[c]){
|
||||
completed = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!completed) continue;
|
||||
|
||||
for (c=0; c < SCO_OUT_BUFFER_COUNT ; c++){
|
||||
if (sco_out_transfers[c]){
|
||||
completed = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sco_shutdown = 0;
|
||||
libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_WARNING);
|
||||
|
||||
log_info("Switching to setting %u on interface 1..", 0);
|
||||
int r = libusb_set_interface_alt_setting(handle, 1, 0);
|
||||
if (r < 0) {
|
||||
log_error("Error setting alternative setting %u for interface 1: %s", 0, libusb_error_name(r));
|
||||
return;
|
||||
}
|
||||
|
||||
printf("usb_sco_stop done\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
static int usb_open(void){
|
||||
int r;
|
||||
|
||||
handle_packet = NULL;
|
||||
|
||||
// default endpoint addresses
|
||||
@ -900,36 +1034,6 @@ static int usb_open(void){
|
||||
|
||||
libusb_state = LIB_USB_TRANSFERS_ALLOCATED;
|
||||
|
||||
#ifdef ENABLE_SCO_OVER_HCI
|
||||
|
||||
// incoming
|
||||
for (c = 0 ; c < SCO_IN_BUFFER_COUNT ; c++) {
|
||||
sco_in_transfer[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // isochronous transfers SCO in
|
||||
log_info("Alloc iso transfer");
|
||||
if (!sco_in_transfer[c]) {
|
||||
usb_close();
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
// configure sco_in handlers
|
||||
libusb_fill_iso_transfer(sco_in_transfer[c], handle, sco_in_addr,
|
||||
hci_sco_in_buffer[c], SCO_PACKET_SIZE, NUM_ISO_PACKETS, async_callback, NULL, 0);
|
||||
libusb_set_iso_packet_lengths(sco_in_transfer[c], ISO_PACKET_SIZE);
|
||||
r = libusb_submit_transfer(sco_in_transfer[c]);
|
||||
log_info("Submit iso transfer res = %d", r);
|
||||
if (r) {
|
||||
log_error("Error submitting isochronous in transfer %d", r);
|
||||
usb_close();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// outgoing
|
||||
for (c=0; c < SCO_OUT_BUFFER_COUNT ; c++){
|
||||
sco_out_transfers[c] = libusb_alloc_transfer(NUM_ISO_PACKETS); // 1 isochronous transfers SCO out - up to 3 parts
|
||||
sco_out_transfers_in_flight[c] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (c = 0 ; c < EVENT_IN_BUFFER_COUNT ; c++) {
|
||||
// configure event_in handlers
|
||||
libusb_fill_interrupt_transfer(event_in_transfer[c], handle, event_in_addr,
|
||||
@ -993,7 +1097,6 @@ static int usb_open(void){
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int usb_close(void){
|
||||
int c;
|
||||
switch (libusb_state){
|
||||
@ -1035,10 +1138,8 @@ static int usb_close(void){
|
||||
}
|
||||
for (c = 0; c < SCO_OUT_BUFFER_COUNT ; c++){
|
||||
if (sco_out_transfers_in_flight[c]) {
|
||||
log_info("libusb_cancel_transfer sco_out_transfers[%d]", c);
|
||||
libusb_cancel_transfer(sco_out_transfers[c]);
|
||||
} else {
|
||||
log_info("libusb_free_transfer sco_out_transfers[%d]", c);
|
||||
libusb_free_transfer(sco_out_transfers[c]);
|
||||
sco_out_transfers[c] = 0;
|
||||
}
|
||||
@ -1197,6 +1298,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
|
||||
|
||||
static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
|
||||
log_info("registering packet handler");
|
||||
packet_handler = handler;
|
||||
@ -1219,6 +1337,9 @@ const hci_transport_t * hci_transport_usb_instance(void) {
|
||||
hci_transport_usb->register_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;
|
||||
}
|
||||
|
@ -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<usb_interface_descriptor.bNumEndpoints;i++){
|
||||
WINUSB_PIPE_INFORMATION_EX pipe;
|
||||
result = WinUsb_QueryPipeEx(
|
||||
usb_interface_1_handle,
|
||||
ALT_SETTING,
|
||||
alt_setting,
|
||||
(UCHAR) i,
|
||||
&pipe);
|
||||
if (!result) goto exit_on_error;
|
||||
log_info("Interface #1, Alt #%u, Pipe idx #%u: type %u, id 0x%02x, max packet size %u, interval %u, max bytes per interval %u",
|
||||
ALT_SETTING, i, pipe.PipeType, pipe.PipeId, pipe.MaximumPacketSize, pipe.Interval, (int) pipe.MaximumBytesPerInterval);
|
||||
alt_setting, i, pipe.PipeType, pipe.PipeId, pipe.MaximumPacketSize, pipe.Interval, (int) pipe.MaximumBytesPerInterval);
|
||||
switch (pipe.PipeType){
|
||||
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
|
||||
if (pipe.PipeId & 0x80) {
|
||||
@ -766,6 +820,88 @@ exit_on_error:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SCO_OVER_HCI
|
||||
|
||||
static int usb_sco_start(void){
|
||||
printf("usb_sco_start\n");
|
||||
log_info("usb_sco_start");
|
||||
|
||||
sco_shutdown = 0;
|
||||
|
||||
sco_state_machine_init();
|
||||
sco_ring_init();
|
||||
|
||||
// calc alt setting
|
||||
int alt_setting;
|
||||
if (sco_voice_setting & 0x0020){
|
||||
// 16-bit PCM
|
||||
alt_setting = alt_setting_16_bit[sco_num_connections-1];
|
||||
} else {
|
||||
// 8-bit PCM or mSBC
|
||||
alt_setting = alt_setting_8_bit[sco_num_connections-1];
|
||||
}
|
||||
|
||||
log_info("Switching to setting %u on interface 1..", alt_setting);
|
||||
// WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds.
|
||||
BOOL result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, alt_setting);
|
||||
if (!result) goto exit_on_error;
|
||||
|
||||
// derive iso packet size from alt setting
|
||||
iso_packet_size = iso_packet_size_for_alt_setting[alt_setting];
|
||||
|
||||
// register isochronous buffer after setting alternate setting
|
||||
usb_sco_register_buffers();
|
||||
|
||||
#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
|
||||
// get current frame number
|
||||
ULONG current_frame_number;
|
||||
LARGE_INTEGER timestamp;
|
||||
WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp);
|
||||
// plan for next tranfer
|
||||
sco_next_transfer_at_frame = current_frame_number + ISOC_BUFFERS * NUM_ISO_PACKETS;
|
||||
#endif
|
||||
|
||||
int i;
|
||||
for (i=0;i<ISOC_BUFFERS;i++){
|
||||
#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
|
||||
usb_submit_sco_in_transfer_at_frame(i, &sco_next_transfer_at_frame);
|
||||
#else
|
||||
usb_submit_sco_in_transfer_asap(i, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
usb_sco_in_expected_transfer = 0;
|
||||
|
||||
// only await first transfer to return
|
||||
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[usb_sco_in_expected_transfer], DATA_SOURCE_CALLBACK_READ);
|
||||
return 1;
|
||||
|
||||
exit_on_error:
|
||||
log_error("usb_sco_start: last error %lu", GetLastError());
|
||||
usb_free_resources();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_sco_stop(void){
|
||||
printf("usb_sco_stop\n");
|
||||
log_info("usb_sco_stop");
|
||||
|
||||
sco_shutdown = 1;
|
||||
|
||||
// abort SCO transfers
|
||||
WinUsb_AbortPipe(usb_interface_0_handle, sco_in_addr);
|
||||
WinUsb_AbortPipe(usb_interface_0_handle, sco_out_addr);
|
||||
|
||||
// unlock/free SCO buffers
|
||||
usb_sco_unregister_buffers();
|
||||
|
||||
int alt_setting = 0;
|
||||
log_info("Switching to setting %u on interface 1..", alt_setting);
|
||||
// WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds.
|
||||
WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, alt_setting);
|
||||
}
|
||||
#endif
|
||||
|
||||
// returns 0 if successful, -1 otherwise
|
||||
static int usb_try_open_device(const char * device_path){
|
||||
|
||||
@ -810,11 +946,6 @@ static int usb_try_open_device(const char * device_path){
|
||||
result = WinUsb_GetAssociatedInterface(usb_interface_0_handle, 0, &usb_interface_1_handle);
|
||||
if (!result) goto exit_on_error;
|
||||
log_info("Claiming interface 1: success");
|
||||
|
||||
log_info("Switching to setting %u on interface 1..", ALT_SETTING);
|
||||
// WinUsb_SetCurrentAlternateSetting returns TRUE if the operation succeeds.
|
||||
result = WinUsb_SetCurrentAlternateSetting(usb_interface_1_handle, ALT_SETTING);
|
||||
if (!result) goto exit_on_error;
|
||||
#endif
|
||||
|
||||
result = usb_scan_for_bluetooth_endpoints();
|
||||
@ -830,14 +961,6 @@ static int usb_try_open_device(const char * device_path){
|
||||
memset(hci_sco_packet_descriptors, 0, sizeof(hci_sco_packet_descriptors));
|
||||
log_info("Size of packet descriptors for SCO IN%u", (int) sizeof(hci_sco_packet_descriptors));
|
||||
|
||||
result = WinUsb_RegisterIsochBuffer(usb_interface_1_handle, sco_in_addr, hci_sco_in_buffer, ISOC_BUFFERS * SCO_PACKET_SIZE, &hci_sco_in_buffer_handle);
|
||||
if (!result) goto exit_on_error;
|
||||
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) goto exit_on_error;
|
||||
log_info("hci_sco_out_buffer_handle %p", hci_sco_out_buffer_handle);
|
||||
|
||||
// setup async io && btstack handler
|
||||
memset(&usb_overlapped_sco_in, 0, sizeof(usb_overlapped_sco_in));
|
||||
for (i=0;i<ISOC_BUFFERS;i++){
|
||||
@ -886,70 +1009,9 @@ static int usb_try_open_device(const char * device_path){
|
||||
btstack_run_loop_set_data_source_handler(&usb_data_source_acl_out, &usb_process_acl_out);
|
||||
btstack_run_loop_add_data_source(&usb_data_source_acl_out);
|
||||
|
||||
// submit all incoming transfers
|
||||
usb_submit_event_in_transfer();
|
||||
usb_submit_acl_in_transfer();
|
||||
|
||||
#ifdef ENABLE_SCO_OVER_HCI
|
||||
|
||||
#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
|
||||
// get current frame number
|
||||
ULONG current_frame_number;
|
||||
LARGE_INTEGER timestamp;
|
||||
WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp);
|
||||
|
||||
// plan for next tranfer
|
||||
sco_next_transfer_at_frame = current_frame_number + ISOC_BUFFERS * NUM_ISO_PACKETS;
|
||||
#endif
|
||||
|
||||
for (i=0;i<ISOC_BUFFERS;i++){
|
||||
#ifdef SCHEDULE_SCO_IN_TRANSFERS_MANUALLY
|
||||
usb_submit_sco_in_transfer_at_frame(i, &sco_next_transfer_at_frame);
|
||||
#else
|
||||
usb_submit_sco_in_transfer_asap(i, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
usb_sco_in_expected_transfer = 0;
|
||||
|
||||
// only await first transfer to return
|
||||
btstack_run_loop_enable_data_source_callbacks(&usb_data_source_sco_in[usb_sco_in_expected_transfer], DATA_SOURCE_CALLBACK_READ);
|
||||
|
||||
#if 0
|
||||
// AUTO_CLEAR_STALL, RAW_IO, IGNORE_SHORT_PACKETS is not callable for ISO EP
|
||||
uint8_t value = 0;
|
||||
ULONG value_len = 1;
|
||||
result = WinUsb_GetPipePolicy(usb_interface_0_handle, event_in_addr, IGNORE_SHORT_PACKETS, &value_len, &value);
|
||||
if (!result) goto exit_on_error;
|
||||
log_info("IGNORE_SHORT_PACKETS = %u", value);
|
||||
|
||||
uint8_t value_on = 0;
|
||||
result = WinUsb_SetPipePolicy(usb_interface_1_handle, sco_in_addr, IGNORE_SHORT_PACKETS, sizeof(value_on), &value_on);
|
||||
if (!result) goto exit_on_error;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
while (1){
|
||||
//
|
||||
DWORD bytes_transferred;
|
||||
BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_in[usb_sco_in_expected_transfer], &bytes_transferred, TRUE);
|
||||
|
||||
// WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp);
|
||||
|
||||
// log_info("WinUsb_GetOverlappedResult[%02u]: ok = %u, current frame %lu", usb_sco_in_expected_transfer, ok, current_frame_number);
|
||||
if (!ok){
|
||||
log_error("WinUsb_GetOverlappedResult res %x", (int) GetLastError());
|
||||
// return 0;
|
||||
}
|
||||
// update expected and wait for completion
|
||||
usb_sco_in_expected_transfer = (usb_sco_in_expected_transfer + 1) % ISOC_BUFFERS;
|
||||
|
||||
usb_submit_sco_in_transfer_at_frame(usb_sco_in_expected_transfer, &sco_next_transfer_at_frame);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// submit all incoming transfers
|
||||
usb_submit_event_in_transfer();
|
||||
usb_submit_acl_in_transfer();
|
||||
return 1;
|
||||
|
||||
exit_on_error:
|
||||
@ -1131,8 +1193,7 @@ static int usb_close(void){
|
||||
WinUsb_AbortPipe(usb_interface_0_handle, acl_in_addr);
|
||||
WinUsb_AbortPipe(usb_interface_0_handle, acl_out_addr);
|
||||
#ifdef ENABLE_SCO_OVER_HCI
|
||||
WinUsb_AbortPipe(usb_interface_0_handle, sco_in_addr);
|
||||
WinUsb_AbortPipe(usb_interface_0_handle, sco_out_addr);
|
||||
usb_sco_stop();
|
||||
#endif
|
||||
usb_acl_out_active = 0;
|
||||
|
||||
@ -1232,7 +1293,7 @@ static int usb_send_sco_packet(uint8_t *packet, int size){
|
||||
// setup transfer
|
||||
int continue_stream = sco_ring_transfers_active > 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) {
|
||||
|
@ -42,8 +42,10 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -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;i<CVSD_OLAL;i++){
|
||||
val = sf*plc_state->hist[plc_state->bestlag+i];
|
||||
plc_state->hist[CVSD_LHIST+i] = crop_to_int8(val);
|
||||
plc_state->hist[CVSD_LHIST+i] = crop_sample(val);
|
||||
}
|
||||
|
||||
for (;i<CVSD_FS;i++){
|
||||
val = sf*plc_state->hist[plc_state->bestlag+i];
|
||||
plc_state->hist[CVSD_LHIST+i] = crop_to_int8(val);
|
||||
plc_state->hist[CVSD_LHIST+i] = crop_sample(val);
|
||||
}
|
||||
|
||||
for (;i<CVSD_FS+CVSD_OLAL;i++){
|
||||
float left = sf*plc_state->hist[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 (;i<CVSD_FS+CVSD_RT+CVSD_OLAL;i++){
|
||||
@ -187,7 +188,7 @@ 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){
|
||||
void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, SAMPLE_FORMAT *in, SAMPLE_FORMAT *out){
|
||||
float val;
|
||||
int i = 0;
|
||||
if (plc_state->nbf>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);
|
||||
|
@ -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
|
||||
|
@ -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;n<SBC_N;n++){
|
||||
Cn = CrossCorrelation(&y[SBC_LHIST-SBC_M] /* x */, &y[n]);
|
||||
Cn = CrossCorrelation(&y[SBC_LHIST-SBC_M], &y[n]);
|
||||
if (Cn>maxCn){
|
||||
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;i<SBC_OLAL;i++){
|
||||
float left = ZIRbuf[i];
|
||||
float right = sf*plc_state->hist[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 (;i<SBC_FS;i++){
|
||||
val = sf*plc_state->hist[plc_state->bestlag+i];
|
||||
plc_state->hist[SBC_LHIST+i] = crop_to_int16(val);
|
||||
plc_state->hist[SBC_LHIST+i] = crop_sample(val);
|
||||
}
|
||||
|
||||
for (;i<SBC_FS+SBC_OLAL;i++){
|
||||
float left = sf*plc_state->hist[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 (;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
|
||||
plc_state->hist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
|
||||
}
|
||||
|
||||
} else {
|
||||
for (i=0;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
|
||||
for (;i<SBC_FS+SBC_RT+SBC_OLAL;i++){
|
||||
plc_state->hist[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;i<SBC_LHIST+SBC_RT+SBC_OLAL;i++){
|
||||
plc_state->hist[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 (;i<SBC_RT+SBC_OLAL;i++){
|
||||
for (i = SBC_RT;i<SBC_RT+SBC_OLAL;i++){
|
||||
float left = plc_state->hist[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 (;i<SBC_FS;i++){
|
||||
out[i] = in[i];
|
||||
}
|
||||
|
||||
/*Copy the output to the history buffer */
|
||||
// Copy the output to the history buffer
|
||||
for (i=0;i<SBC_FS;i++){
|
||||
plc_state->hist[SBC_LHIST+i] = out[i];
|
||||
}
|
||||
/* shift the history buffer */
|
||||
// shift the history buffer
|
||||
for (i=0;i<SBC_LHIST;i++){
|
||||
plc_state->hist[i] = plc_state->hist[i+SBC_FS];
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
71
src/hci.c
71
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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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<num_samples; i++){
|
||||
data[i] = (int8_t)sine_uint8[phase];
|
||||
phase++;
|
||||
if (phase >= 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");
|
||||
|
BIN
test/hfp/data/sco_input-16bit.wav
Normal file
BIN
test/hfp/data/sco_input-16bit.wav
Normal file
Binary file not shown.
@ -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;
|
||||
|
@ -48,7 +48,7 @@ def scrape_page(fout, url):
|
||||
print('-' * 70)
|
||||
|
||||
# get all <tr> elements in <table id="gattTable">
|
||||
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()
|
||||
|
@ -40,7 +40,7 @@ def scrape_page(fout, url):
|
||||
page = requests.get(url)
|
||||
tree = html.fromstring(page.content)
|
||||
# get all <tr> elements in <table id="gattTable">
|
||||
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()
|
||||
|
@ -38,7 +38,7 @@ def list_services():
|
||||
page = requests.get(url)
|
||||
tree = html.fromstring(page.content)
|
||||
# get all <tr> elements in <table id="gattTable">
|
||||
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'):
|
||||
|
Loading…
x
Reference in New Issue
Block a user