mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-04-07 16:20:19 +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
|
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 UART | Dual mode | H4, H5 | rarely | Maybe | No | Maybe (3) | bcm | Max UART baudrate 3 mbps
|
||||||
Broadcom USB Dongles | Dual mode | USB | Yes | No (didn't work) | No | No | bcm |
|
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 UART | Dual mode | H4, H5 | rarely | No (didn't work) | No | No | csr |
|
||||||
CSR USB Dongles | Dual mode | USB | Mostly | Yes | 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
|
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;
|
break;
|
||||||
case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED:
|
case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED:
|
||||||
printf("\n** Audio connection released **\n");
|
printf("Audio connection released\n");
|
||||||
sco_handle = 0;
|
sco_handle = 0;
|
||||||
|
sco_demo_close();
|
||||||
break;
|
break;
|
||||||
case HFP_SUBEVENT_START_RINGINIG:
|
case HFP_SUBEVENT_START_RINGINIG:
|
||||||
printf("\n** Start Ringing **\n");
|
printf("Start Ringing\n");
|
||||||
break;
|
break;
|
||||||
case HFP_SUBEVENT_STOP_RINGINIG:
|
case HFP_SUBEVENT_STOP_RINGINIG:
|
||||||
printf("\n** Stop Ringing **\n");
|
printf("Stop Ringing\n");
|
||||||
break;
|
break;
|
||||||
case HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER:
|
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
|
// validate number
|
||||||
if ( strcmp("1234567", hfp_subevent_place_call_with_number_get_number(event)) == 0
|
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
|
|| 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;
|
break;
|
||||||
|
|
||||||
case HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG:
|
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");
|
hfp_ag_send_phone_number_for_voice_tag(acl_handle, "1234567");
|
||||||
break;
|
break;
|
||||||
case HFP_SUBEVENT_TRANSMIT_DTMF_CODES:
|
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);
|
hfp_ag_send_dtmf_code_done(acl_handle);
|
||||||
break;
|
break;
|
||||||
case HFP_SUBEVENT_CALL_ANSWERED:
|
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
|
* sco_demo_util.c - send/receive test data via SCO, used by hfp_*_demo and hsp_*_demo
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "sco_demo_util.h"
|
#include "sco_demo_util.h"
|
||||||
@ -53,86 +52,93 @@
|
|||||||
#include "wav_util.h"
|
#include "wav_util.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// configure test mode
|
#ifdef HAVE_PORTAUDIO
|
||||||
#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
|
|
||||||
#include <portaudio.h>
|
#include <portaudio.h>
|
||||||
#include "btstack_ring_buffer.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 NUM_CHANNELS 1
|
||||||
|
#define CVSD_BYTES_PER_FRAME (2*NUM_CHANNELS)
|
||||||
#define CVSD_SAMPLE_RATE 8000
|
#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_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_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
|
#if defined(HAVE_PORTAUDIO) && (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE || SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
|
||||||
static PaStream * stream;
|
#define USE_PORTAUDIO
|
||||||
static uint8_t pa_stream_started = 0;
|
#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
|
#endif
|
||||||
|
|
||||||
static int dump_data = 1;
|
static int dump_data = 1;
|
||||||
static int count_sent = 0;
|
static int count_sent = 0;
|
||||||
static int count_received = 0;
|
static int count_received = 0;
|
||||||
static uint8_t negotiated_codec = 0;
|
static int negotiated_codec = -1;
|
||||||
#if SCO_DEMO_MODE != SCO_DEMO_MODE_55
|
|
||||||
static int phase = 0;
|
btstack_sbc_decoder_state_t decoder_state;
|
||||||
#endif
|
btstack_cvsd_plc_state_t cvsd_plc_state;
|
||||||
|
|
||||||
FILE * msbc_file_in;
|
FILE * msbc_file_in;
|
||||||
FILE * msbc_file_out;
|
FILE * msbc_file_out;
|
||||||
|
|
||||||
|
int num_samples_to_write;
|
||||||
|
int num_audio_frames;
|
||||||
|
int phase;
|
||||||
|
|
||||||
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
|
#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
|
// 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,
|
0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557,
|
||||||
19260, 20886, 22431, 23886, 25247, 26509, 27666, 28714, 29648, 30466,
|
19260, 20886, 22431, 23886, 25247, 26509, 27666, 28714, 29648, 30466,
|
||||||
31163, 31738, 32187, 32509, 32702, 32767, 32702, 32509, 32187, 31738,
|
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,
|
-19260, -17557, -15786, -13952, -12062, -10126, -8149, -6140, -4107, -2057,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sco_demo_sine_wave_int8(int num_samples, int8_t * data){
|
// ony use every second value from 16khz table
|
||||||
int i;
|
static void sco_demo_sine_wave_int16_at_8000_hz(int num_samples, int16_t * data){
|
||||||
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){
|
|
||||||
int i;
|
int i;
|
||||||
for (i=0; i < num_samples; i++){
|
for (i=0; i < num_samples; i++){
|
||||||
data[i] = sine_int16[phase++];
|
data[i] = sine_int16_at_16000hz[phase++];
|
||||||
if (phase >= (sizeof(sine_int16) / sizeof(int16_t))){
|
phase++;
|
||||||
|
if (phase >= (sizeof(sine_int16_at_16000hz) / sizeof(int16_t))){
|
||||||
phase = 0;
|
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;
|
if (!hfp_msbc_can_encode_audio_frame_now()) return;
|
||||||
int num_samples = hfp_msbc_num_audio_samples_per_frame();
|
int num_samples = hfp_msbc_num_audio_samples_per_frame();
|
||||||
int16_t sample_buffer[num_samples];
|
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);
|
hfp_msbc_encode_audio_frame(sample_buffer);
|
||||||
num_audio_frames++;
|
num_audio_frames++;
|
||||||
}
|
}
|
||||||
#ifdef SCO_WAV_FILENAME
|
#endif
|
||||||
static btstack_sbc_decoder_state_t decoder_state;
|
|
||||||
static btstack_cvsd_plc_state_t cvsd_plc_state;
|
|
||||||
static int num_samples_to_write;
|
|
||||||
|
|
||||||
#ifdef USE_PORTAUDIO
|
#ifdef USE_PORTAUDIO
|
||||||
static int patestCallback( const void *inputBuffer, void *outputBuffer,
|
static int portaudio_callback( const void *inputBuffer, void *outputBuffer,
|
||||||
unsigned long framesPerBuffer,
|
unsigned long framesPerBuffer,
|
||||||
const PaStreamCallbackTimeInfo* timeInfo,
|
const PaStreamCallbackTimeInfo* timeInfo,
|
||||||
PaStreamCallbackFlags statusFlags,
|
PaStreamCallbackFlags statusFlags,
|
||||||
@ -189,45 +184,166 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer,
|
|||||||
(void) inputBuffer;
|
(void) inputBuffer;
|
||||||
(void) userData;
|
(void) userData;
|
||||||
|
|
||||||
uint32_t bytes_read = 0;
|
// output part
|
||||||
int bytes_per_buffer = framesPerBuffer;
|
|
||||||
if (negotiated_codec == HFP_CODEC_MSBC){
|
// config based on codec
|
||||||
bytes_per_buffer *= MSBC_BYTES_PER_FRAME;
|
int bytes_to_copy;
|
||||||
} else {
|
int prebuffer_bytes;
|
||||||
bytes_per_buffer *= CVSD_BYTES_PER_FRAME;
|
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){
|
// fill with silence while paused
|
||||||
btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_per_buffer, &bytes_read);
|
if (pa_output_paused){
|
||||||
} else {
|
if (btstack_ring_buffer_bytes_available(&pa_output_ring_buffer) < prebuffer_bytes){
|
||||||
printf("NOT ENOUGH DATA!\n");
|
memset(outputBuffer, 0, bytes_to_copy);
|
||||||
memset(outputBuffer, 0, bytes_per_buffer);
|
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 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
|
#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){
|
static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
UNUSED(sample_rate);
|
UNUSED(sample_rate);
|
||||||
|
|
||||||
// printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels);
|
// printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels);
|
||||||
#ifdef USE_PORTAUDIO
|
#ifdef HAVE_PORTAUDIO
|
||||||
if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= MSBC_PREBUFFER_BYTES){
|
btstack_ring_buffer_write(&pa_output_ring_buffer, (uint8_t *)data, num_samples*num_channels*2);
|
||||||
/* -- 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));
|
|
||||||
#else
|
#else
|
||||||
UNUSED(num_channels);
|
UNUSED(num_channels);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!num_samples_to_write) return;
|
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){
|
static void sco_demo_init_mSBC(void){
|
||||||
int sample_rate = 16000;
|
printf("SCO Demo: Init mSBC\n");
|
||||||
wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate);
|
|
||||||
|
wav_writer_open(SCO_WAV_FILENAME, 1, MSBC_SAMPLE_RATE);
|
||||||
btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, NULL);
|
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();
|
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
|
#ifdef SCO_MSBC_IN_FILENAME
|
||||||
msbc_file_in = fopen(SCO_MSBC_IN_FILENAME, "wb");
|
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);
|
printf("SCO Demo: creating mSBC in file %s, %p\n", SCO_MSBC_IN_FILENAME, msbc_file_in);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SCO_MSBC_OUT_FILENAME
|
#ifdef SCO_MSBC_OUT_FILENAME
|
||||||
msbc_file_out = fopen(SCO_MSBC_OUT_FILENAME, "wb");
|
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);
|
printf("SCO Demo: creating mSBC out file %s, %p\n", SCO_MSBC_OUT_FILENAME, msbc_file_out);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_PORTAUDIO
|
#ifdef USE_PORTAUDIO
|
||||||
PaError err;
|
portaudio_initialize(MSBC_SAMPLE_RATE);
|
||||||
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;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,116 +396,65 @@ static void sco_demo_receive_mSBC(uint8_t * packet, uint16_t size){
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void sco_demo_init_CVSD(void){
|
static void sco_demo_init_CVSD(void){
|
||||||
int sample_rate = 8000;
|
printf("SCO Demo: Init CVSD\n");
|
||||||
wav_writer_open(SCO_WAV_FILENAME, 1, sample_rate);
|
|
||||||
|
wav_writer_open(SCO_WAV_FILENAME, 1, CVSD_SAMPLE_RATE);
|
||||||
btstack_cvsd_plc_init(&cvsd_plc_state);
|
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
|
#ifdef USE_PORTAUDIO
|
||||||
PaError err;
|
portaudio_initialize(CVSD_SAMPLE_RATE);
|
||||||
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;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){
|
static void sco_demo_receive_CVSD(uint8_t * packet, uint16_t size){
|
||||||
if (!num_samples_to_write) return;
|
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);
|
const int samples_to_write = btstack_min(num_samples, num_samples_to_write);
|
||||||
int8_t audio_frame_out[24];
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
// memcpy(audio_frame_out, (int8_t*)(packet+3), 24);
|
|
||||||
btstack_cvsd_plc_process_data(&cvsd_plc_state, (int8_t *)(packet+3), num_samples, audio_frame_out);
|
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;
|
num_samples_to_write -= samples_to_write;
|
||||||
if (num_samples_to_write == 0){
|
if (num_samples_to_write == 0){
|
||||||
sco_demo_close();
|
sco_demo_close();
|
||||||
}
|
}
|
||||||
#ifdef USE_PORTAUDIO
|
#ifdef USE_PORTAUDIO
|
||||||
if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= CVSD_PREBUFFER_BYTES){
|
btstack_ring_buffer_write(&pa_output_ring_buffer, (uint8_t *)audio_frame_out, audio_bytes_read);
|
||||||
/* -- 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);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void sco_demo_close(void){
|
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)
|
#if defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
|
||||||
wav_writer_close();
|
wav_writer_close();
|
||||||
|
#endif
|
||||||
printf("SCO demo statistics: ");
|
printf("SCO demo statistics: ");
|
||||||
if (negotiated_codec == HFP_CODEC_MSBC){
|
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);
|
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 {
|
} 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);
|
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
|
#ifdef HAVE_PORTAUDIO
|
||||||
if (pa_stream_started){
|
portaudio_terminate();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
|
|
||||||
#ifdef SCO_WAV_FILENAME
|
#ifdef SCO_WAV_FILENAME
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
printf("SCO Demo: closing wav file\n");
|
printf("SCO Demo: closing wav file\n");
|
||||||
if (negotiated_codec == HFP_CODEC_MSBC){
|
if (negotiated_codec == HFP_CODEC_MSBC){
|
||||||
@ -426,13 +467,16 @@ void sco_demo_close(void){
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
negotiated_codec = -1;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void sco_demo_set_codec(uint8_t codec){
|
void sco_demo_set_codec(uint8_t codec){
|
||||||
if (negotiated_codec == codec) return;
|
if (negotiated_codec == codec) return;
|
||||||
negotiated_codec = codec;
|
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 defined(SCO_WAV_FILENAME) || defined(SCO_SBC_FILENAME)
|
||||||
if (negotiated_codec == HFP_CODEC_MSBC){
|
if (negotiated_codec == HFP_CODEC_MSBC){
|
||||||
sco_demo_init_mSBC();
|
sco_demo_init_mSBC();
|
||||||
@ -444,8 +488,10 @@ void sco_demo_set_codec(uint8_t codec){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sco_demo_init(void){
|
void sco_demo_init(void){
|
||||||
|
|
||||||
// status
|
// 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
|
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
|
||||||
#ifdef HAVE_PORTAUDIO
|
#ifdef HAVE_PORTAUDIO
|
||||||
printf("SCO Demo: Sending sine wave, audio output via portaudio.\n");
|
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");
|
printf("SCO Demo: Sending counter value, hexdump received data.\n");
|
||||||
#endif
|
#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
|
hci_set_sco_voice_setting(0x03); // linear, unsigned, 8-bit, transparent
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -478,19 +526,16 @@ void sco_demo_send(hci_con_handle_t sco_handle){
|
|||||||
|
|
||||||
if (!sco_handle) return;
|
if (!sco_handle) return;
|
||||||
|
|
||||||
const int sco_packet_length = 24 + 3; // hci_get_sco_packet_length();
|
int sco_packet_length = hci_get_sco_packet_length();
|
||||||
const int sco_payload_length = sco_packet_length - 3;
|
int sco_payload_length = sco_packet_length - 3;
|
||||||
|
|
||||||
hci_reserve_packet_buffer();
|
hci_reserve_packet_buffer();
|
||||||
uint8_t * sco_packet = hci_get_outgoing_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 SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
|
||||||
if (negotiated_codec == HFP_CODEC_MSBC){
|
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){
|
if (hfp_msbc_num_bytes_in_stream() < sco_payload_length){
|
||||||
log_error("mSBC stream is empty.");
|
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);
|
fwrite(sco_packet + 3, sco_payload_length, 1, msbc_file_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
sco_demo_fill_audio_frame();
|
sco_demo_msbc_fill_sine_audio_frame();
|
||||||
} else {
|
} 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
|
#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
|
#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';
|
if (phase > 'z') phase = 'a';
|
||||||
#endif
|
#endif
|
||||||
#if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
|
#if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
|
||||||
int j;
|
int j;
|
||||||
for (j=0;j<audio_samples_per_packet;j++){
|
for (j=0;j<sco_payload_length;j++){
|
||||||
sco_packet[3+j] = phase++;
|
sco_packet[3+j] = phase++;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if SCO_DEMO_MODE == SCO_DEMO_MODE_55
|
#if SCO_DEMO_MODE == SCO_DEMO_MODE_55
|
||||||
int j;
|
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] = j & 1 ? 0x35 : 0x53;
|
||||||
sco_packet[3+j] = 0x55;
|
sco_packet[3+j] = 0x55;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if SCO_DEMO_MODE == SCO_DEMO_MODE_00
|
#if SCO_DEMO_MODE == SCO_DEMO_MODE_00
|
||||||
int j;
|
int j;
|
||||||
for (j=0;j<audio_samples_per_packet;j++){
|
for (j=0;j<sco_payload_length;j++){
|
||||||
sco_packet[3+j] = 0x00;
|
sco_packet[3+j] = 0x00;
|
||||||
}
|
}
|
||||||
// additional hack
|
// additional hack
|
||||||
@ -533,6 +658,14 @@ void sco_demo_send(hci_con_handle_t sco_handle){
|
|||||||
(void) phase;
|
(void) phase;
|
||||||
#endif
|
#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);
|
hci_send_sco_packet_buffer(sco_packet_length);
|
||||||
|
|
||||||
// request another send event
|
// request another send event
|
||||||
@ -575,15 +708,18 @@ void sco_demo_receive(uint8_t * packet, uint16_t size){
|
|||||||
packets = 0;
|
packets = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
|
#if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
|
||||||
#ifdef SCO_WAV_FILENAME
|
switch (negotiated_codec){
|
||||||
if (negotiated_codec == HFP_CODEC_MSBC){
|
case HFP_CODEC_MSBC:
|
||||||
sco_demo_receive_mSBC(packet, size);
|
sco_demo_receive_mSBC(packet, size);
|
||||||
} else {
|
break;
|
||||||
sco_demo_receive_CVSD(packet, size);
|
case HFP_CODEC_CVSD:
|
||||||
|
sco_demo_receive_CVSD(packet, size);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
dump_data = 0;
|
dump_data = 0;
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (packet[1] & 0x30){
|
if (packet[1] & 0x30){
|
||||||
@ -617,7 +753,7 @@ void sco_demo_receive(uint8_t * packet, uint16_t size){
|
|||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
#endif
|
#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 i;
|
||||||
int contains_error = 0;
|
int contains_error = 0;
|
||||||
for (i=3;i<size;i++){
|
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
|
// 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
|
// 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
|
// --> 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
|
// 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)
|
// One complete SCO packet with 24 frames every 3 frames (== 3 ms)
|
||||||
#define NUM_ISO_PACKETS (3)
|
#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)
|
// 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
|
// 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
|
// Outgoing SCO packet queue
|
||||||
// simplified ring buffer implementation
|
// simplified ring buffer implementation
|
||||||
@ -162,6 +174,15 @@ static int sco_ring_write; // packet idx
|
|||||||
static int sco_out_transfers_active;
|
static int sco_out_transfers_active;
|
||||||
static struct libusb_transfer *sco_out_transfers[SCO_OUT_BUFFER_COUNT];
|
static struct libusb_transfer *sco_out_transfers[SCO_OUT_BUFFER_COUNT];
|
||||||
static int sco_out_transfers_in_flight[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
|
#endif
|
||||||
|
|
||||||
// outgoing buffer for HCI Command packets
|
// outgoing buffer for HCI Command packets
|
||||||
@ -240,22 +261,8 @@ LIBUSB_CALL static void async_callback(struct libusb_transfer *transfer){
|
|||||||
int c;
|
int c;
|
||||||
|
|
||||||
// identify and free transfers as part of shutdown
|
// 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
|
#ifdef ENABLE_SCO_OVER_HCI
|
||||||
|
if (libusb_state != LIB_USB_TRANSFERS_ALLOCATED || sco_shutdown) {
|
||||||
for (c=0;c<SCO_IN_BUFFER_COUNT;c++){
|
for (c=0;c<SCO_IN_BUFFER_COUNT;c++){
|
||||||
if (transfer == sco_in_transfer[c]){
|
if (transfer == sco_in_transfer[c]){
|
||||||
libusb_free_transfer(transfer);
|
libusb_free_transfer(transfer);
|
||||||
@ -272,8 +279,24 @@ LIBUSB_CALL static void async_callback(struct libusb_transfer *transfer){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,9 +350,10 @@ static int usb_send_sco_packet(uint8_t *packet, int size){
|
|||||||
memcpy(data, packet, size);
|
memcpy(data, packet, size);
|
||||||
|
|
||||||
// setup transfer
|
// 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];
|
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_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);
|
libusb_set_iso_packet_lengths(sco_transfer, iso_packet_size);
|
||||||
r = libusb_submit_transfer(sco_transfer);
|
r = libusb_submit_transfer(sco_transfer);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_error("Error submitting sco transfer, %d", r);
|
log_error("Error submitting sco transfer, %d", r);
|
||||||
@ -724,13 +748,6 @@ static int prepare_device(libusb_device_handle * aHandle){
|
|||||||
libusb_close(aHandle);
|
libusb_close(aHandle);
|
||||||
return r;
|
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
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -760,14 +777,131 @@ static libusb_device_handle * try_open_device(libusb_device * device){
|
|||||||
return dev_handle;
|
return dev_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int usb_open(void){
|
|
||||||
int r;
|
|
||||||
|
|
||||||
#ifdef ENABLE_SCO_OVER_HCI
|
#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_state_machine_init();
|
||||||
sco_ring_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
|
#endif
|
||||||
|
|
||||||
|
static int usb_open(void){
|
||||||
|
int r;
|
||||||
|
|
||||||
handle_packet = NULL;
|
handle_packet = NULL;
|
||||||
|
|
||||||
// default endpoint addresses
|
// default endpoint addresses
|
||||||
@ -900,36 +1034,6 @@ static int usb_open(void){
|
|||||||
|
|
||||||
libusb_state = LIB_USB_TRANSFERS_ALLOCATED;
|
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++) {
|
for (c = 0 ; c < EVENT_IN_BUFFER_COUNT ; c++) {
|
||||||
// configure event_in handlers
|
// configure event_in handlers
|
||||||
libusb_fill_interrupt_transfer(event_in_transfer[c], handle, event_in_addr,
|
libusb_fill_interrupt_transfer(event_in_transfer[c], handle, event_in_addr,
|
||||||
@ -993,7 +1097,6 @@ static int usb_open(void){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int usb_close(void){
|
static int usb_close(void){
|
||||||
int c;
|
int c;
|
||||||
switch (libusb_state){
|
switch (libusb_state){
|
||||||
@ -1035,10 +1138,8 @@ static int usb_close(void){
|
|||||||
}
|
}
|
||||||
for (c = 0; c < SCO_OUT_BUFFER_COUNT ; c++){
|
for (c = 0; c < SCO_OUT_BUFFER_COUNT ; c++){
|
||||||
if (sco_out_transfers_in_flight[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]);
|
libusb_cancel_transfer(sco_out_transfers[c]);
|
||||||
} else {
|
} else {
|
||||||
log_info("libusb_free_transfer sco_out_transfers[%d]", c);
|
|
||||||
libusb_free_transfer(sco_out_transfers[c]);
|
libusb_free_transfer(sco_out_transfers[c]);
|
||||||
sco_out_transfers[c] = 0;
|
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)){
|
static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
|
||||||
log_info("registering packet handler");
|
log_info("registering packet handler");
|
||||||
packet_handler = 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->register_packet_handler = usb_register_packet_handler;
|
||||||
hci_transport_usb->can_send_packet_now = usb_can_send_packet_now;
|
hci_transport_usb->can_send_packet_now = usb_can_send_packet_now;
|
||||||
hci_transport_usb->send_packet = usb_send_packet;
|
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;
|
return hci_transport_usb;
|
||||||
}
|
}
|
||||||
|
@ -167,16 +167,27 @@ static WinUsb_GetCurrentFrameNumber_t WinUsb_GetCurrentFrameNumber;
|
|||||||
// --> support only a single SCO connection
|
// --> support only a single SCO connection
|
||||||
#define ALT_SETTING (1)
|
#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
|
// 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)
|
// One complete SCO packet with 24 frames every 3 frames (== 3 ms)
|
||||||
#define NUM_ISO_PACKETS (3)
|
#define NUM_ISO_PACKETS (3)
|
||||||
|
|
||||||
// results in 9 bytes per frame
|
const uint16_t iso_packet_size_for_alt_setting[] = {
|
||||||
#define ISO_PACKET_SIZE (9)
|
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)
|
// 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
|
// 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
|
#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 uint8_t sco_ring_buffer[SCO_RING_BUFFER_SIZE];
|
||||||
static int sco_ring_write; // packet idx
|
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
|
#endif
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
@ -310,6 +327,30 @@ static void sco_ring_init(void){
|
|||||||
static int sco_ring_have_space(void){
|
static int sco_ring_have_space(void){
|
||||||
return sco_ring_transfers_active < SCO_RING_BUFFER_COUNT;
|
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
|
#endif
|
||||||
|
|
||||||
static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
|
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
|
#ifdef ENABLE_SCO_OVER_HCI
|
||||||
if (hci_sco_in_buffer_handle){
|
usb_sco_unregister_buffers();
|
||||||
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,16 +421,21 @@ exit_on_error:
|
|||||||
// frame number gets updated
|
// frame number gets updated
|
||||||
static void usb_submit_sco_in_transfer_at_frame(int i, ULONG * frame_number){
|
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;
|
LARGE_INTEGER timestamp;
|
||||||
ULONG current_frame_number;
|
ULONG current_frame_number;
|
||||||
WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp);
|
WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp);
|
||||||
|
|
||||||
ULONG frame_before = *frame_number;
|
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]);
|
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 (!result) {
|
||||||
if (GetLastError() == ERROR_IO_PENDING) {
|
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){
|
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;
|
LARGE_INTEGER timestamp;
|
||||||
ULONG current_frame_number;
|
ULONG current_frame_number;
|
||||||
WinUsb_GetCurrentFrameNumber(usb_interface_0_handle, ¤t_frame_number, ×tamp);
|
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);
|
// 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]);
|
continue_stream, NUM_ISO_PACKETS, &hci_sco_packet_descriptors[i * NUM_ISO_PACKETS], &usb_overlapped_sco_in[i]);
|
||||||
|
|
||||||
if (!result) {
|
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);
|
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
|
// get current frame number
|
||||||
ULONG current_frame_number;
|
ULONG current_frame_number;
|
||||||
LARGE_INTEGER timestamp;
|
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;
|
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;
|
DWORD bytes_transferred;
|
||||||
BOOL ok = WinUsb_GetOverlappedResult(usb_interface_0_handle, &usb_overlapped_sco_out[transfer_index], &bytes_transferred, FALSE);
|
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){
|
if(!ok){
|
||||||
DWORD err = GetLastError();
|
DWORD err = GetLastError();
|
||||||
if (err == ERROR_IO_INCOMPLETE){
|
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){
|
if (sco_ring_transfers_active){
|
||||||
// update expected and wait for completion
|
// update expected and wait for completion
|
||||||
usb_sco_out_expected_transfer = (transfer_index+ 1) % SCO_RING_BUFFER_COUNT;
|
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);
|
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
|
// mark free
|
||||||
if (sco_ring_have_space()) {
|
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);
|
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
|
// find index
|
||||||
int i;
|
int i;
|
||||||
@ -705,19 +758,20 @@ static BOOL usb_scan_for_bluetooth_endpoints(void) {
|
|||||||
sco_out_addr = 0;
|
sco_out_addr = 0;
|
||||||
sco_in_addr = 0;
|
sco_in_addr = 0;
|
||||||
|
|
||||||
// look for SCO pipes on Interface #1, Alt Setting ALT_SETTING
|
// look for SCO pipes on Interface #1, Alt Setting 1
|
||||||
result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, ALT_SETTING, &usb_interface_descriptor);
|
int alt_setting = 1;
|
||||||
|
result = WinUsb_QueryInterfaceSettings(usb_interface_1_handle, alt_setting, &usb_interface_descriptor);
|
||||||
if (!result) goto exit_on_error;
|
if (!result) goto exit_on_error;
|
||||||
for (i=0;i<usb_interface_descriptor.bNumEndpoints;i++){
|
for (i=0;i<usb_interface_descriptor.bNumEndpoints;i++){
|
||||||
WINUSB_PIPE_INFORMATION_EX pipe;
|
WINUSB_PIPE_INFORMATION_EX pipe;
|
||||||
result = WinUsb_QueryPipeEx(
|
result = WinUsb_QueryPipeEx(
|
||||||
usb_interface_1_handle,
|
usb_interface_1_handle,
|
||||||
ALT_SETTING,
|
alt_setting,
|
||||||
(UCHAR) i,
|
(UCHAR) i,
|
||||||
&pipe);
|
&pipe);
|
||||||
if (!result) goto exit_on_error;
|
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",
|
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){
|
switch (pipe.PipeType){
|
||||||
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
|
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
|
||||||
if (pipe.PipeId & 0x80) {
|
if (pipe.PipeId & 0x80) {
|
||||||
@ -766,6 +820,88 @@ exit_on_error:
|
|||||||
return FALSE;
|
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
|
// returns 0 if successful, -1 otherwise
|
||||||
static int usb_try_open_device(const char * device_path){
|
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);
|
result = WinUsb_GetAssociatedInterface(usb_interface_0_handle, 0, &usb_interface_1_handle);
|
||||||
if (!result) goto exit_on_error;
|
if (!result) goto exit_on_error;
|
||||||
log_info("Claiming interface 1: success");
|
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
|
#endif
|
||||||
|
|
||||||
result = usb_scan_for_bluetooth_endpoints();
|
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));
|
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));
|
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
|
// setup async io && btstack handler
|
||||||
memset(&usb_overlapped_sco_in, 0, sizeof(usb_overlapped_sco_in));
|
memset(&usb_overlapped_sco_in, 0, sizeof(usb_overlapped_sco_in));
|
||||||
for (i=0;i<ISOC_BUFFERS;i++){
|
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_set_data_source_handler(&usb_data_source_acl_out, &usb_process_acl_out);
|
||||||
btstack_run_loop_add_data_source(&usb_data_source_acl_out);
|
btstack_run_loop_add_data_source(&usb_data_source_acl_out);
|
||||||
|
|
||||||
// submit all incoming transfers
|
// submit all incoming transfers
|
||||||
usb_submit_event_in_transfer();
|
usb_submit_event_in_transfer();
|
||||||
usb_submit_acl_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
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
exit_on_error:
|
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_in_addr);
|
||||||
WinUsb_AbortPipe(usb_interface_0_handle, acl_out_addr);
|
WinUsb_AbortPipe(usb_interface_0_handle, acl_out_addr);
|
||||||
#ifdef ENABLE_SCO_OVER_HCI
|
#ifdef ENABLE_SCO_OVER_HCI
|
||||||
WinUsb_AbortPipe(usb_interface_0_handle, sco_in_addr);
|
usb_sco_stop();
|
||||||
WinUsb_AbortPipe(usb_interface_0_handle, sco_out_addr);
|
|
||||||
#endif
|
#endif
|
||||||
usb_acl_out_active = 0;
|
usb_acl_out_active = 0;
|
||||||
|
|
||||||
@ -1232,7 +1293,7 @@ static int usb_send_sco_packet(uint8_t *packet, int size){
|
|||||||
// setup transfer
|
// setup transfer
|
||||||
int continue_stream = sco_ring_transfers_active > 0;
|
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]);
|
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 (!ok) {
|
||||||
if (GetLastError() != ERROR_IO_PENDING) goto exit_on_error;
|
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};
|
uint8_t event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0};
|
||||||
packet_handler(HCI_EVENT_PACKET, &event[0], sizeof(event));
|
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
|
// and if we have more space for SCO packets
|
||||||
if (sco_ring_have_space()) {
|
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
|
// get usb singleton
|
||||||
static const hci_transport_t hci_transport_usb = {
|
static const hci_transport_t hci_transport_usb = {
|
||||||
/* const char * name; */ "H2_WINUSB",
|
/* const char * name; */ "H2_WINUSB",
|
||||||
@ -1292,6 +1370,7 @@ static const hci_transport_t hci_transport_usb = {
|
|||||||
/* int (*send_packet)(...); */ &usb_send_packet,
|
/* int (*send_packet)(...); */ &usb_send_packet,
|
||||||
/* int (*set_baudrate)(uint32_t baudrate); */ NULL,
|
/* int (*set_baudrate)(uint32_t baudrate); */ NULL,
|
||||||
/* void (*reset_link)(void); */ 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) {
|
const hci_transport_t * hci_transport_usb_instance(void) {
|
||||||
|
@ -42,8 +42,10 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "btstack_ring_buffer.h"
|
#include "btstack_ring_buffer.h"
|
||||||
|
#include "btstack_util.h"
|
||||||
|
|
||||||
#define ERROR_CODE_MEMORY_CAPACITY_EXCEEDED 0x07
|
#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){
|
if (btstack_ring_buffer_bytes_free(ring_buffer) < data_length){
|
||||||
return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
|
return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
|
||||||
}
|
}
|
||||||
int count = 0;
|
|
||||||
while (count < data_length){
|
// copy first chunk
|
||||||
if (ring_buffer->last_written_index < ring_buffer->size - 1){
|
unsigned int bytes_until_end = ring_buffer->size - ring_buffer->last_written_index;
|
||||||
ring_buffer->last_written_index++;
|
unsigned int bytes_to_copy = btstack_min(bytes_until_end, data_length);
|
||||||
} else {
|
memcpy(&ring_buffer->storage[ring_buffer->last_written_index], data, bytes_to_copy);
|
||||||
ring_buffer->last_written_index = 0;
|
data_length -= bytes_to_copy;
|
||||||
}
|
data += bytes_to_copy;
|
||||||
ring_buffer->storage[ring_buffer->last_written_index] = data[count++];
|
|
||||||
|
// 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){
|
if (ring_buffer->last_written_index == ring_buffer->last_read_index){
|
||||||
ring_buffer->full = 1;
|
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
|
// 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){
|
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;
|
// limit data to get and report
|
||||||
while (count < data_length && btstack_ring_buffer_bytes_available(ring_buffer)){
|
data_length = btstack_min(data_length, btstack_ring_buffer_bytes_available(ring_buffer));
|
||||||
if (ring_buffer->last_read_index < ring_buffer->last_written_index ) {
|
*number_of_bytes_read = data_length;
|
||||||
ring_buffer->last_read_index++;
|
|
||||||
} else {
|
// copy first chunk
|
||||||
if (ring_buffer->last_read_index < ring_buffer->size - 1){
|
unsigned int bytes_until_end = ring_buffer->size - ring_buffer->last_read_index;
|
||||||
ring_buffer->last_read_index++;
|
unsigned int bytes_to_copy = btstack_min(bytes_until_end, data_length);
|
||||||
} else {
|
memcpy(data, &ring_buffer->storage[ring_buffer->last_read_index], bytes_to_copy);
|
||||||
ring_buffer->last_read_index = 0;
|
data_length -= bytes_to_copy;
|
||||||
}
|
data += bytes_to_copy;
|
||||||
}
|
|
||||||
ring_buffer->full = 0;
|
// update last read index
|
||||||
data[count++] = ring_buffer->storage[ring_buffer->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_cvsd_plc.h"
|
||||||
#include "btstack_debug.h"
|
#include "btstack_debug.h"
|
||||||
|
|
||||||
|
#define SAMPLE_FORMAT int16_t
|
||||||
|
|
||||||
static float rcos[CVSD_OLAL] = {
|
static float rcos[CVSD_OLAL] = {
|
||||||
0.99148655,0.96623611,0.92510857,0.86950446,
|
0.99148655f,0.96623611f,0.92510857f,0.86950446f,
|
||||||
0.80131732,0.72286918,0.63683150,0.54613418,
|
0.80131732f,0.72286918f,0.63683150f,0.54613418f,
|
||||||
0.45386582,0.36316850,0.27713082,0.19868268,
|
0.45386582f,0.36316850f,0.27713082f,0.19868268f,
|
||||||
0.13049554,0.07489143,0.03376389,0.00851345};
|
0.13049554f,0.07489143f,0.03376389f,0.00851345f};
|
||||||
|
|
||||||
// taken from http://www.codeproject.com/Articles/69941/Best-Square-Root-Method-Algorithm-Function-Precisi
|
// 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
|
// Algorithm: Babylonian Method + some manipulations on IEEE 32 bit floating point representation
|
||||||
@ -79,7 +81,7 @@ static float absolute(float x){
|
|||||||
return 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 num = 0;
|
||||||
float den = 0;
|
float den = 0;
|
||||||
float x2 = 0;
|
float x2 = 0;
|
||||||
@ -94,7 +96,7 @@ static float CrossCorrelation(int8_t *x, int8_t *y){
|
|||||||
return num/den;
|
return num/den;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int PatternMatch(int8_t *y){
|
static int PatternMatch(SAMPLE_FORMAT *y){
|
||||||
float maxCn = -999999.0; // large negative number
|
float maxCn = -999999.0; // large negative number
|
||||||
int bestmatch = 0;
|
int bestmatch = 0;
|
||||||
float Cn;
|
float Cn;
|
||||||
@ -109,7 +111,7 @@ static int PatternMatch(int8_t *y){
|
|||||||
return bestmatch;
|
return bestmatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float AmplitudeMatch(int8_t *y, int8_t bestmatch) {
|
static float AmplitudeMatch(SAMPLE_FORMAT *y, SAMPLE_FORMAT bestmatch) {
|
||||||
int i;
|
int i;
|
||||||
float sumx = 0;
|
float sumx = 0;
|
||||||
float sumy = 0.000001f;
|
float sumy = 0.000001f;
|
||||||
@ -126,19 +128,18 @@ static float AmplitudeMatch(int8_t *y, int8_t bestmatch) {
|
|||||||
return sf;
|
return sf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int8_t crop_to_int8(float val){
|
static SAMPLE_FORMAT crop_sample(float val){
|
||||||
float croped_val = val;
|
float croped_val = val;
|
||||||
if (croped_val > 127.0) croped_val= 127.0;
|
if (croped_val > 32767.0) croped_val= 32767.0;
|
||||||
if (croped_val < -128.0) croped_val=-128.0;
|
if (croped_val < -32768.0) croped_val=-32768.0;
|
||||||
return (int8_t) croped_val;
|
return (SAMPLE_FORMAT) croped_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void btstack_cvsd_plc_init(btstack_cvsd_plc_state_t *plc_state){
|
void btstack_cvsd_plc_init(btstack_cvsd_plc_state_t *plc_state){
|
||||||
memset(plc_state, 0, sizeof(btstack_cvsd_plc_state_t));
|
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;
|
float val;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
float sf = 1;
|
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);
|
sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag);
|
||||||
for (i=0;i<CVSD_OLAL;i++){
|
for (i=0;i<CVSD_OLAL;i++){
|
||||||
val = sf*plc_state->hist[plc_state->bestlag+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++){
|
for (;i<CVSD_FS;i++){
|
||||||
val = sf*plc_state->hist[plc_state->bestlag+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++){
|
for (;i<CVSD_FS+CVSD_OLAL;i++){
|
||||||
float left = sf*plc_state->hist[plc_state->bestlag+i];
|
float left = sf*plc_state->hist[plc_state->bestlag+i];
|
||||||
float right = 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];
|
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++){
|
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;
|
float val;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
if (plc_state->nbf>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 left = plc_state->hist[CVSD_LHIST+i];
|
||||||
float right = in[i];
|
float right = in[i];
|
||||||
val = left * rcos[i-CVSD_RT] + right *rcos[CVSD_OLAL+CVSD_RT-1-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;
|
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 count = 0;
|
||||||
int temp_count = 1;
|
int temp_count = 1;
|
||||||
int i;
|
int i;
|
||||||
@ -237,13 +238,15 @@ static int count_equal_bytes(int8_t * packet, uint16_t size){
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bad_frame(int8_t * frame, uint16_t size){
|
// @assumption frame len 24 samples
|
||||||
return count_equal_bytes(frame, size) > 20;
|
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){
|
if (size != 24){
|
||||||
log_error("btstack_cvsd_plc_process_data: audio frame size is incorrect. Expected %d, got %d", CVSD_FS, size);
|
log_error("btstack_cvsd_plc_process_data: audio frame size is incorrect. Expected %d, got %d", CVSD_FS, size);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
state->frame_count++;
|
state->frame_count++;
|
||||||
if (bad_frame(in,size)){
|
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){
|
void btstack_cvsd_dump_statistics(btstack_cvsd_plc_state_t * state){
|
||||||
log_info("Good frames: %d\n", state->good_frames_nr);
|
log_info("Good frames: %d\n", state->good_frames_nr);
|
||||||
log_info("Bad frames: %d\n", state->bad_frames_nr);
|
log_info("Bad frames: %d\n", state->bad_frames_nr);
|
||||||
|
@ -58,7 +58,7 @@ extern "C" {
|
|||||||
|
|
||||||
/* PLC State Information */
|
/* PLC State Information */
|
||||||
typedef struct cvsd_plc_state {
|
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;
|
int16_t bestlag;
|
||||||
int nbf;
|
int nbf;
|
||||||
|
|
||||||
@ -69,11 +69,9 @@ typedef struct cvsd_plc_state {
|
|||||||
} btstack_cvsd_plc_state_t;
|
} btstack_cvsd_plc_state_t;
|
||||||
|
|
||||||
void btstack_cvsd_plc_init(btstack_cvsd_plc_state_t *plc_state);
|
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_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, int8_t *in, int8_t *out);
|
void btstack_cvsd_plc_good_frame(btstack_cvsd_plc_state_t *plc_state, int16_t *in, int16_t *out);
|
||||||
uint8_t * btstack_cvsd_plc_zero_signal_frame(void);
|
void btstack_cvsd_plc_process_data(btstack_cvsd_plc_state_t * state, int16_t * in, uint16_t size, int16_t * out);
|
||||||
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_dump_statistics(btstack_cvsd_plc_state_t * state);
|
void btstack_cvsd_dump_statistics(btstack_cvsd_plc_state_t * state);
|
||||||
|
|
||||||
#if defined __cplusplus
|
#if defined __cplusplus
|
||||||
|
@ -47,6 +47,8 @@
|
|||||||
|
|
||||||
#include "btstack_sbc_plc.h"
|
#include "btstack_sbc_plc.h"
|
||||||
|
|
||||||
|
#define SAMPLE_FORMAT int16_t
|
||||||
|
|
||||||
static uint8_t indices0[] = { 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d,
|
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,
|
||||||
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;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float CrossCorrelation(int16_t *x, int16_t *y){
|
static float CrossCorrelation(SAMPLE_FORMAT *x, SAMPLE_FORMAT *y){
|
||||||
float num = 0;
|
float num = 0;
|
||||||
float den = 0;
|
float den = 0;
|
||||||
float x2 = 0;
|
float x2 = 0;
|
||||||
@ -99,13 +101,13 @@ static float CrossCorrelation(int16_t *x, int16_t *y){
|
|||||||
return num/den;
|
return num/den;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int PatternMatch(int16_t *y){
|
static int PatternMatch(SAMPLE_FORMAT *y){
|
||||||
float maxCn = -999999.0; /* large negative number */
|
float maxCn = -999999.0; // large negative number
|
||||||
int bestmatch = 0;
|
int bestmatch = 0;
|
||||||
float Cn;
|
float Cn;
|
||||||
int n;
|
int n;
|
||||||
for (n=0;n<SBC_N;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){
|
if (Cn>maxCn){
|
||||||
bestmatch=n;
|
bestmatch=n;
|
||||||
maxCn = Cn;
|
maxCn = Cn;
|
||||||
@ -114,8 +116,7 @@ static int PatternMatch(int16_t *y){
|
|||||||
return bestmatch;
|
return bestmatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static float AmplitudeMatch(SAMPLE_FORMAT *y, SAMPLE_FORMAT bestmatch) {
|
||||||
static float AmplitudeMatch(int16_t *y, int16_t bestmatch) {
|
|
||||||
int i;
|
int i;
|
||||||
float sumx = 0;
|
float sumx = 0;
|
||||||
float sumy = 0.000001f;
|
float sumy = 0.000001f;
|
||||||
@ -126,17 +127,17 @@ static float AmplitudeMatch(int16_t *y, int16_t bestmatch) {
|
|||||||
sumy += absolute(y[bestmatch+i]);
|
sumy += absolute(y[bestmatch+i]);
|
||||||
}
|
}
|
||||||
sf = sumx/sumy;
|
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<0.75f) sf=0.75f;
|
||||||
if (sf>1.2f) sf=1.2f;
|
if (sf>1.2f) sf=1.2f;
|
||||||
return sf;
|
return sf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int16_t crop_to_int16(float val){
|
static SAMPLE_FORMAT crop_sample(float val){
|
||||||
float croped_val = val;
|
float croped_val = val;
|
||||||
if (croped_val > 32767.0) croped_val= 32767.0;
|
if (croped_val > 32767.0) croped_val= 32767.0;
|
||||||
if (croped_val < -32768.0) croped_val=-32768.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){
|
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));
|
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;
|
float val;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
float sf = 1;
|
float sf = 1;
|
||||||
|
|
||||||
plc_state->nbf++;
|
plc_state->nbf++;
|
||||||
|
|
||||||
if (plc_state->nbf==1){
|
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);
|
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;
|
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);
|
sf = AmplitudeMatch(plc_state->hist, plc_state->bestlag);
|
||||||
for (i=0;i<SBC_OLAL;i++){
|
for (i=0;i<SBC_OLAL;i++){
|
||||||
float left = ZIRbuf[i];
|
float left = ZIRbuf[i];
|
||||||
float right = sf*plc_state->hist[plc_state->bestlag+i];
|
float right = sf*plc_state->hist[plc_state->bestlag+i];
|
||||||
val = left*rcos[i] + right*rcos[SBC_OLAL-1-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++){
|
for (;i<SBC_FS;i++){
|
||||||
val = sf*plc_state->hist[plc_state->bestlag+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++){
|
for (;i<SBC_FS+SBC_OLAL;i++){
|
||||||
float left = sf*plc_state->hist[plc_state->bestlag+i];
|
float left = sf*plc_state->hist[plc_state->bestlag+i];
|
||||||
float right = 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];
|
val = left*rcos[i-SBC_FS]+right*rcos[SBC_OLAL-1-i+SBC_FS];
|
||||||
plc_state->hist[SBC_LHIST+i] = crop_to_int16(val);
|
plc_state->hist[SBC_LHIST+i] = crop_sample(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;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];
|
plc_state->hist[SBC_LHIST+i] = plc_state->hist[plc_state->bestlag+i];
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} 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];
|
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];
|
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++){
|
for (i=0;i<SBC_LHIST+SBC_RT+SBC_OLAL;i++){
|
||||||
plc_state->hist[i] = plc_state->hist[i+SBC_FS];
|
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;
|
float val;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
if (plc_state->nbf>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];
|
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 left = plc_state->hist[SBC_LHIST+i];
|
||||||
float right = in[i];
|
float right = in[i];
|
||||||
val = left*rcos[i-SBC_RT] + right*rcos[SBC_OLAL-1-i+SBC_RT];
|
val = left*rcos[i-SBC_RT] + right*rcos[SBC_OLAL+SBC_RT-1-i];
|
||||||
out[i] = (int16_t)val;
|
out[i] = (SAMPLE_FORMAT)val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;i<SBC_FS;i++){
|
for (;i<SBC_FS;i++){
|
||||||
out[i] = in[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++){
|
for (i=0;i<SBC_FS;i++){
|
||||||
plc_state->hist[SBC_LHIST+i] = out[i];
|
plc_state->hist[SBC_LHIST+i] = out[i];
|
||||||
}
|
}
|
||||||
/* shift the history buffer */
|
// shift the history buffer
|
||||||
for (i=0;i<SBC_LHIST;i++){
|
for (i=0;i<SBC_LHIST;i++){
|
||||||
plc_state->hist[i] = plc_state->hist[i+SBC_FS];
|
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;
|
hfp_connection_t * hfp_connection = NULL;
|
||||||
uint8_t status;
|
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)) {
|
switch (hci_event_packet_get_type(packet)) {
|
||||||
case HCI_EVENT_CONNECTION_REQUEST:
|
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){
|
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;
|
if (hfp_connection->state >= HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) return 0;
|
||||||
int done = 0;
|
int done = 0;
|
||||||
switch(hfp_connection->command){
|
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){
|
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;
|
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_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){
|
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);
|
hci_connection_t * connection = (hci_connection_t *) btstack_run_loop_get_timer_context(timer);
|
||||||
#ifdef HAVE_EMBEDDED_TICK
|
#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){
|
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));
|
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_run_loop_remove_timer(&conn->timeout);
|
||||||
|
|
||||||
btstack_linked_list_remove(&hci_stack->connections, (btstack_linked_item_t *) conn);
|
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
|
// now it's gone
|
||||||
hci_emit_nr_connections_changed();
|
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
|
#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_stack->substate = HCI_INIT_W4_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING;
|
||||||
hci_send_cmd(&hci_write_default_erroneous_data_reporting, 1);
|
hci_send_cmd(&hci_write_default_erroneous_data_reporting, 1);
|
||||||
break;
|
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
|
#endif
|
||||||
#ifdef ENABLE_BLE
|
#ifdef ENABLE_BLE
|
||||||
// LE INIT
|
// 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:
|
case HCI_INIT_W4_SET_EVENT_MASK:
|
||||||
// skip Classic init commands for LE only chipsets
|
// skip Classic init commands for LE only chipsets
|
||||||
if (!hci_classic_supported()){
|
if (!hci_classic_supported()){
|
||||||
|
#ifdef ENABLE_BLE
|
||||||
if (hci_le_supported()){
|
if (hci_le_supported()){
|
||||||
hci_stack->substate = HCI_INIT_LE_READ_BUFFER_SIZE; // skip all classic command
|
hci_stack->substate = HCI_INIT_LE_READ_BUFFER_SIZE; // skip all classic command
|
||||||
return;
|
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()){
|
if (!gap_ssp_supported()){
|
||||||
hci_stack->substate = HCI_INIT_WRITE_PAGE_TIMEOUT;
|
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
|
// explicit fall through to reduce repetitions
|
||||||
|
|
||||||
case HCI_INIT_W4_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING:
|
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()){
|
if (!hci_le_supported()){
|
||||||
// SKIP LE init for Classic only configuration
|
// SKIP LE init for Classic only configuration
|
||||||
hci_init_done();
|
hci_init_done();
|
||||||
@ -1724,6 +1763,13 @@ static void event_handler(uint8_t *packet, int size){
|
|||||||
}
|
}
|
||||||
conn->state = OPEN;
|
conn->state = OPEN;
|
||||||
conn->con_handle = little_endian_read_16(packet, 3);
|
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;
|
break;
|
||||||
|
|
||||||
case HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE:
|
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_authentication_requirement = SSP_IO_AUTHREQ_MITM_PROTECTION_NOT_REQUIRED_GENERAL_BONDING;
|
||||||
hci_stack->ssp_auto_accept = 1;
|
hci_stack->ssp_auto_accept = 1;
|
||||||
|
|
||||||
// voice setting - signed 8 bit pcm data with CVSD over the air
|
// voice setting - signed 16 bit pcm data with CVSD over the air
|
||||||
hci_stack->sco_voice_setting = 0x40;
|
hci_stack->sco_voice_setting = 0x60;
|
||||||
|
|
||||||
hci_state_reset();
|
hci_state_reset();
|
||||||
}
|
}
|
||||||
@ -3074,6 +3120,19 @@ int hci_send_cmd_packet(uint8_t *packet, int size){
|
|||||||
connectionClearAuthenticationFlags(conn, SSP_PAIRING_ACTIVE);
|
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
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_BLE
|
#ifdef ENABLE_BLE
|
||||||
|
@ -550,6 +550,10 @@ typedef enum hci_init_state{
|
|||||||
HCI_INIT_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING,
|
HCI_INIT_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING,
|
||||||
HCI_INIT_W4_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
|
#ifdef ENABLE_BLE
|
||||||
HCI_INIT_LE_READ_BUFFER_SIZE,
|
HCI_INIT_LE_READ_BUFFER_SIZE,
|
||||||
HCI_INIT_W4_LE_READ_BUFFER_SIZE,
|
HCI_INIT_W4_LE_READ_BUFFER_SIZE,
|
||||||
@ -694,6 +698,7 @@ typedef struct {
|
|||||||
uint8_t new_scan_enable_value;
|
uint8_t new_scan_enable_value;
|
||||||
|
|
||||||
uint16_t sco_voice_setting;
|
uint16_t sco_voice_setting;
|
||||||
|
uint16_t sco_voice_setting_active;
|
||||||
|
|
||||||
uint8_t loopback_mode;
|
uint8_t loopback_mode;
|
||||||
|
|
||||||
|
@ -1061,3 +1061,18 @@ OPCODE(OGF_LE_CONTROLLER, 0x26), "QQ"
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#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_start_encryption;
|
||||||
extern const hci_cmd_t hci_le_test_end;
|
extern const hci_cmd_t hci_le_test_end;
|
||||||
extern const hci_cmd_t hci_le_transmitter_test;
|
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
|
* construct HCI Command based on template
|
||||||
*
|
*
|
||||||
|
@ -104,6 +104,11 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
void (*reset_link)(void);
|
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;
|
} hci_transport_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -549,6 +549,7 @@ static const hci_transport_t hci_transport_h4 = {
|
|||||||
/* int (*send_packet)(...); */ &hci_transport_h4_send_packet,
|
/* int (*send_packet)(...); */ &hci_transport_h4_send_packet,
|
||||||
/* int (*set_baudrate)(uint32_t baudrate); */ &hci_transport_h4_set_baudrate,
|
/* int (*set_baudrate)(uint32_t baudrate); */ &hci_transport_h4_set_baudrate,
|
||||||
/* void (*reset_link)(void); */ NULL,
|
/* void (*reset_link)(void); */ NULL,
|
||||||
|
/* void (*set_sco_config)(uint16_t voice_setting, int num_connections); */ NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
// configure and return h4 singleton
|
// 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 (*send_packet)(...); */ &hci_transport_h5_send_packet,
|
||||||
/* int (*set_baudrate)(uint32_t baudrate); */ &hci_transport_h5_set_baudrate,
|
/* int (*set_baudrate)(uint32_t baudrate); */ &hci_transport_h5_set_baudrate,
|
||||||
/* void (*reset_link)(void); */ &hci_transport_h5_reset_link,
|
/* 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
|
// configure and return h5 singleton
|
||||||
|
@ -11,10 +11,10 @@
|
|||||||
#include "btstack_cvsd_plc.h"
|
#include "btstack_cvsd_plc.h"
|
||||||
#include "wav_util.h"
|
#include "wav_util.h"
|
||||||
|
|
||||||
const int audio_samples_per_frame = 24;
|
const int audio_samples_per_frame = 24;
|
||||||
static int8_t audio_frame_in[audio_samples_per_frame];
|
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 },
|
{ 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, 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 },
|
{ 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;
|
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
|
// input signal: pre-computed sine wave, 160 Hz at 16000 kHz
|
||||||
static const int16_t sine_int16[] = {
|
static const int16_t sine_int16[] = {
|
||||||
0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557,
|
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,
|
-19260, -17557, -15786, -13952, -12062, -10126, -8149, -6140, -4107, -2057,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int phase = 0;
|
static int count_equal_samples(int16_t * packet, uint16_t size){
|
||||||
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){
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int temp_count = 1;
|
int temp_count = 1;
|
||||||
int i;
|
int i;
|
||||||
@ -87,61 +57,120 @@ static int count_equal_bytes(uint8_t * packet, uint16_t size){
|
|||||||
return count;
|
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);
|
btstack_cvsd_plc_init(&plc_state);
|
||||||
wav_writer_open(out_filename, 1, 8000);
|
wav_writer_open(out_filename, 1, 8000);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i=0; i<2000; i++){
|
for (i=0; i<2000; i++){
|
||||||
sco_demo_sine_wave_int8(audio_samples_per_frame, audio_frame_in);
|
create_sine_wave_int16_data(audio_samples_per_frame, audio_frame_in);
|
||||||
wav_writer_write_int8(audio_samples_per_frame, audio_frame_in);
|
wav_writer_write_int16(audio_samples_per_frame, audio_frame_in);
|
||||||
}
|
}
|
||||||
wav_writer_close();
|
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);
|
btstack_cvsd_plc_init(&plc_state);
|
||||||
wav_writer_open(out_filename, 1, 8000);
|
wav_writer_open(out_filename, 1, 8000);
|
||||||
wav_reader_open(in_filename);
|
wav_reader_open(in_filename);
|
||||||
|
|
||||||
int fc = 0;
|
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){
|
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++;
|
fc++;
|
||||||
}
|
}
|
||||||
wav_reader_close();
|
wav_reader_close();
|
||||||
wav_writer_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);
|
// printf("\nProcess %s -> %s\n", in_filename, out_filename);
|
||||||
btstack_cvsd_plc_init(&plc_state);
|
btstack_cvsd_plc_init(&plc_state);
|
||||||
wav_writer_open(out_filename, 1, 8000);
|
wav_writer_open(out_filename, 1, 8000);
|
||||||
wav_reader_open(in_filename);
|
wav_reader_open(in_filename);
|
||||||
|
|
||||||
while (wav_reader_read_int8(audio_samples_per_frame, audio_frame_in)){
|
while (wav_reader_read_int16(audio_samples_per_frame, audio_frame_in)){
|
||||||
int8_t audio_frame_out[audio_samples_per_frame];
|
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);
|
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_reader_close();
|
||||||
wav_writer_close();
|
wav_writer_close();
|
||||||
btstack_cvsd_dump_statistics(&plc_state);
|
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);
|
// printf("\nMark bad frame %s -> %s\n", in_filename, out_filename);
|
||||||
btstack_cvsd_plc_init(&plc_state);
|
btstack_cvsd_plc_init(&plc_state);
|
||||||
CHECK_EQUAL(wav_writer_open(out_filename, 1, 8000), 0);
|
CHECK_EQUAL(wav_writer_open(out_filename, 1, 8000), 0);
|
||||||
CHECK_EQUAL(wav_reader_open(in_filename), 0);
|
CHECK_EQUAL(wav_reader_open(in_filename), 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)){
|
||||||
int8_t audio_frame_out[audio_samples_per_frame];
|
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);
|
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_reader_close();
|
||||||
wav_writer_close();
|
wav_writer_close();
|
||||||
@ -161,8 +190,8 @@ TEST(CVSD_PLC, CountEqBytes){
|
|||||||
|
|
||||||
TEST(CVSD_PLC, TestLiveWavFile){
|
TEST(CVSD_PLC, TestLiveWavFile){
|
||||||
int corruption_step = 10;
|
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-16bit.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_with_bad_frames.wav", corruption_step);
|
||||||
|
|
||||||
mark_bad_frames_wav_file("results/sco_input.wav", "results/sco_input_detected_frames.wav");
|
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");
|
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/TestHarness.h"
|
||||||
#include "CppUTest/CommandLineTestRunner.h"
|
#include "CppUTest/CommandLineTestRunner.h"
|
||||||
#include "btstack_ring_buffer.h"
|
#include "btstack_ring_buffer.h"
|
||||||
|
#include "btstack_util.h"
|
||||||
|
|
||||||
static uint8_t storage[10];
|
static uint8_t storage[10];
|
||||||
|
|
||||||
|
uint32_t btstack_min(uint32_t a, uint32_t b){
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
TEST_GROUP(RingBuffer){
|
TEST_GROUP(RingBuffer){
|
||||||
btstack_ring_buffer_t ring_buffer;
|
btstack_ring_buffer_t ring_buffer;
|
||||||
int storage_size;
|
int storage_size;
|
||||||
|
@ -48,7 +48,7 @@ def scrape_page(fout, url):
|
|||||||
print('-' * 70)
|
print('-' * 70)
|
||||||
|
|
||||||
# get all <tr> elements in <table id="gattTable">
|
# 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:
|
for row in rows:
|
||||||
children = row.getchildren()
|
children = row.getchildren()
|
||||||
data_type_value = children[0].text_content()
|
data_type_value = children[0].text_content()
|
||||||
|
@ -40,7 +40,7 @@ def scrape_page(fout, url):
|
|||||||
page = requests.get(url)
|
page = requests.get(url)
|
||||||
tree = html.fromstring(page.content)
|
tree = html.fromstring(page.content)
|
||||||
# get all <tr> elements in <table id="gattTable">
|
# 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:
|
for row in rows:
|
||||||
children = row.getchildren()
|
children = row.getchildren()
|
||||||
summary = children[0].text_content()
|
summary = children[0].text_content()
|
||||||
|
@ -38,7 +38,7 @@ def list_services():
|
|||||||
page = requests.get(url)
|
page = requests.get(url)
|
||||||
tree = html.fromstring(page.content)
|
tree = html.fromstring(page.content)
|
||||||
# get all <tr> elements in <table id="gattTable">
|
# 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("%-55s| %-30s| %s" % ('Specification Type', 'Specification Name', 'UUID'))
|
||||||
print('-'*55 + '+-' + '-' * 30 + '+-' + '-'*10)
|
print('-'*55 + '+-' + '-' * 30 + '+-' + '-'*10)
|
||||||
maxlen_type = 0
|
maxlen_type = 0
|
||||||
@ -65,9 +65,14 @@ def parse_properties(element):
|
|||||||
property_list_human = []
|
property_list_human = []
|
||||||
for property in properties:
|
for property in properties:
|
||||||
property_name = property.tag
|
property_name = property.tag
|
||||||
|
if property_name == "WriteWithoutResponse":
|
||||||
|
property_name = "Write_Without_Response"
|
||||||
|
if property_name == "InformationText":
|
||||||
|
continue
|
||||||
property_requirement = property.text
|
property_requirement = property.text
|
||||||
if (property_requirement != "Excluded"):
|
if (property_requirement == "Excluded"):
|
||||||
property_list_human.append(property_name)
|
continue
|
||||||
|
property_list_human.append(property_name)
|
||||||
return (property_list_human)
|
return (property_list_human)
|
||||||
|
|
||||||
print("Characteristic %s - properties %s" % (name, property_list_human))
|
print("Characteristic %s - properties %s" % (name, property_list_human))
|
||||||
@ -163,7 +168,8 @@ def convert_service(fout, specification_type):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if (type == 'org.bluetooth.descriptor.report_reference'):
|
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
|
continue
|
||||||
|
|
||||||
if (type == 'org.bluetooth.descriptor.number_of_digitals'):
|
if (type == 'org.bluetooth.descriptor.number_of_digitals'):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user