2016-05-23 18:04:57 +02:00
/*
* Copyright ( C ) 2016 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
* 4. Any redistribution , use , or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain .
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL MATTHIAS
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING ,
* BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS
* OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE .
*
* Please inquire about commercial licensing options at
* contact @ bluekitchen - gmbh . com
*
*/
2017-03-24 23:39:20 +01:00
# define __BTSTACK_FILE__ "sco_demo_util.c"
2016-05-23 18:04:57 +02:00
/*
* sco_demo_util . c - send / receive test data via SCO , used by hfp_ * _demo and hsp_ * _demo
*/
2016-07-20 11:11:02 +02:00
# include <stdio.h>
2016-05-23 18:04:57 +02:00
# include "sco_demo_util.h"
2018-07-20 16:13:52 +02:00
# include "btstack_audio.h"
2016-07-08 17:11:57 +02:00
# include "btstack_debug.h"
2018-07-20 16:13:52 +02:00
# include "btstack_ring_buffer.h"
2016-11-11 14:06:23 +01:00
# include "classic/btstack_cvsd_plc.h"
2018-07-20 16:13:52 +02:00
# include "classic/btstack_sbc.h"
2016-11-11 14:06:23 +01:00
# include "classic/hfp.h"
2018-07-20 16:13:52 +02:00
# include "classic/hfp_msbc.h"
2016-07-08 17:11:57 +02:00
2016-11-11 14:06:23 +01:00
# ifdef HAVE_POSIX_FILE_IO
2016-09-19 14:40:47 +02:00
# include "wav_util.h"
2016-11-11 14:06:23 +01:00
# endif
2016-09-19 14:40:47 +02:00
2017-01-20 23:08:05 +01:00
// test modes
2017-01-24 17:26:55 +01:00
# 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
2016-05-23 18:04:57 +02:00
// SCO demo configuration
2017-01-20 23:08:05 +01:00
# define SCO_DEMO_MODE SCO_DEMO_MODE_SINE
2016-05-23 18:04:57 +02:00
2017-01-20 23:08:05 +01:00
// number of sco packets until 'report' on console
# define SCO_REPORT_PERIOD 100
2016-07-23 16:29:33 +02:00
2017-02-01 15:02:28 +01:00
# ifdef HAVE_POSIX_FILE_IO
2017-01-30 17:49:47 +01:00
// length and name of wav file on disk
2016-09-14 16:06:19 +02:00
# define SCO_WAV_DURATION_IN_SECONDS 15
2017-01-20 23:08:05 +01:00
# define SCO_WAV_FILENAME "sco_input.wav"
2017-02-01 15:02:28 +01:00
# endif
2016-05-23 18:04:57 +02:00
2017-01-20 23:08:05 +01:00
// name of sbc test files
# define SCO_MSBC_OUT_FILENAME "sco_output.msbc"
# define SCO_MSBC_IN_FILENAME "sco_input.msbc"
2016-05-23 18:04:57 +02:00
2017-01-20 23:08:05 +01:00
// pre-buffer for CVSD and mSBC - also defines latency
# define SCO_CVSD_PA_PREBUFFER_MS 50
# define SCO_MSBC_PA_PREBUFFER_MS 50
2016-12-19 14:35:28 +01:00
2017-01-20 23:08:05 +01:00
// constants
2016-10-19 15:04:10 +02:00
# define NUM_CHANNELS 1
# define CVSD_SAMPLE_RATE 8000
# define MSBC_SAMPLE_RATE 16000
2018-07-20 16:13:52 +02:00
# define BYTES_PER_FRAME 2
2016-10-19 15:04:10 +02:00
2018-07-20 16:13:52 +02:00
# if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE || SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
# define CVSD_PA_PREBUFFER_BYTES (SCO_CVSD_PA_PREBUFFER_MS * CVSD_SAMPLE_RATE / 1000 * BYTES_PER_FRAME)
# define MSBC_PA_PREBUFFER_BYTES (SCO_MSBC_PA_PREBUFFER_MS * MSBC_SAMPLE_RATE / 1000 * BYTES_PER_FRAME)
2017-01-20 23:08:05 +01:00
# endif
2016-10-19 15:04:10 +02:00
2017-01-25 16:58:03 +01:00
// output
2018-07-20 16:13:52 +02:00
static int audio_output_paused = 0 ;
static uint8_t audio_output_ring_buffer_storage [ 2 * MSBC_PA_PREBUFFER_BYTES ] ;
static btstack_ring_buffer_t audio_output_ring_buffer ;
2017-01-25 16:58:03 +01:00
// input
# if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE
2018-07-20 16:13:52 +02:00
# define USE_AUDIO_INPUT
static int audio_input_paused = 0 ;
static uint8_t audio_input_ring_buffer_storage [ 2 * 8000 ] ; // full second input buffer
static btstack_ring_buffer_t audio_input_ring_buffer ;
2016-05-23 18:04:57 +02:00
# endif
2016-07-08 17:11:57 +02:00
static int dump_data = 1 ;
static int count_sent = 0 ;
static int count_received = 0 ;
2017-01-20 23:08:05 +01:00
static int negotiated_codec = - 1 ;
2017-03-20 10:57:13 +01:00
# ifdef ENABLE_HFP_WIDE_BAND_SPEECH
2018-07-20 16:13:52 +02:00
static btstack_sbc_decoder_state_t decoder_state ;
2017-03-20 10:57:13 +01:00
# endif
2018-07-20 16:13:52 +02:00
static btstack_cvsd_plc_state_t cvsd_plc_state ;
# define MAX_NUM_MSBC_SAMPLES (16*8)
2016-07-08 17:11:57 +02:00
2017-03-20 10:57:13 +01:00
# ifdef ENABLE_HFP_WIDE_BAND_SPEECH
2016-08-09 18:09:44 +02:00
FILE * msbc_file_in ;
FILE * msbc_file_out ;
2017-03-20 10:57:13 +01:00
# endif
2016-07-26 00:24:03 +02:00
2017-01-25 16:58:03 +01:00
int num_samples_to_write ;
int num_audio_frames ;
2017-08-08 18:03:40 +02:00
unsigned int phase ;
2017-01-25 16:58:03 +01:00
2016-05-23 18:04:57 +02:00
# if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
2016-07-25 11:04:15 +02:00
2016-11-11 14:06:23 +01:00
// input signal: pre-computed sine wave, 160 Hz at 16000 kHz
2017-01-20 23:08:05 +01:00
static const int16_t sine_int16_at_16000hz [ ] = {
2016-11-11 14:06:23 +01:00
0 , 2057 , 4107 , 6140 , 8149 , 10126 , 12062 , 13952 , 15786 , 17557 ,
19260 , 20886 , 22431 , 23886 , 25247 , 26509 , 27666 , 28714 , 29648 , 30466 ,
31163 , 31738 , 32187 , 32509 , 32702 , 32767 , 32702 , 32509 , 32187 , 31738 ,
31163 , 30466 , 29648 , 28714 , 27666 , 26509 , 25247 , 23886 , 22431 , 20886 ,
19260 , 17557 , 15786 , 13952 , 12062 , 10126 , 8149 , 6140 , 4107 , 2057 ,
0 , - 2057 , - 4107 , - 6140 , - 8149 , - 10126 , - 12062 , - 13952 , - 15786 , - 17557 ,
- 19260 , - 20886 , - 22431 , - 23886 , - 25247 , - 26509 , - 27666 , - 28714 , - 29648 , - 30466 ,
- 31163 , - 31738 , - 32187 , - 32509 , - 32702 , - 32767 , - 32702 , - 32509 , - 32187 , - 31738 ,
- 31163 , - 30466 , - 29648 , - 28714 , - 27666 , - 26509 , - 25247 , - 23886 , - 22431 , - 20886 ,
- 19260 , - 17557 , - 15786 , - 13952 , - 12062 , - 10126 , - 8149 , - 6140 , - 4107 , - 2057 ,
} ;
2017-03-20 10:57:13 +01:00
// 8 kHz samples for CVSD/SCO packets in little endian
2017-08-09 22:58:56 +02:00
static void sco_demo_sine_wave_int16_at_8000_hz_little_endian ( unsigned int num_samples , uint8_t * data ) {
2017-08-08 18:03:40 +02:00
unsigned int i ;
2016-11-11 14:06:23 +01:00
for ( i = 0 ; i < num_samples ; i + + ) {
2017-03-20 10:57:13 +01:00
int16_t sample = sine_int16_at_16000hz [ phase ] ;
2017-08-09 22:58:56 +02:00
little_endian_store_16 ( data , i * 2 , sample ) ;
2017-03-20 10:57:13 +01:00
// ony use every second sample from 16khz table to get 8khz
phase + = 2 ;
2017-01-26 19:27:31 +01:00
if ( phase > = ( sizeof ( sine_int16_at_16000hz ) / sizeof ( int16_t ) ) ) {
phase = 0 ;
}
}
}
2017-03-20 10:57:13 +01:00
// 16 kHz samples for mSBC encoder in host endianess
# ifdef ENABLE_HFP_WIDE_BAND_SPEECH
2017-08-08 18:03:40 +02:00
static void sco_demo_sine_wave_int16_at_16000_hz_host_endian ( unsigned int num_samples , int16_t * data ) {
unsigned int i ;
2017-01-26 19:27:31 +01:00
for ( i = 0 ; i < num_samples ; i + + ) {
2017-03-20 10:57:13 +01:00
data [ i ] = sine_int16_at_16000hz [ phase + + ] ;
2017-01-20 23:08:05 +01:00
if ( phase > = ( sizeof ( sine_int16_at_16000hz ) / sizeof ( int16_t ) ) ) {
2016-11-11 14:06:23 +01:00
phase = 0 ;
}
}
}
2017-01-25 17:23:55 +01:00
static void sco_demo_msbc_fill_sine_audio_frame ( void ) {
2016-11-11 14:06:23 +01:00
if ( ! hfp_msbc_can_encode_audio_frame_now ( ) ) return ;
int num_samples = hfp_msbc_num_audio_samples_per_frame ( ) ;
2018-07-20 16:13:52 +02:00
if ( num_samples > MAX_NUM_MSBC_SAMPLES ) return ;
int16_t sample_buffer [ MAX_NUM_MSBC_SAMPLES ] ;
2017-01-26 19:27:31 +01:00
sco_demo_sine_wave_int16_at_16000_hz_host_endian ( num_samples , sample_buffer ) ;
2016-11-11 14:06:23 +01:00
hfp_msbc_encode_audio_frame ( sample_buffer ) ;
num_audio_frames + + ;
}
2017-01-25 16:58:03 +01:00
# endif
2017-03-20 10:57:13 +01:00
# endif
2016-10-19 15:04:10 +02:00
2018-07-20 16:13:52 +02:00
static void playback_callback ( int16_t * buffer , uint16_t num_samples ) {
2017-01-25 16:58:03 +01:00
2017-01-20 23:08:05 +01:00
// config based on codec
2018-07-20 16:13:52 +02:00
int bytes_to_copy = num_samples * BYTES_PER_FRAME ;
2017-08-11 12:14:47 +02:00
uint32_t prebuffer_bytes ;
2017-01-20 23:08:05 +01:00
switch ( negotiated_codec ) {
case HFP_CODEC_MSBC :
prebuffer_bytes = MSBC_PA_PREBUFFER_BYTES ;
break ;
case HFP_CODEC_CVSD :
2018-07-20 16:13:52 +02:00
default :
2017-01-20 23:08:05 +01:00
prebuffer_bytes = CVSD_PA_PREBUFFER_BYTES ;
break ;
2016-10-19 15:04:10 +02:00
}
2017-01-20 23:08:05 +01:00
// fill with silence while paused
2018-07-20 16:13:52 +02:00
if ( audio_output_paused ) {
if ( btstack_ring_buffer_bytes_available ( & audio_output_ring_buffer ) < prebuffer_bytes ) {
memset ( buffer , 0 , bytes_to_copy ) ;
return ;
2017-01-20 23:08:05 +01:00
} else {
// resume playback
2018-07-20 16:13:52 +02:00
audio_output_paused = 0 ;
2017-01-20 23:08:05 +01:00
}
}
// get data from ringbuffer
uint32_t bytes_read = 0 ;
2018-07-20 16:13:52 +02:00
btstack_ring_buffer_read ( & audio_output_ring_buffer , ( uint8_t * ) buffer , bytes_to_copy , & bytes_read ) ;
2017-01-20 23:08:05 +01:00
bytes_to_copy - = bytes_read ;
// fill with 0 if not enough
if ( bytes_to_copy ) {
2018-07-20 16:13:52 +02:00
memset ( buffer + bytes_read , 0 , bytes_to_copy ) ;
audio_output_paused = 1 ;
2016-10-19 15:04:10 +02:00
}
2018-07-20 16:13:52 +02:00
}
2016-12-22 22:16:16 +01:00
2018-07-20 16:13:52 +02:00
# ifdef USE_AUDIO_INPUT
static void recording_callback ( const int16_t * buffer , uint16_t num_samples ) {
btstack_ring_buffer_write ( & audio_input_ring_buffer , ( uint8_t * ) buffer , num_samples * 2 ) ;
2017-01-20 23:08:05 +01:00
}
2018-07-20 16:13:52 +02:00
# endif
2017-01-20 23:08:05 +01:00
// return 1 if ok
2018-07-20 16:13:52 +02:00
static int audio_initialize ( int sample_rate ) {
// init buffers
memset ( audio_output_ring_buffer_storage , 0 , sizeof ( audio_output_ring_buffer_storage ) ) ;
btstack_ring_buffer_init ( & audio_output_ring_buffer , audio_output_ring_buffer_storage , sizeof ( audio_output_ring_buffer_storage ) ) ;
# ifdef USE_AUDIO_INPUT
memset ( audio_input_ring_buffer_storage , 0 , sizeof ( audio_input_ring_buffer_storage ) ) ;
btstack_ring_buffer_init ( & audio_input_ring_buffer , audio_input_ring_buffer_storage , sizeof ( audio_input_ring_buffer_storage ) ) ;
printf ( " Audio: Input buffer size %u \n " , btstack_ring_buffer_bytes_free ( & audio_input_ring_buffer ) ) ;
# endif
// config and setup audio playback/recording
const btstack_audio_t * audio = btstack_audio_get_instance ( ) ;
if ( ! audio ) return 0 ;
void ( * recording ) ( const int16_t * buffer , uint16_t num_samples ) = NULL ;
# ifdef USE_AUDIO_INPUT
recording = & recording_callback ;
# endif
audio - > init ( 1 , sample_rate , & playback_callback , recording ) ;
audio - > start_stream ( ) ;
audio_output_paused = 1 ;
# ifdef USE_AUDIO_INPUT
audio_input_paused = 1 ;
2017-01-25 16:58:03 +01:00
# endif
2017-01-20 23:08:05 +01:00
return 1 ;
}
2017-01-25 16:58:03 +01:00
2018-07-20 16:13:52 +02:00
static void audio_terminate ( void ) {
const btstack_audio_t * audio = btstack_audio_get_instance ( ) ;
if ( ! audio ) return ;
audio - > close ( ) ;
2017-01-25 16:58:03 +01:00
}
2017-01-20 23:08:05 +01:00
2017-01-30 17:49:47 +01:00
# if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
2017-01-20 23:08:05 +01:00
2017-03-20 10:57:13 +01:00
# ifdef ENABLE_HFP_WIDE_BAND_SPEECH
2017-01-20 23:08:05 +01:00
static void handle_pcm_data ( int16_t * data , int num_samples , int num_channels , int sample_rate , void * context ) {
UNUSED ( context ) ;
UNUSED ( sample_rate ) ;
2017-01-30 17:49:47 +01:00
UNUSED ( data ) ;
UNUSED ( num_samples ) ;
UNUSED ( num_channels ) ;
# if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
2017-01-20 23:08:05 +01:00
// printf("handle_pcm_data num samples %u, sample rate %d\n", num_samples, num_channels);
2018-07-20 16:13:52 +02:00
// samples in callback in host endianess, ready for playback
btstack_ring_buffer_write ( & audio_output_ring_buffer , ( uint8_t * ) data , num_samples * num_channels * 2 ) ;
2016-10-19 15:04:10 +02:00
2017-01-30 17:49:47 +01:00
# ifdef SCO_WAV_FILENAME
2016-07-08 17:11:57 +02:00
if ( ! num_samples_to_write ) return ;
num_samples = btstack_min ( num_samples , num_samples_to_write ) ;
num_samples_to_write - = num_samples ;
2016-09-19 14:40:47 +02:00
wav_writer_write_int16 ( num_samples , data ) ;
2016-07-08 17:11:57 +02:00
if ( num_samples_to_write = = 0 ) {
2017-01-30 17:49:47 +01:00
wav_writer_close ( ) ;
2016-07-08 17:11:57 +02:00
}
2017-03-20 10:57:13 +01:00
# endif /* SCO_WAV_FILENAME */
2017-01-30 17:49:47 +01:00
2017-03-20 10:57:13 +01:00
# endif /* Demo mode sine or microphone */
2016-07-08 17:11:57 +02:00
}
2017-03-20 10:57:13 +01:00
# endif /* ENABLE_HFP_WIDE_BAND_SPEECH */
# ifdef ENABLE_HFP_WIDE_BAND_SPEECH
2016-07-08 17:11:57 +02:00
static void sco_demo_init_mSBC ( void ) {
2017-01-20 23:08:05 +01:00
printf ( " SCO Demo: Init mSBC \n " ) ;
2016-09-19 14:40:47 +02:00
btstack_sbc_decoder_init ( & decoder_state , SBC_MODE_mSBC , & handle_pcm_data , NULL ) ;
2017-01-30 17:49:47 +01:00
hfp_msbc_init ( ) ;
2016-07-08 17:11:57 +02:00
2017-01-30 17:49:47 +01:00
# ifdef SCO_WAV_FILENAME
2017-01-20 23:08:05 +01:00
num_samples_to_write = MSBC_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS ;
2017-01-30 17:49:47 +01:00
wav_writer_open ( SCO_WAV_FILENAME , 1 , MSBC_SAMPLE_RATE ) ;
# endif
2017-01-25 16:58:03 +01:00
# if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
2017-01-25 17:23:55 +01:00
sco_demo_msbc_fill_sine_audio_frame ( ) ;
2017-01-25 16:58:03 +01:00
# endif
2016-07-24 22:49:47 +02:00
2016-08-09 18:09:44 +02:00
# ifdef SCO_MSBC_IN_FILENAME
msbc_file_in = fopen ( SCO_MSBC_IN_FILENAME , " wb " ) ;
printf ( " SCO Demo: creating mSBC in file %s, %p \n " , SCO_MSBC_IN_FILENAME , msbc_file_in ) ;
# endif
2017-01-25 16:58:03 +01:00
2016-07-26 00:24:03 +02:00
# ifdef SCO_MSBC_OUT_FILENAME
2016-08-09 18:09:44 +02:00
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 ) ;
2016-07-26 00:24:03 +02:00
# endif
2016-10-19 15:04:10 +02:00
2018-07-20 16:13:52 +02:00
audio_initialize ( MSBC_SAMPLE_RATE ) ;
2016-07-08 17:11:57 +02:00
}
static void sco_demo_receive_mSBC ( uint8_t * packet , uint16_t size ) {
if ( num_samples_to_write ) {
2016-08-09 18:09:44 +02:00
if ( msbc_file_in ) {
// log incoming mSBC data for testing
fwrite ( packet + 3 , size - 3 , 1 , msbc_file_in ) ;
}
2016-07-08 17:11:57 +02:00
}
2016-10-19 15:04:10 +02:00
btstack_sbc_decoder_process_data ( & decoder_state , ( packet [ 1 ] > > 4 ) & 3 , packet + 3 , size - 3 ) ;
2016-07-08 17:11:57 +02:00
}
2017-03-20 10:57:13 +01:00
# endif
2016-07-08 17:11:57 +02:00
2016-09-19 14:40:47 +02:00
static void sco_demo_init_CVSD ( void ) {
2017-01-20 23:08:05 +01:00
printf ( " SCO Demo: Init CVSD \n " ) ;
2016-09-19 14:40:47 +02:00
btstack_cvsd_plc_init ( & cvsd_plc_state ) ;
2016-10-19 15:04:10 +02:00
2017-01-30 17:49:47 +01:00
# ifdef SCO_WAV_FILENAME
2017-01-20 23:08:05 +01:00
num_samples_to_write = CVSD_SAMPLE_RATE * SCO_WAV_DURATION_IN_SECONDS ;
2017-01-30 17:49:47 +01:00
wav_writer_open ( SCO_WAV_FILENAME , 1 , CVSD_SAMPLE_RATE ) ;
# endif
2016-10-19 15:04:10 +02:00
2018-07-20 16:13:52 +02:00
audio_initialize ( CVSD_SAMPLE_RATE ) ;
2016-09-19 14:40:47 +02:00
}
2016-07-08 17:11:57 +02:00
static void sco_demo_receive_CVSD ( uint8_t * packet , uint16_t size ) {
2016-10-19 15:04:10 +02:00
if ( ! num_samples_to_write ) return ;
2017-01-30 17:49:47 +01:00
2017-01-26 22:02:33 +01:00
int16_t audio_frame_out [ 128 ] ; //
2017-01-19 12:27:44 +01:00
if ( size > sizeof ( audio_frame_out ) ) {
printf ( " sco_demo_receive_CVSD: SCO packet larger than local output buffer - dropping data. \n " ) ;
return ;
}
2017-01-30 17:49:47 +01:00
2017-01-20 23:08:05 +01:00
const int audio_bytes_read = size - 3 ;
2018-07-20 16:13:52 +02:00
const int num_samples = audio_bytes_read / BYTES_PER_FRAME ;
2017-01-26 22:02:33 +01:00
// convert into host endian
int16_t audio_frame_in [ 128 ] ;
int i ;
for ( i = 0 ; i < num_samples ; i + + ) {
audio_frame_in [ i ] = little_endian_read_16 ( packet , 3 + i * 2 ) ;
}
2017-01-30 15:16:42 +01:00
btstack_cvsd_plc_process_data ( & cvsd_plc_state , audio_frame_in , num_samples , audio_frame_out ) ;
2017-01-26 22:02:33 +01:00
2017-01-30 17:49:47 +01:00
# ifdef SCO_WAV_FILENAME
// Samples in CVSD SCO packet are in little endian, ready for wav files (take shortcut)
const int samples_to_write = btstack_min ( num_samples , num_samples_to_write ) ;
wav_writer_write_le_int16 ( samples_to_write , audio_frame_out ) ;
num_samples_to_write - = samples_to_write ;
if ( num_samples_to_write = = 0 ) {
wav_writer_close ( ) ;
}
# endif
2018-07-20 16:13:52 +02:00
btstack_ring_buffer_write ( & audio_output_ring_buffer , ( uint8_t * ) audio_frame_out , audio_bytes_read ) ;
2016-07-08 17:11:57 +02:00
}
2017-01-30 17:49:47 +01:00
# endif
2016-09-13 15:58:17 +02:00
void sco_demo_close ( void ) {
2017-01-20 23:08:05 +01:00
printf ( " SCO demo close \n " ) ;
2017-01-25 16:58:03 +01:00
2016-09-13 15:58:17 +02:00
printf ( " SCO demo statistics: " ) ;
2017-03-20 10:57:13 +01:00
# ifdef ENABLE_HFP_WIDE_BAND_SPEECH
2016-09-13 15:58:17 +02:00
if ( negotiated_codec = = HFP_CODEC_MSBC ) {
2017-01-30 17:49:47 +01:00
printf ( " Used mSBC with PLC, number of processed frames: \n - %d good frames, \n - %d zero frames, \n - %d bad frames. \n " , decoder_state . good_frames_nr , decoder_state . zero_frames_nr , decoder_state . bad_frames_nr ) ;
2017-03-20 10:57:13 +01:00
} else
# endif
{
2017-01-30 17:49:47 +01:00
printf ( " Used CVSD with PLC, number of proccesed frames: \n - %d good frames, \n - %d bad frames. \n " , cvsd_plc_state . good_frames_nr , cvsd_plc_state . bad_frames_nr ) ;
2016-09-13 15:58:17 +02:00
}
2017-01-30 17:49:47 +01:00
negotiated_codec = - 1 ;
2017-01-20 23:08:05 +01:00
2017-01-30 17:49:47 +01:00
# if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
# if defined(SCO_WAV_FILENAME)
wav_writer_close ( ) ;
2016-07-08 17:11:57 +02:00
# endif
2017-01-20 23:08:05 +01:00
2018-07-20 16:13:52 +02:00
audio_terminate ( ) ;
2017-01-20 23:08:05 +01:00
2016-07-08 17:11:57 +02:00
# endif
}
void sco_demo_set_codec ( uint8_t codec ) {
if ( negotiated_codec = = codec ) return ;
negotiated_codec = codec ;
2017-01-30 17:49:47 +01:00
2017-01-25 16:58:03 +01:00
# if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
2016-07-23 16:29:33 +02:00
if ( negotiated_codec = = HFP_CODEC_MSBC ) {
2017-03-20 10:57:13 +01:00
# ifdef ENABLE_HFP_WIDE_BAND_SPEECH
2016-07-23 16:29:33 +02:00
sco_demo_init_mSBC ( ) ;
2017-03-20 10:57:13 +01:00
# endif
2016-07-23 16:29:33 +02:00
} else {
sco_demo_init_CVSD ( ) ;
}
# endif
2016-07-08 17:11:57 +02:00
}
2016-06-14 12:44:29 +02:00
2016-05-23 18:04:57 +02:00
void sco_demo_init ( void ) {
// status
2017-01-25 16:58:03 +01:00
# if SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE
2018-07-20 16:13:52 +02:00
printf ( " SCO Demo: Sending and receiving audio via btstack_audio. \n " ) ;
2017-01-25 16:58:03 +01:00
# endif
2016-05-23 18:04:57 +02:00
# if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
# ifdef HAVE_PORTAUDIO
2018-07-20 16:13:52 +02:00
printf ( " SCO Demo: Sending sine wave, audio output via btstack_audio. \n " ) ;
2016-05-23 18:04:57 +02:00
# else
printf ( " SCO Demo: Sending sine wave, hexdump received data. \n " ) ;
# endif
# endif
# if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
printf ( " SCO Demo: Sending ASCII blocks, print received data. \n " ) ;
# endif
# if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
printf ( " SCO Demo: Sending counter value, hexdump received data. \n " ) ;
# endif
2017-01-25 16:58:03 +01:00
# if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
2017-01-20 23:08:05 +01:00
hci_set_sco_voice_setting ( 0x60 ) ; // linear, unsigned, 16-bit, CVSD
# else
2016-05-23 18:04:57 +02:00
hci_set_sco_voice_setting ( 0x03 ) ; // linear, unsigned, 8-bit, transparent
2016-07-26 00:24:03 +02:00
# endif
2016-05-23 18:04:57 +02:00
# if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
phase = ' a ' ;
# endif
}
2016-12-19 14:35:28 +01:00
void sco_report ( void ) ;
void sco_report ( void ) {
2016-06-16 11:43:40 +02:00
printf ( " SCO: sent %u, received %u \n " , count_sent , count_received ) ;
}
2016-05-23 18:04:57 +02:00
void sco_demo_send ( hci_con_handle_t sco_handle ) {
if ( ! sco_handle ) return ;
2017-01-20 23:08:05 +01:00
int sco_packet_length = hci_get_sco_packet_length ( ) ;
int sco_payload_length = sco_packet_length - 3 ;
2016-05-23 18:04:57 +02:00
hci_reserve_packet_buffer ( ) ;
uint8_t * sco_packet = hci_get_outgoing_packet_buffer ( ) ;
# if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE
2017-03-20 10:57:13 +01:00
# ifdef ENABLE_HFP_WIDE_BAND_SPEECH
2016-07-23 16:29:33 +02:00
if ( negotiated_codec = = HFP_CODEC_MSBC ) {
2017-01-20 23:08:05 +01:00
// overwrite
sco_payload_length = 24 ;
sco_packet_length = sco_payload_length + 3 ;
2016-07-23 16:29:33 +02:00
if ( hfp_msbc_num_bytes_in_stream ( ) < sco_payload_length ) {
log_error ( " mSBC stream is empty. " ) ;
}
hfp_msbc_read_from_stream ( sco_packet + 3 , sco_payload_length ) ;
2016-08-09 18:09:44 +02:00
if ( msbc_file_out ) {
2016-07-26 09:25:37 +02:00
// log outgoing mSBC data for testing
2016-08-09 18:09:44 +02:00
fwrite ( sco_packet + 3 , sco_payload_length , 1 , msbc_file_out ) ;
2016-07-26 09:25:37 +02:00
}
2016-07-26 00:24:03 +02:00
2017-01-25 17:23:55 +01:00
sco_demo_msbc_fill_sine_audio_frame ( ) ;
2017-03-20 10:57:13 +01:00
} else
# endif
{
2018-07-20 16:13:52 +02:00
const int audio_samples_per_packet = sco_payload_length / BYTES_PER_FRAME ;
2017-08-09 22:58:56 +02:00
sco_demo_sine_wave_int16_at_8000_hz_little_endian ( audio_samples_per_packet , & sco_packet [ 3 ] ) ;
2016-05-23 18:04:57 +02:00
}
2016-12-19 14:35:28 +01:00
# endif
2017-01-25 16:58:03 +01:00
# 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 ;
2018-07-20 16:13:52 +02:00
if ( audio_input_paused ) {
if ( btstack_ring_buffer_bytes_available ( & audio_input_ring_buffer ) > = MSBC_PA_PREBUFFER_BYTES ) {
2017-01-25 17:23:55 +01:00
// resume sending
2018-07-20 16:13:52 +02:00
audio_input_paused = 0 ;
2017-01-25 17:23:55 +01:00
}
2017-01-25 16:58:03 +01:00
}
2017-01-25 17:23:55 +01:00
2018-07-20 16:13:52 +02:00
if ( ! audio_input_paused ) {
2017-01-25 17:23:55 +01:00
int num_samples = hfp_msbc_num_audio_samples_per_frame ( ) ;
2018-07-20 16:13:52 +02:00
if ( num_samples > MAX_NUM_MSBC_SAMPLES ) return ; // assert
if ( hfp_msbc_can_encode_audio_frame_now ( ) & & btstack_ring_buffer_bytes_available ( & audio_input_ring_buffer ) > = ( unsigned int ) ( num_samples * BYTES_PER_FRAME ) ) {
int16_t sample_buffer [ MAX_NUM_MSBC_SAMPLES ] ;
2017-01-25 17:23:55 +01:00
uint32_t bytes_read ;
2018-07-20 16:13:52 +02:00
btstack_ring_buffer_read ( & audio_input_ring_buffer , ( uint8_t * ) sample_buffer , num_samples * BYTES_PER_FRAME , & bytes_read ) ;
2017-01-25 17:23:55 +01:00
hfp_msbc_encode_audio_frame ( sample_buffer ) ;
num_audio_frames + + ;
}
2018-07-20 16:13:52 +02:00
if ( hfp_msbc_num_bytes_in_stream ( ) < sco_payload_length ) {
log_error ( " mSBC stream should not be empty. " ) ;
}
2017-01-25 17:23:55 +01:00
}
2018-07-20 16:13:52 +02:00
if ( audio_input_paused | | hfp_msbc_num_bytes_in_stream ( ) < sco_payload_length ) {
2017-01-25 17:23:55 +01:00
memset ( sco_packet + 3 , 0 , sco_payload_length ) ;
2018-07-20 16:13:52 +02:00
audio_input_paused = 1 ;
2017-01-25 17:23:55 +01:00
} 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 ) ;
}
2017-01-25 16:58:03 +01:00
}
} else {
// CVSD
2018-07-20 16:13:52 +02:00
log_info ( " send: bytes avail %u, free %u " , btstack_ring_buffer_bytes_available ( & audio_input_ring_buffer ) , btstack_ring_buffer_bytes_free ( & audio_input_ring_buffer ) ) ;
2017-01-25 16:58:03 +01:00
// fill with silence while paused
int bytes_to_copy = sco_payload_length ;
2018-07-20 16:13:52 +02:00
if ( audio_input_paused ) {
if ( btstack_ring_buffer_bytes_available ( & audio_input_ring_buffer ) > = CVSD_PA_PREBUFFER_BYTES ) {
2017-01-25 16:58:03 +01:00
// resume sending
2018-07-20 16:13:52 +02:00
audio_input_paused = 0 ;
2017-01-25 16:58:03 +01:00
}
}
// get data from ringbuffer
uint16_t pos = 0 ;
2017-01-26 22:21:12 +01:00
uint8_t * sample_data = & sco_packet [ 3 ] ;
2018-07-20 16:13:52 +02:00
if ( ! audio_input_paused ) {
2017-01-25 16:58:03 +01:00
uint32_t bytes_read = 0 ;
2018-07-20 16:13:52 +02:00
btstack_ring_buffer_read ( & audio_input_ring_buffer , sample_data , bytes_to_copy , & bytes_read ) ;
2017-01-26 22:21:12 +01:00
// flip 16 on big endian systems
// @note We don't use (uint16_t *) casts since all sample addresses are odd which causes crahses on some systems
if ( btstack_is_big_endian ( ) ) {
2018-07-10 12:31:12 +02:00
unsigned int i ;
2017-01-26 22:21:12 +01:00
for ( i = 0 ; i < bytes_read ; i + = 2 ) {
uint8_t tmp = sample_data [ i * 2 ] ;
sample_data [ i * 2 ] = sample_data [ i * 2 + 1 ] ;
sample_data [ i * 2 + 1 ] = tmp ;
}
}
2017-01-25 16:58:03 +01:00
bytes_to_copy - = bytes_read ;
pos + = bytes_read ;
}
// fill with 0 if not enough
if ( bytes_to_copy ) {
2017-01-26 22:21:12 +01:00
memset ( sample_data + pos , 0 , bytes_to_copy ) ;
2018-07-20 16:13:52 +02:00
audio_input_paused = 1 ;
2017-01-25 16:58:03 +01:00
}
}
# 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
2016-05-23 18:04:57 +02:00
# if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
2017-01-20 23:08:05 +01:00
memset ( & sco_packet [ 3 ] , phase + + , sco_payload_length ) ;
2016-05-23 18:04:57 +02:00
if ( phase > ' z ' ) phase = ' a ' ;
2016-12-19 14:35:28 +01:00
# endif
# if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
2016-06-14 17:59:18 +02:00
int j ;
2017-01-20 23:08:05 +01:00
for ( j = 0 ; j < sco_payload_length ; j + + ) {
2016-06-14 17:59:18 +02:00
sco_packet [ 3 + j ] = phase + + ;
2016-05-23 18:04:57 +02:00
}
# endif
2016-12-19 14:35:28 +01:00
# if SCO_DEMO_MODE == SCO_DEMO_MODE_55
int j ;
2017-01-20 23:08:05 +01:00
for ( j = 0 ; j < sco_payload_length ; j + + ) {
2016-12-19 14:35:28 +01:00
// sco_packet[3+j] = j & 1 ? 0x35 : 0x53;
sco_packet [ 3 + j ] = 0x55 ;
}
# endif
# if SCO_DEMO_MODE == SCO_DEMO_MODE_00
int j ;
2017-01-20 23:08:05 +01:00
for ( j = 0 ; j < sco_payload_length ; j + + ) {
2016-12-19 14:35:28 +01:00
sco_packet [ 3 + j ] = 0x00 ;
}
// additional hack
// big_endian_store_16(sco_packet, 5, phase++);
( void ) phase ;
2016-05-23 18:04:57 +02:00
# endif
2016-07-23 16:29:33 +02:00
2017-01-25 16:58:03 +01:00
// test silence
// memset(sco_packet+3, 0, sco_payload_length);
2017-01-20 23:08:05 +01:00
// set handle + flags
little_endian_store_16 ( sco_packet , 0 , sco_handle ) ;
// set len
sco_packet [ 2 ] = sco_payload_length ;
// finally send packet
2016-05-23 18:04:57 +02:00
hci_send_sco_packet_buffer ( sco_packet_length ) ;
// request another send event
hci_request_sco_can_send_now_event ( ) ;
2016-06-16 11:43:40 +02:00
count_sent + + ;
2016-12-19 14:35:28 +01:00
# if SCO_DEMO_MODE != SCO_DEMO_MODE_55
2016-06-16 11:43:40 +02:00
if ( ( count_sent % SCO_REPORT_PERIOD ) = = 0 ) sco_report ( ) ;
2016-12-19 14:35:28 +01:00
# endif
2016-05-23 18:04:57 +02:00
}
/**
* @ brief Process received data
*/
2016-12-19 14:35:28 +01:00
# define ANSI_COLOR_RED "\x1b[31m"
# define ANSI_COLOR_GREEN "\x1b[32m"
# define ANSI_COLOR_YELLOW "\x1b[33m"
# define ANSI_COLOR_BLUE "\x1b[34m"
# define ANSI_COLOR_MAGENTA "\x1b[35m"
# define ANSI_COLOR_CYAN "\x1b[36m"
# define ANSI_COLOR_RESET "\x1b[0m"
2016-05-23 18:04:57 +02:00
void sco_demo_receive ( uint8_t * packet , uint16_t size ) {
2016-07-08 17:11:57 +02:00
dump_data = 1 ;
2016-06-14 12:44:29 +02:00
2016-06-16 11:43:40 +02:00
count_received + + ;
2016-12-19 14:35:28 +01:00
static uint32_t packets = 0 ;
static uint32_t crc_errors = 0 ;
static uint32_t data_received = 0 ;
static uint32_t byte_errors = 0 ;
data_received + = size - 3 ;
packets + + ;
if ( data_received > 100000 ) {
2017-02-01 15:02:28 +01:00
printf ( " Summary: data %07u, packets %04u, packet with crc errors %0u, byte errors %04u \n " , ( unsigned int ) data_received , ( unsigned int ) packets , ( unsigned int ) crc_errors , ( unsigned int ) byte_errors ) ;
2016-12-19 14:35:28 +01:00
crc_errors = 0 ;
byte_errors = 0 ;
data_received = 0 ;
packets = 0 ;
}
2016-06-16 11:43:40 +02:00
2017-01-25 16:58:03 +01:00
# if (SCO_DEMO_MODE == SCO_DEMO_MODE_SINE) || (SCO_DEMO_MODE == SCO_DEMO_MODE_MICROPHONE)
2017-01-20 23:08:05 +01:00
switch ( negotiated_codec ) {
2017-03-20 10:57:13 +01:00
# ifdef ENABLE_HFP_WIDE_BAND_SPEECH
2017-01-20 23:08:05 +01:00
case HFP_CODEC_MSBC :
2017-01-25 16:58:03 +01:00
sco_demo_receive_mSBC ( packet , size ) ;
2017-01-20 23:08:05 +01:00
break ;
2017-03-20 10:57:13 +01:00
# endif
2017-01-20 23:08:05 +01:00
case HFP_CODEC_CVSD :
2017-01-25 16:58:03 +01:00
sco_demo_receive_CVSD ( packet , size ) ;
2017-01-20 23:08:05 +01:00
break ;
default :
break ;
2016-06-14 12:44:29 +02:00
}
2016-10-19 15:04:10 +02:00
dump_data = 0 ;
2016-06-14 12:44:29 +02:00
# endif
2016-08-31 15:43:29 +02:00
if ( packet [ 1 ] & 0x30 ) {
2016-12-19 14:35:28 +01:00
crc_errors + + ;
// printf("SCO CRC Error: %x - data: ", (packet[1] & 0x30) >> 4);
// printf_hexdump(&packet[3], size-3);
2016-06-14 12:44:29 +02:00
return ;
}
if ( dump_data ) {
# if SCO_DEMO_MODE == SCO_DEMO_MODE_ASCII
2016-12-19 14:35:28 +01:00
printf ( " data: " ) ;
2016-06-14 12:44:29 +02:00
int i ;
for ( i = 3 ; i < size ; i + + ) {
printf ( " %c " , packet [ i ] ) ;
}
printf ( " \n " ) ;
dump_data = 0 ;
2016-12-19 14:35:28 +01:00
# endif
# if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER
// colored hexdump with expected
static uint8_t expected_byte = 0 ;
int i ;
printf ( " data: " ) ;
for ( i = 3 ; i < size ; i + + ) {
if ( packet [ i ] ! = expected_byte ) {
printf ( ANSI_COLOR_RED " %02x " ANSI_COLOR_RESET , packet [ i ] ) ;
} else {
printf ( " %02x " , packet [ i ] ) ;
}
expected_byte = packet [ i ] + 1 ;
}
printf ( " \n " ) ;
# endif
2017-01-19 11:55:07 +01:00
# if SCO_DEMO_MODE == SCO_DEMO_MODE_55 || SCO_DEMO_MODE == SCO_DEMO_MODE_00
2016-12-19 14:35:28 +01:00
int i ;
int contains_error = 0 ;
for ( i = 3 ; i < size ; i + + ) {
if ( packet [ i ] ! = 0x00 & & packet [ i ] ! = 0x35 & & packet [ i ] ! = 0x53 & & packet [ i ] ! = 0x55 ) {
contains_error = 1 ;
byte_errors + + ;
}
}
if ( contains_error ) {
printf ( " data: " ) ;
for ( i = 0 ; i < 3 ; i + + ) {
printf ( " %02x " , packet [ i ] ) ;
}
for ( i = 3 ; i < size ; i + + ) {
if ( packet [ i ] ! = 0x00 & & packet [ i ] ! = 0x35 & & packet [ i ] ! = 0x53 & & packet [ i ] ! = 0x55 ) {
printf ( ANSI_COLOR_RED " %02x " ANSI_COLOR_RESET , packet [ i ] ) ;
} else {
printf ( " %02x " , packet [ i ] ) ;
}
}
printf ( " \n " ) ;
}
2016-05-23 18:04:57 +02:00
# endif
2016-06-14 12:44:29 +02:00
}
2016-05-23 18:04:57 +02:00
}