introduce portaudio in avdtp_sink

This commit is contained in:
Milanka Ringwald 2016-10-18 16:21:41 +02:00
parent b07c2f619d
commit 2af9447338
3 changed files with 155 additions and 45 deletions

View File

@ -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

View File

@ -49,8 +49,88 @@
#include "btstack_sbc.h"
#include "wav_util.h"
#ifdef HAVE_PORTAUDIO
#include <portaudio.h>
#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);
}

View File

@ -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; i<TABLE_SIZE; i++){
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
data.sine[i] = sin(((double)i/(double)TABLE_SIZE) * M_PI * 2.)*32767;
}
data.left_phase = data.right_phase = 0;
@ -143,13 +138,12 @@ int main(int argc, const char * argv[]){
}
PaStreamParameters outputParameters;
/* -- 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,
@ -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));