mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-02-10 06:40:23 +00:00
introduce portaudio in avdtp_sink
This commit is contained in:
parent
b07c2f619d
commit
2af9447338
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user