static void hal_audio_dma_process(btstack_data_source_t * ds, btstack_data_source_callback_type_t callback_type){ UNUSED(ds); UNUSED(callback_type); if (!media_initialized) return; int trigger_resume = 0; if (audio_stream_paused) { if (sbc_frame_size && btstack_ring_buffer_bytes_available(&ring_buffer) >= OPTIMAL_FRAMES_MIN * sbc_frame_size){ trigger_resume = 1; // reset buffers playback_buffer = NUM_AUDIO_BUFFERS - 1; write_buffer = 0; } else { return; } } while (playback_buffer != write_buffer && btstack_ring_buffer_bytes_available(&ring_buffer) >= sbc_frame_size ){ uint8_t frame[MAX_SBC_FRAME_SIZE]; uint32_t bytes_read = 0; btstack_ring_buffer_read(&ring_buffer, frame, sbc_frame_size, &bytes_read); btstack_sbc_decoder_process_data(&state, 0, frame, sbc_frame_size); } if (trigger_resume){ printf("%6u - resume\n", (int) btstack_run_loop_get_time_ms()); audio_stream_paused = 0; } }
static int portaudio_callback( 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; (void) userData; // output part // config based on codec int bytes_to_copy; uint32_t prebuffer_bytes; 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; } // fill with silence while paused if (pa_output_paused){ if (btstack_ring_buffer_bytes_available(&pa_output_ring_buffer) < prebuffer_bytes){ memset(outputBuffer, 0, bytes_to_copy); return 0; } else { // resume playback pa_output_paused = 0; } } // 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; }
static void handle_l2cap_media_data_packet(uint8_t seid, uint8_t *packet, uint16_t size){ UNUSED(seid); int pos = 0; avdtp_media_packet_header_t media_header; media_header.version = packet[pos] & 0x03; media_header.padding = get_bit16(packet[pos],2); media_header.extension = get_bit16(packet[pos],3); media_header.csrc_count = (packet[pos] >> 4) & 0x0F; pos++; media_header.marker = get_bit16(packet[pos],0); media_header.payload_type = (packet[pos] >> 1) & 0x7F; pos++; media_header.sequence_number = big_endian_read_16(packet, pos); pos+=2; media_header.timestamp = big_endian_read_32(packet, pos); pos+=4; media_header.synchronization_source = big_endian_read_32(packet, pos); pos+=4; UNUSED(media_header); // 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); avdtp_sbc_codec_header_t sbc_header; sbc_header.fragmentation = get_bit16(packet[pos], 7); sbc_header.starting_packet = get_bit16(packet[pos], 6); sbc_header.last_packet = get_bit16(packet[pos], 5); sbc_header.num_frames = packet[pos] & 0x0f; pos++; UNUSED(sbc_header); // 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 DECODE_SBC btstack_sbc_decoder_process_data(&state, 0, packet+pos, size-pos); #endif #ifdef STORE_SBC_TO_SBC_FILE fwrite(packet+pos, size-pos, 1, sbc_file); #endif #ifdef HAVE_PORTAUDIO log_info("PA: bytes avail after recv: %d", btstack_ring_buffer_bytes_available(&ring_buffer)); #endif }
static void transport_deliver_packets(void){ xSemaphoreTake(ring_buffer_mutex, portMAX_DELAY); while (btstack_ring_buffer_bytes_available(&hci_ringbuffer)){ uint32_t number_read; uint8_t len_tag[2]; btstack_ring_buffer_read(&hci_ringbuffer, len_tag, 2, &number_read); uint32_t len = little_endian_read_16(len_tag, 0); btstack_ring_buffer_read(&hci_ringbuffer, hci_receive_buffer, len, &number_read); xSemaphoreGive(ring_buffer_mutex); transport_packet_handler(hci_receive_buffer[0], &hci_receive_buffer[1], len-1); xSemaphoreTake(ring_buffer_mutex, portMAX_DELAY); } xSemaphoreGive(ring_buffer_mutex); }
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; (void) userData; uint32_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); } return paContinue; }
static void handle_l2cap_media_data_packet(uint8_t seid, uint8_t *packet, uint16_t size){ UNUSED(seid); int pos = 0; avdtp_media_packet_header_t media_header; if (!read_media_data_header(packet, size, &pos, &media_header)) return; avdtp_sbc_codec_header_t sbc_header; if (!read_sbc_header(packet, size, &pos, &sbc_header)) return; #ifdef HAVE_AUDIO_DMA // store sbc frame size for buffer management sbc_frame_size = (size-pos)/ sbc_header.num_frames; #endif #if defined(HAVE_PORTAUDIO) || defined(STORE_SBC_TO_WAV_FILE) btstack_sbc_decoder_process_data(&state, 0, packet+pos, size-pos); #endif #ifdef HAVE_AUDIO_DMA btstack_ring_buffer_write(&ring_buffer, packet+pos, size-pos); // decide on audio sync drift based on number of sbc frames in queue int sbc_frames_in_buffer = btstack_ring_buffer_bytes_available(&ring_buffer) / sbc_frame_size; if (sbc_frames_in_buffer < OPTIMAL_FRAMES_MIN){ sbc_samples_fix = 1; // duplicate last sample } else if (sbc_frames_in_buffer <= OPTIMAL_FRAMES_MAX){ sbc_samples_fix = 0; // nothing to do } else { sbc_samples_fix = -1; // drop last sample } // dump printf("%6u %03u %d\n", (int) btstack_run_loop_get_time_ms(), sbc_frames_in_buffer, sbc_samples_fix); #endif #ifdef STORE_SBC_TO_SBC_FILE fwrite(packet+pos, size-pos, 1, sbc_file); #endif }
static int patestCallback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ) { /** patestCallback is called from different thread, don't use hci_dump / log_info here without additional checks */ (void) timeInfo; /* Prevent unused variable warnings. */ (void) statusFlags; (void) inputBuffer; (void) userData; int bytes_to_copy = framesPerBuffer * BYTES_PER_FRAME; // fill with silence while paused if (pa_stream_paused){ if (btstack_ring_buffer_bytes_available(&ring_buffer) < PREBUFFER_BYTES){ // printf("PA: silence\n"); memset(outputBuffer, 0, bytes_to_copy); return 0; } else { // resume playback pa_stream_paused = 0; } } // get data from ringbuffer uint32_t bytes_read = 0; btstack_ring_buffer_read(&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_stream_paused = 1; } return 0; }
void hal_audio_dma_done(void){ if (audio_stream_paused){ hal_audio_dma_play((const uint8_t *) silent_buffer, DMA_AUDIO_FRAMES*4); return; } // next buffer int next_playback_buffer = next_buffer(playback_buffer); uint8_t * playback_data; if (next_playback_buffer == write_buffer){ // TODO: stop codec while playing silence when getting 'stream paused' // start playing silence audio_stream_paused = 1; hal_audio_dma_play((const uint8_t *) silent_buffer, DMA_AUDIO_FRAMES*4); printf("%6u - paused - bytes in buffer %u\n", (int) btstack_run_loop_get_time_ms(), btstack_ring_buffer_bytes_available(&ring_buffer)); return; } playback_buffer = next_playback_buffer; playback_data = start_of_buffer(playback_buffer); hal_audio_dma_play(playback_data, audio_samples_len[playback_buffer]); // btstack_run_loop_embedded_trigger(); }
void sco_demo_send(hci_con_handle_t sco_handle){ if (!sco_handle) return; int sco_packet_length = hci_get_sco_packet_length(); int sco_payload_length = sco_packet_length - 3; hci_reserve_packet_buffer(); uint8_t * sco_packet = hci_get_outgoing_packet_buffer(); #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE #ifdef ENABLE_HFP_WIDE_BAND_SPEECH 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){ log_error("mSBC stream is empty."); } 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); } sco_demo_msbc_fill_sine_audio_frame(); } else #endif { const int audio_samples_per_packet = sco_payload_length / CVSD_BYTES_PER_FRAME; sco_demo_sine_wave_int16_at_8000_hz_little_endian(audio_samples_per_packet, &sco_packet[3]); } #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; uint8_t * sample_data = &sco_packet[3]; if (!pa_input_paused){ uint32_t bytes_read = 0; btstack_ring_buffer_read(&pa_input_ring_buffer, sample_data, bytes_to_copy, &bytes_read); // 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()){ int i; 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; } } bytes_to_copy -= bytes_read; pos += bytes_read; } // fill with 0 if not enough if (bytes_to_copy){ memset(sample_data + 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 memset(&sco_packet[3], phase++, sco_payload_length); if (phase > 'z') phase = 'a'; #endif #if SCO_DEMO_MODE == SCO_DEMO_MODE_COUNTER int j; for (j=0;j<sco_payload_length;j++){ sco_packet[3+j] = phase++; } #endif #if SCO_DEMO_MODE == SCO_DEMO_MODE_55 int j; for (j=0;j<sco_payload_length;j++){ // sco_packet[3+j] = j & 1 ? 0x35 : 0x53; sco_packet[3+j] = 0x55; } #endif #if SCO_DEMO_MODE == SCO_DEMO_MODE_00 int j; for (j=0;j<sco_payload_length;j++){ sco_packet[3+j] = 0x00; } // additional hack // big_endian_store_16(sco_packet, 5, phase++); (void) phase; #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); // request another send event hci_request_sco_can_send_now_event(); count_sent++; #if SCO_DEMO_MODE != SCO_DEMO_MODE_55 if ((count_sent % SCO_REPORT_PERIOD) == 0) sco_report(); #endif }