From 2af9447338cb14b564341011a5198855b398b8d0 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Tue, 18 Oct 2016 16:21:41 +0200 Subject: [PATCH] introduce portaudio in avdtp_sink --- test/avdtp/Makefile | 2 +- test/avdtp/avdtp_sink.c | 176 ++++++++++++++++++++++++++++++------ test/avdtp/portaudio_test.c | 22 ++--- 3 files changed, 155 insertions(+), 45 deletions(-) diff --git a/test/avdtp/Makefile b/test/avdtp/Makefile index f7e518f8e..df4ed8cf4 100644 --- a/test/avdtp/Makefile +++ b/test/avdtp/Makefile @@ -71,7 +71,7 @@ SBC_ENCODER_OBJ = $(SBC_ENCODER:.c=.o) all: ${AVDTP_TESTS} -avdtp_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} avdtp_sink.o avdtp_test.o +avdtp_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} btstack_ring_buffer.o avdtp_sink.o avdtp_test.o ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ portaudio_test: btstack_util.o hci_dump.o wav_util.o btstack_ring_buffer.o portaudio_test.c diff --git a/test/avdtp/avdtp_sink.c b/test/avdtp/avdtp_sink.c index 4e5bf0f78..cfd30a7cd 100644 --- a/test/avdtp/avdtp_sink.c +++ b/test/avdtp/avdtp_sink.c @@ -49,8 +49,88 @@ #include "btstack_sbc.h" #include "wav_util.h" +#ifdef HAVE_PORTAUDIO +#include +#include "btstack_ring_buffer.h" +#endif + +#define NUM_CHANNELS 2 +#define SAMPLE_RATE 44100 + +#ifdef HAVE_POSIX_FILE_IO #define STORE_SBC_TO_SBC_FILE #define STORE_SBC_TO_WAV_FILE +#endif + +#ifdef HAVE_PORTAUDIO +#define PA_SAMPLE_TYPE paInt16 +#define FRAMES_PER_BUFFER 128 +#define BYTES_PER_FRAME (2*NUM_CHANNELS) +#define PREBUFFER_MS 150 +#define PREBUFFER_BYTES (PREBUFFER_MS*SAMPLE_RATE/1000*BYTES_PER_FRAME) +static uint8_t ring_buffer_storage[2*PREBUFFER_BYTES]; +static btstack_ring_buffer_t ring_buffer; + +static PaStream * stream; +static uint8_t pa_stream_started = 0; + +static int patestCallback( const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) { + (void) timeInfo; /* Prevent unused variable warnings. */ + (void) statusFlags; + (void) inputBuffer; + + uint16_t bytes_read = 0; + int bytes_per_buffer = framesPerBuffer * BYTES_PER_FRAME; + + if (btstack_ring_buffer_bytes_available(&ring_buffer) >= bytes_per_buffer){ + btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_per_buffer, &bytes_read); + } else { + printf("NOT ENOUGH DATA!\n"); + memset(outputBuffer, 0, bytes_per_buffer); + } + printf("bytes avail after read: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer)); + return 0; +} +#endif + +#ifdef STORE_SBC_TO_WAV_FILE +// store sbc as wav: +static btstack_sbc_decoder_state_t state; +static int total_num_samples = 0; +static int frame_count = 0; +static char * wav_filename = "avdtp_sink.wav"; +static btstack_sbc_mode_t mode = SBC_MODE_STANDARD; + +static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){ + wav_writer_write_int16(num_samples*num_channels, data); + total_num_samples+=num_samples*num_channels; + frame_count++; + +#ifdef HAVE_PORTAUDIO + if (!pa_stream_started && btstack_ring_buffer_bytes_available(&ring_buffer) >= PREBUFFER_BYTES){ + /* -- start stream -- */ + PaError err = Pa_StartStream(stream); + if (err != paNoError){ + printf("Error starting the stream: \"%s\"\n", Pa_GetErrorText(err)); + return; + } + pa_stream_started = 1; + } + btstack_ring_buffer_write(&ring_buffer, (uint8_t *)data, num_samples*num_channels*2); + printf("bytes avail after write: %d\n", btstack_ring_buffer_bytes_available(&ring_buffer)); +#endif +} +#endif + +#ifdef STORE_SBC_TO_SBC_FILE +static FILE * sbc_file; +static char * sbc_filename = "avdtp_sink.sbc"; +#endif + static const char * default_avdtp_sink_service_name = "BTstack AVDTP Sink Service"; static const char * default_avdtp_sink_service_provider_name = "BTstack AVDTP Sink Service Provider"; @@ -64,24 +144,6 @@ static btstack_packet_handler_t avdtp_sink_callback; static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); static void avdtp_sink_run_for_connection(avdtp_sink_connection_t *connection); -#ifdef STORE_SBC_TO_WAV_FILE -// store sbc as wav: -static btstack_sbc_decoder_state_t state; -static int total_num_samples = 0; -static int frame_count = 0; -static char * wav_filename = "avdtp_sink.wav"; -static btstack_sbc_mode_t mode = SBC_MODE_STANDARD; -static void handle_pcm_data(int16_t * data, int num_samples, int num_channels, int sample_rate, void * context){ - wav_writer_write_int16(num_samples, data); - total_num_samples+=num_samples; - frame_count++; -} -#endif - -#ifdef STORE_SBC_TO_SBC_FILE -static FILE * sbc_file; -static char * sbc_filename = "avdtp_sink.sbc"; -#endif void a2dp_sink_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name){ uint8_t* attribute; @@ -519,11 +581,11 @@ static void handle_l2cap_media_data_packet(avdtp_sink_connection_t * connection, // TODO: read csrc list - printf_hexdump( packet, pos ); - printf("MEDIA HEADER: %u timestamp, version %u, padding %u, extension %u, csrc_count %u\n", - media_header.timestamp, media_header.version, media_header.padding, media_header.extension, media_header.csrc_count); - printf("MEDIA HEADER: marker %02x, payload_type %02x, sequence_number %u, synchronization_source %u\n", - media_header.marker, media_header.payload_type, media_header.sequence_number, media_header.synchronization_source); + // printf_hexdump( packet, pos ); + // printf("MEDIA HEADER: %u timestamp, version %u, padding %u, extension %u, csrc_count %u\n", + // media_header.timestamp, media_header.version, media_header.padding, media_header.extension, media_header.csrc_count); + // printf("MEDIA HEADER: marker %02x, payload_type %02x, sequence_number %u, synchronization_source %u\n", + // media_header.marker, media_header.payload_type, media_header.sequence_number, media_header.synchronization_source); avdtp_sbc_codec_header_t sbc_header; sbc_header.fragmentation = get_bit16(packet[pos], 7); @@ -532,8 +594,8 @@ static void handle_l2cap_media_data_packet(avdtp_sink_connection_t * connection, sbc_header.num_frames = packet[pos] & 0x0f; pos++; - printf("SBC HEADER: num_frames %u, fragmented %u, start %u, stop %u\n", sbc_header.num_frames, sbc_header.fragmentation, sbc_header.starting_packet, sbc_header.last_packet); - printf_hexdump( packet+pos, size-pos ); + // printf("SBC HEADER: num_frames %u, fragmented %u, start %u, stop %u\n", sbc_header.num_frames, sbc_header.fragmentation, sbc_header.starting_packet, sbc_header.last_packet); + // printf_hexdump( packet+pos, size-pos ); #ifdef STORE_SBC_TO_WAV_FILE btstack_sbc_decoder_process_data(&state, 0, packet+pos, size-pos); #endif @@ -541,7 +603,6 @@ static void handle_l2cap_media_data_packet(avdtp_sink_connection_t * connection, #ifdef STORE_SBC_TO_SBC_FILE fwrite(packet+pos, size-pos, 1, sbc_file); #endif - } static void handle_l2cap_signaling_data_packet(avdtp_sink_connection_t * connection, uint8_t *packet, uint16_t size){ @@ -614,7 +675,7 @@ static void handle_l2cap_signaling_data_packet(avdtp_sink_connection_t * connect break; - default: + default:{ printf("NOT IMPLEMENTED signal_identifier %d\n", signaling_header.signal_identifier); printf_hexdump( packet, size ); #ifdef STORE_SBC_TO_WAV_FILE @@ -628,7 +689,28 @@ static void handle_l2cap_signaling_data_packet(avdtp_sink_connection_t * connect #ifdef STORE_SBC_TO_SBC_FILE fclose(sbc_file); #endif + +#ifdef HAVE_PORTAUDIO + 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 break; + } } return; } @@ -810,15 +892,49 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe // TODO: find out which security level is needed, and replace LEVEL_0 in avdtp_sink_init void avdtp_sink_init(void){ #ifdef STORE_SBC_TO_WAV_FILE - int num_channels = 2; - btstack_sbc_decoder_init(&state, mode, num_channels, &handle_pcm_data, NULL); - wav_writer_open(wav_filename, num_channels, 44100); + btstack_sbc_decoder_init(&state, mode, &handle_pcm_data, NULL); + wav_writer_open(wav_filename, NUM_CHANNELS, SAMPLE_RATE); #endif #ifdef STORE_SBC_TO_SBC_FILE sbc_file = fopen(sbc_filename, "wb"); #endif +#ifdef HAVE_PORTAUDIO + PaError err; + PaStreamParameters outputParameters; + + /* -- initialize PortAudio -- */ + err = Pa_Initialize(); + if (err != paNoError){ + printf("Error initializing portaudio: \"%s\"\n", Pa_GetErrorText(err)); + return; + } + /* -- setup input and output -- */ + outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ + outputParameters.channelCount = NUM_CHANNELS; + outputParameters.sampleFormat = PA_SAMPLE_TYPE; + outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + /* -- setup stream -- */ + err = Pa_OpenStream( + &stream, + NULL, /* &inputParameters */ + &outputParameters, + SAMPLE_RATE, + FRAMES_PER_BUFFER, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, /* use callback */ + NULL ); + + 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 l2cap_register_service(&packet_handler, PSM_AVDTP, 0xffff, LEVEL_0); } diff --git a/test/avdtp/portaudio_test.c b/test/avdtp/portaudio_test.c index f01cc5da6..959da2464 100644 --- a/test/avdtp/portaudio_test.c +++ b/test/avdtp/portaudio_test.c @@ -58,10 +58,9 @@ #define TABLE_SIZE 100 typedef struct { - float sine[TABLE_SIZE]; + int16_t sine[TABLE_SIZE]; int left_phase; int right_phase; - char message[20]; } paTestData; static uint8_t ring_buffer_storage[3*FRAMES_PER_BUFFER*BYTES_PER_FRAME]; @@ -76,17 +75,14 @@ static void write_wav_data(int16_t * data, int num_frames, int num_channels, int if (total_num_samples>5*SAMPLE_RATE) wav_writer_close(); } - static void fill_ring_buffer(void *userData){ paTestData *data = (paTestData*)userData; while (btstack_ring_buffer_bytes_free(&ring_buffer) > BYTES_PER_FRAME){ - int16_t left = data->sine[data->left_phase] * 32767; - int16_t right = data->sine[data->right_phase] * 32767; - uint8_t write_data[BYTES_PER_FRAME]; - *(int16_t*)&write_data[0] = left; - *(int16_t*)&write_data[2] = right; + *(int16_t*)&write_data[0] = data->sine[data->left_phase]; + *(int16_t*)&write_data[2] = data->sine[data->right_phase]; + btstack_ring_buffer_write(&ring_buffer, write_data, BYTES_PER_FRAME); write_wav_data((int16_t*)write_data, 1, NUM_CHANNELS, SAMPLE_RATE); @@ -116,7 +112,7 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, if (btstack_ring_buffer_bytes_available(&ring_buffer) >= bytes_per_buffer){ btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_per_buffer, &bytes_read); } else { - printf("NOT ENGOUGH DAT!\n"); + printf("NOT ENOUGH DATA!\n"); memset(outputBuffer, 0, bytes_per_buffer); } return paContinue; @@ -124,7 +120,6 @@ static int patestCallback( const void *inputBuffer, void *outputBuffer, int main(int argc, const char * argv[]){ - PaError err; static paTestData data; static PaStream * stream; @@ -132,7 +127,7 @@ int main(int argc, const char * argv[]){ /* initialise sinusoidal wavetable */ int i; for (i=0; idefaultHighOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; + /* -- setup stream -- */ err = Pa_OpenStream( &stream, @@ -159,7 +153,7 @@ int main(int argc, const char * argv[]){ FRAMES_PER_BUFFER, paClipOff, /* we won't output out of range samples so don't bother clipping them */ patestCallback, /* use callback */ - &data ); /* no callback userData yet */ + &data ); /* callback userData */ memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage)); btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));