2017-06-13 15:32:44 +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
*
*/
2019-05-11 19:31:55 +02:00
# define BTSTACK_FILE__ "a2dp_sink_demo.c"
2017-10-31 12:52:56 +01:00
/*
* a2dp_sink_demo . c
*/
// *****************************************************************************
2020-10-23 09:54:54 +02:00
/* EXAMPLE_START(a2dp_sink_demo): A2DP Sink - Receive Audio Stream and Control Playback
2017-10-31 12:52:56 +01:00
*
* @ text This A2DP Sink example demonstrates how to use the A2DP Sink service to
* receive an audio data stream from a remote A2DP Source device . In addition ,
* the AVRCP Controller is used to get information on currently played media ,
* such are title , artist and album , as well as to control the playback ,
2020-06-23 11:38:26 +02:00
* i . e . to play , stop , repeat , etc . If HAVE_BTSTACK_STDIN is set , press SPACE on
* the console to show the available AVDTP and AVRCP commands .
2017-11-01 12:25:25 +01:00
*
2020-06-23 11:38:26 +02:00
* @ text To test with a remote device , e . g . a mobile phone ,
2017-11-01 12:25:25 +01:00
* pair from the remote device with the demo , then start playing music on the remote device .
* Alternatively , set the device_addr_string to the Bluetooth address of your
* remote device in the code , and call connect from the UI .
*
2020-06-23 11:38:26 +02:00
* @ text For more info on BTstack audio , see our blog post
2020-03-25 10:06:25 +01:00
* [ A2DP Sink and Source on STM32 F4 Discovery Board ] ( http : //bluekitchen-gmbh.com/a2dp-sink-and-source-on-stm32-f4-discovery-board/).
*
2017-10-31 12:52:56 +01:00
*/
// *****************************************************************************
2017-06-13 15:32:44 +02:00
2018-01-12 15:54:03 +01:00
# include <inttypes.h>
2017-06-13 15:32:44 +02:00
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2017-07-21 14:48:45 +02:00
# include "btstack.h"
2019-02-21 12:03:23 +01:00
# include "btstack_resample.h"
2017-06-13 15:32:44 +02:00
2019-02-18 11:45:09 +01:00
//#define AVRCP_BROWSING_ENABLED
2020-06-26 16:53:55 +02:00
// if volume control not supported by btstack_audio_sink, you can try to disable volume change notification
// to force the A2DP Source to reduce volume by attenuating the audio stream
2020-06-26 11:34:54 +02:00
# define SUPPORT_VOLUME_CHANGE_NOTIFICATION
2017-06-13 15:32:44 +02:00
# ifdef HAVE_BTSTACK_STDIN
# include "btstack_stdin.h"
# endif
# include "btstack_ring_buffer.h"
# ifdef HAVE_POSIX_FILE_IO
# include "wav_util.h"
2019-03-04 22:25:57 +01:00
# define STORE_TO_SBC_FILE
2019-03-04 22:19:03 +01:00
# define STORE_TO_WAV_FILE
2017-06-13 15:32:44 +02:00
# endif
# define NUM_CHANNELS 2
# define BYTES_PER_FRAME (2*NUM_CHANNELS)
# define MAX_SBC_FRAME_SIZE 120
2018-07-20 16:13:33 +02:00
// SBC Decoder for WAV file or live playback
2017-06-13 15:32:44 +02:00
static btstack_sbc_decoder_state_t state ;
static btstack_sbc_mode_t mode = SBC_MODE_STANDARD ;
2018-07-20 16:13:33 +02:00
// ring buffer for SBC Frames
2017-06-13 15:32:44 +02:00
// below 30: add samples, 30-40: fine, above 40: drop samples
# define OPTIMAL_FRAMES_MIN 30
# define OPTIMAL_FRAMES_MAX 40
2019-02-21 12:03:23 +01:00
# define ADDITIONAL_FRAMES 20
2018-07-20 16:13:33 +02:00
static uint8_t sbc_frame_storage [ ( OPTIMAL_FRAMES_MAX + ADDITIONAL_FRAMES ) * MAX_SBC_FRAME_SIZE ] ;
static btstack_ring_buffer_t sbc_frame_ring_buffer ;
static unsigned int sbc_frame_size ;
2017-06-13 15:32:44 +02:00
2019-02-21 12:03:23 +01:00
// rest buffer for not fully used sbc frames, with additional frames for resampling
static uint8_t decoded_audio_storage [ ( 128 + 16 ) * BYTES_PER_FRAME ] ;
2018-07-20 16:13:33 +02:00
static btstack_ring_buffer_t decoded_audio_ring_buffer ;
2020-06-23 11:38:26 +02:00
2018-07-20 16:13:33 +02:00
static int audio_stream_started ;
// temp storage of lower-layer request
static int16_t * request_buffer ;
2019-02-21 12:03:23 +01:00
static int request_frames ;
# define STORE_FROM_PLAYBACK
2017-06-13 15:32:44 +02:00
// WAV File
2020-03-19 10:47:49 +01:00
# ifdef STORE_TO_WAV_FILE
static uint32_t audio_frame_count = 0 ;
static char * wav_filename = " av2dp_sink_demo.wav " ;
2017-06-13 15:32:44 +02:00
# endif
2019-03-04 22:25:57 +01:00
# ifdef STORE_TO_SBC_FILE
2017-06-13 15:32:44 +02:00
static FILE * sbc_file ;
2020-03-19 10:47:49 +01:00
static char * sbc_filename = " av2dp_sink_demo.sbc " ;
2017-06-13 15:32:44 +02:00
# endif
typedef struct {
int reconfigure ;
2020-11-19 13:25:14 +01:00
2017-06-13 15:32:44 +02:00
int num_channels ;
int sampling_frequency ;
int block_length ;
int subbands ;
int min_bitpool_value ;
int max_bitpool_value ;
2020-11-19 13:25:14 +01:00
btstack_sbc_channel_mode_t channel_mode ;
btstack_sbc_allocation_method_t allocation_method ;
} media_codec_configuration_sbc_t ;
2017-06-13 15:32:44 +02:00
2020-11-19 13:25:14 +01:00
static media_codec_configuration_sbc_t sbc_configuration ;
2020-06-23 11:38:26 +02:00
static int volume_percentage = 0 ;
2019-02-21 13:00:21 +01:00
2020-06-25 15:27:24 +02:00
# ifdef SUPPORT_VOLUME_CHANGE_NOTIFICATION
2020-07-02 10:40:33 +02:00
static uint8_t events_num = 3 ;
2019-02-21 13:00:21 +01:00
static uint8_t events [ ] = {
2020-07-02 10:40:33 +02:00
AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED ,
AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED ,
2020-06-25 15:27:24 +02:00
AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED
2019-02-21 13:00:21 +01:00
} ;
2020-06-25 15:27:24 +02:00
# endif
2019-02-21 13:00:21 +01:00
static uint8_t companies_num = 1 ;
static uint8_t companies [ ] = {
0x00 , 0x19 , 0x58 //BT SIG registered CompanyID
} ;
2020-06-23 11:38:26 +02:00
2017-06-13 15:32:44 +02:00
# ifdef HAVE_BTSTACK_STDIN
2019-09-20 14:19:40 +02:00
// pts:
2020-04-24 11:59:12 +02:00
static const char * device_addr_string = " 6C:72:E7:10:22:EE " ;
2020-06-23 11:38:26 +02:00
// mac 2013: static const char * device_addr_string = "84:38:35:65:d1:15";
// iPhone 5S: static const char * device_addr_string = "54:E4:3A:26:A2:39";
static bd_addr_t device_addr ;
2017-06-13 15:32:44 +02:00
# endif
2020-06-23 11:38:26 +02:00
static btstack_packet_callback_registration_t hci_event_callback_registration ;
2017-08-09 16:45:27 +02:00
static uint8_t sdp_avdtp_sink_service_buffer [ 150 ] ;
2019-02-21 13:00:21 +01:00
static uint8_t sdp_avrcp_target_service_buffer [ 150 ] ;
2020-06-23 11:38:26 +02:00
static uint8_t sdp_avrcp_controller_service_buffer [ 200 ] ;
2020-10-07 11:48:56 +02:00
static uint8_t device_id_sdp_service_buffer [ 100 ] ;
2020-06-23 11:38:26 +02:00
2017-08-09 16:45:27 +02:00
static uint16_t a2dp_cid = 0 ;
2019-02-21 16:45:43 +01:00
static uint8_t a2dp_local_seid = 0 ;
2017-07-19 14:17:58 +02:00
2020-04-24 11:59:12 +02:00
static uint16_t avrcp_cid = 0 ;
2017-10-27 10:21:06 +02:00
static uint8_t avrcp_connected = 0 ;
2020-06-23 11:38:26 +02:00
static uint8_t avrcp_subevent_value [ 100 ] ;
2017-06-13 15:32:44 +02:00
2017-10-31 12:52:56 +01:00
static uint8_t media_sbc_codec_capabilities [ ] = {
0xFF , //(AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO,
0xFF , //(AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS,
2 , 53
} ;
2020-11-18 09:51:11 +01:00
static uint8_t media_sbc_codec_configuration [ 4 ] ;
2017-10-31 12:52:56 +01:00
2020-06-23 11:38:26 +02:00
static int media_initialized = 0 ;
2019-02-21 12:03:23 +01:00
static btstack_resample_t resample_instance ;
2017-10-31 12:52:56 +01:00
/* @section Main Application Setup
*
2020-06-23 11:38:26 +02:00
* @ text The Listing MainConfiguration shows how to setup AD2P Sink and AVRCP services .
* Besides calling init ( ) method for each service , you ' ll also need to register several packet handlers :
* - hci_packet_handler - handles legacy pairing , here by using fixed ' 0000 ' pin code .
2017-10-31 12:52:56 +01:00
* - a2dp_sink_packet_handler - handles events on stream connection status ( established , released ) , the media codec configuration , and , the status of the stream itself ( opened , paused , stopped ) .
2019-03-04 22:19:03 +01:00
* - handle_l2cap_media_data_packet - used to receive streaming data . If STORE_TO_WAV_FILE directive ( check btstack_config . h ) is used , the SBC decoder will be used to decode the SBC data into PCM frames . The resulting PCM frames are then processed in the SBC Decoder callback .
2020-06-23 11:38:26 +02:00
* - avrcp_packet_handler - receives connect / disconnect event .
* - avrcp_controller_packet_handler - receives answers for sent AVRCP commands .
* - avrcp_target_packet_handler - receives AVRCP commands , and registered notifications .
2020-06-23 11:57:59 +02:00
* - stdin_process - used to trigger AVRCP commands to the A2DP Source device , such are get now playing info , start , stop , volume control . Requires HAVE_BTSTACK_STDIN .
2020-06-23 11:38:26 +02:00
*
* @ text To announce A2DP Sink and AVRCP services , you need to create corresponding
* SDP records and register them with the SDP service .
2017-10-31 12:52:56 +01:00
*
* @ text Note , currently only the SBC codec is supported .
2019-03-04 22:19:03 +01:00
* If you want to store the audio data in a file , you ' ll need to define STORE_TO_WAV_FILE .
* If STORE_TO_WAV_FILE directive is defined , the SBC decoder needs to get initialized when a2dp_sink_packet_handler receives event A2DP_SUBEVENT_STREAM_STARTED .
2017-10-31 12:52:56 +01:00
* The initialization of the SBC decoder requires a callback that handles PCM data :
2019-03-04 22:19:03 +01:00
* - handle_pcm_data - handles PCM audio frames . Here , they are stored a in wav file if STORE_TO_WAV_FILE is defined , and / or played using the audio library .
2017-10-31 12:52:56 +01:00
*/
2020-10-07 11:48:56 +02:00
/* LISTING_START(MainConfiguration): Setup Audio Sink and AVRCP services */
2020-06-23 11:57:59 +02:00
static void hci_packet_handler ( uint8_t packet_type , uint16_t channel , uint8_t * packet , uint16_t size ) ;
2017-10-31 12:52:56 +01:00
static void a2dp_sink_packet_handler ( uint8_t packet_type , uint16_t channel , uint8_t * event , uint16_t event_size ) ;
2020-06-23 11:38:26 +02:00
static void handle_l2cap_media_data_packet ( uint8_t seid , uint8_t * packet , uint16_t size ) ;
2020-04-24 17:25:21 +02:00
static void avrcp_packet_handler ( uint8_t packet_type , uint16_t channel , uint8_t * packet , uint16_t size ) ;
2017-10-31 12:52:56 +01:00
static void avrcp_controller_packet_handler ( uint8_t packet_type , uint16_t channel , uint8_t * packet , uint16_t size ) ;
2019-02-21 13:00:21 +01:00
static void avrcp_target_packet_handler ( uint8_t packet_type , uint16_t channel , uint8_t * packet , uint16_t size ) ;
2020-06-23 11:57:59 +02:00
# ifdef HAVE_BTSTACK_STDIN
static void stdin_process ( char cmd ) ;
# endif
2017-10-31 12:52:56 +01:00
2018-07-20 16:13:33 +02:00
static int a2dp_and_avrcp_setup ( void ) {
2017-10-31 12:52:56 +01:00
l2cap_init ( ) ;
2018-07-20 16:13:33 +02:00
// Initialize AVDTP Sink
2017-10-31 12:52:56 +01:00
a2dp_sink_init ( ) ;
a2dp_sink_register_packet_handler ( & a2dp_sink_packet_handler ) ;
a2dp_sink_register_media_handler ( & handle_l2cap_media_data_packet ) ;
2018-07-20 16:13:33 +02:00
2020-10-07 11:48:56 +02:00
// Create stream endpoint
2019-09-20 14:19:40 +02:00
avdtp_stream_endpoint_t * local_stream_endpoint = a2dp_sink_create_stream_endpoint ( AVDTP_AUDIO ,
AVDTP_CODEC_SBC , media_sbc_codec_capabilities , sizeof ( media_sbc_codec_capabilities ) ,
media_sbc_codec_configuration , sizeof ( media_sbc_codec_configuration ) ) ;
if ( ! local_stream_endpoint ) {
printf ( " A2DP Sink: not enough memory to create local stream endpoint \n " ) ;
2017-10-31 12:52:56 +01:00
return 1 ;
}
2020-10-07 11:48:56 +02:00
// Store stream enpoint's SEP ID, as it is used by A2DP API to indentify the stream endpoint
2019-09-20 14:19:40 +02:00
a2dp_local_seid = avdtp_local_seid ( local_stream_endpoint ) ;
2020-04-24 11:20:23 +02:00
2020-10-07 11:48:56 +02:00
// Initialize AVRCP service
2020-04-24 11:20:23 +02:00
avrcp_init ( ) ;
2020-04-24 17:25:21 +02:00
avrcp_register_packet_handler ( & avrcp_packet_handler ) ;
2020-04-24 11:20:23 +02:00
2019-02-21 13:00:21 +01:00
// Initialize AVRCP Controller
2017-10-31 12:52:56 +01:00
avrcp_controller_init ( ) ;
avrcp_controller_register_packet_handler ( & avrcp_controller_packet_handler ) ;
2019-02-21 13:00:21 +01:00
// Initialize AVRCP Target
avrcp_target_init ( ) ;
avrcp_target_register_packet_handler ( & avrcp_target_packet_handler ) ;
2018-07-20 16:13:33 +02:00
// Initialize SDP
2017-10-31 12:52:56 +01:00
sdp_init ( ) ;
2020-10-07 11:48:56 +02:00
// Create A2DP Sink service record and register it with SDP
2017-10-31 12:52:56 +01:00
memset ( sdp_avdtp_sink_service_buffer , 0 , sizeof ( sdp_avdtp_sink_service_buffer ) ) ;
2020-07-23 12:23:56 +02:00
a2dp_sink_create_sdp_record ( sdp_avdtp_sink_service_buffer , 0x10001 , AVDTP_SINK_FEATURE_MASK_HEADPHONE , NULL , NULL ) ;
2017-10-31 12:52:56 +01:00
sdp_register_service ( sdp_avdtp_sink_service_buffer ) ;
2020-10-07 11:48:56 +02:00
// Create AVRCP Controller service record and register it with SDP
2017-10-31 12:52:56 +01:00
memset ( sdp_avrcp_controller_service_buffer , 0 , sizeof ( sdp_avrcp_controller_service_buffer ) ) ;
2020-07-23 00:16:07 +02:00
uint16_t controller_supported_features = AVRCP_FEATURE_MASK_CATEGORY_PLAYER_OR_RECORDER ;
2019-02-18 11:45:09 +01:00
# ifdef AVRCP_BROWSING_ENABLED
2020-07-23 00:16:07 +02:00
controller_supported_features | = AVRCP_FEATURE_MASK_BROWSING ;
2019-02-18 11:45:09 +01:00
# endif
2019-02-21 13:00:21 +01:00
avrcp_controller_create_sdp_record ( sdp_avrcp_controller_service_buffer , 0x10002 , controller_supported_features , NULL , NULL ) ;
2017-10-31 12:52:56 +01:00
sdp_register_service ( sdp_avrcp_controller_service_buffer ) ;
2020-10-07 11:48:56 +02:00
// Create AVRCP Target service record and register it with SDP
2019-02-21 13:00:21 +01:00
memset ( sdp_avrcp_target_service_buffer , 0 , sizeof ( sdp_avrcp_target_service_buffer ) ) ;
2020-07-23 00:16:07 +02:00
uint16_t target_supported_features = AVRCP_FEATURE_MASK_CATEGORY_MONITOR_OR_AMPLIFIER ;
2019-02-21 13:00:21 +01:00
avrcp_target_create_sdp_record ( sdp_avrcp_target_service_buffer , 0x10003 , target_supported_features , NULL , NULL ) ;
sdp_register_service ( sdp_avrcp_target_service_buffer ) ;
2020-10-07 11:48:56 +02:00
// Create Device ID (PnP) service record and register it with SDP
memset ( device_id_sdp_service_buffer , 0 , sizeof ( device_id_sdp_service_buffer ) ) ;
device_id_create_sdp_record ( device_id_sdp_service_buffer , 0x10004 , DEVICE_ID_VENDOR_ID_SOURCE_BLUETOOTH , BLUETOOTH_COMPANY_ID_BLUEKITCHEN_GMBH , 1 , 1 ) ;
sdp_register_service ( device_id_sdp_service_buffer ) ;
// Set local name with a template Bluetooth address, that will be automatically
// replaced with a actual address once it is available, i.e. when BTstack boots
// up and starts talking to a Bluetooth module.
2017-10-31 12:52:56 +01:00
gap_set_local_name ( " A2DP Sink Demo 00:00:00:00:00:00 " ) ;
gap_discoverable_control ( 1 ) ;
gap_set_class_of_device ( 0x200408 ) ;
2020-10-07 11:48:56 +02:00
// Register for HCI events
2018-08-24 10:03:14 +02:00
hci_event_callback_registration . callback = & hci_packet_handler ;
hci_add_event_handler ( & hci_event_callback_registration ) ;
2019-03-04 16:37:54 +01:00
# ifdef HAVE_POSIX_FILE_IO
if ( ! btstack_audio_sink_get_instance ( ) ) {
printf ( " No audio playback. \n " ) ;
} else {
printf ( " Audio playback supported. \n " ) ;
}
2019-03-04 22:19:03 +01:00
# ifdef STORE_TO_WAV_FILE
2019-03-04 16:37:54 +01:00
printf ( " Audio will be stored to \' %s \' file. \n " , wav_filename ) ;
# endif
# endif
2017-10-31 12:52:56 +01:00
return 0 ;
}
2020-06-23 11:38:26 +02:00
/* LISTING_END */
2018-07-20 16:13:33 +02:00
2020-03-19 10:47:49 +01:00
static void playback_handler ( int16_t * buffer , uint16_t num_audio_frames ) {
2019-02-21 12:03:23 +01:00
2019-03-04 22:19:03 +01:00
# ifdef STORE_TO_WAV_FILE
2020-03-19 10:47:49 +01:00
int wav_samples = num_audio_frames * NUM_CHANNELS ;
2019-02-21 12:03:23 +01:00
int16_t * wav_buffer = buffer ;
# endif
2017-06-13 15:32:44 +02:00
2018-07-20 16:13:33 +02:00
// called from lower-layer but guaranteed to be on main thread
2019-03-04 16:37:54 +01:00
if ( sbc_frame_size = = 0 ) {
2020-03-19 10:47:49 +01:00
memset ( buffer , 0 , num_audio_frames * BYTES_PER_FRAME ) ;
2019-03-04 16:37:54 +01:00
return ;
}
2019-02-21 12:03:23 +01:00
// first fill from resampled audio
2018-07-20 16:13:33 +02:00
uint32_t bytes_read ;
2020-03-19 10:47:49 +01:00
btstack_ring_buffer_read ( & decoded_audio_ring_buffer , ( uint8_t * ) buffer , num_audio_frames * BYTES_PER_FRAME , & bytes_read ) ;
2018-07-20 16:13:33 +02:00
buffer + = bytes_read / NUM_CHANNELS ;
2020-03-19 10:47:49 +01:00
num_audio_frames - = bytes_read / BYTES_PER_FRAME ;
2018-07-20 16:13:33 +02:00
// then start decoding sbc frames using request_* globals
request_buffer = buffer ;
2020-03-19 10:47:49 +01:00
request_frames = num_audio_frames ;
2019-02-21 22:50:26 +01:00
while ( request_frames & & btstack_ring_buffer_bytes_available ( & sbc_frame_ring_buffer ) > = sbc_frame_size ) {
// decode frame
uint8_t sbc_frame [ MAX_SBC_FRAME_SIZE ] ;
btstack_ring_buffer_read ( & sbc_frame_ring_buffer , sbc_frame , sbc_frame_size , & bytes_read ) ;
btstack_sbc_decoder_process_data ( & state , 0 , sbc_frame , sbc_frame_size ) ;
2018-07-20 16:13:33 +02:00
}
2019-02-21 12:03:23 +01:00
2019-03-04 22:19:03 +01:00
# ifdef STORE_TO_WAV_FILE
2020-03-19 10:47:49 +01:00
audio_frame_count + = num_audio_frames ;
2019-02-21 12:03:23 +01:00
wav_writer_write_int16 ( wav_samples , wav_buffer ) ;
# endif
2018-07-20 16:13:33 +02:00
}
2020-03-19 10:47:49 +01:00
static void handle_pcm_data ( int16_t * data , int num_audio_frames , int num_channels , int sample_rate , void * context ) {
2018-07-20 16:13:33 +02:00
UNUSED ( sample_rate ) ;
UNUSED ( context ) ;
UNUSED ( num_channels ) ; // must be stereo == 2
2019-03-04 22:19:03 +01:00
const btstack_audio_sink_t * audio_sink = btstack_audio_sink_get_instance ( ) ;
if ( ! audio_sink ) {
# ifdef STORE_TO_WAV_FILE
2020-03-19 10:47:49 +01:00
audio_frame_count + = num_audio_frames ;
wav_writer_write_int16 ( num_audio_frames * NUM_CHANNELS , data ) ;
2019-02-21 12:03:23 +01:00
# endif
2019-03-04 22:19:03 +01:00
return ;
}
2019-02-21 12:03:23 +01:00
// resample into request buffer - add some additional space for resampling
int16_t output_buffer [ ( 128 + 16 ) * NUM_CHANNELS ] ; // 16 * 8 * 2
2020-03-19 10:47:49 +01:00
uint32_t resampled_frames = btstack_resample_block ( & resample_instance , data , num_audio_frames , output_buffer ) ;
2019-02-21 12:03:23 +01:00
2018-07-20 16:13:33 +02:00
// store data in btstack_audio buffer first
2019-02-21 12:03:23 +01:00
int frames_to_copy = btstack_min ( resampled_frames , request_frames ) ;
memcpy ( request_buffer , output_buffer , frames_to_copy * BYTES_PER_FRAME ) ;
request_frames - = frames_to_copy ;
request_buffer + = frames_to_copy * NUM_CHANNELS ;
2017-06-13 15:32:44 +02:00
2018-07-20 16:13:33 +02:00
// and rest in ring buffer
2019-02-21 12:03:23 +01:00
int frames_to_store = resampled_frames - frames_to_copy ;
if ( frames_to_store ) {
int status = btstack_ring_buffer_write ( & decoded_audio_ring_buffer , ( uint8_t * ) & output_buffer [ frames_to_copy * NUM_CHANNELS ] , frames_to_store * BYTES_PER_FRAME ) ;
if ( status ) {
printf ( " Error storing samples in PCM ring buffer!!! \n " ) ;
2018-07-20 16:13:33 +02:00
}
}
2017-06-13 15:32:44 +02:00
}
2020-11-19 13:25:14 +01:00
static int media_processing_init ( media_codec_configuration_sbc_t configuration ) {
2017-07-10 14:02:35 +02:00
if ( media_initialized ) return 0 ;
2018-07-20 16:13:33 +02:00
2017-06-13 15:32:44 +02:00
btstack_sbc_decoder_init ( & state , mode , handle_pcm_data , NULL ) ;
2019-03-04 22:19:03 +01:00
# ifdef STORE_TO_WAV_FILE
2017-06-13 15:32:44 +02:00
wav_writer_open ( wav_filename , configuration . num_channels , configuration . sampling_frequency ) ;
# endif
2019-03-04 22:25:57 +01:00
# ifdef STORE_TO_SBC_FILE
2017-08-11 12:21:31 +02:00
sbc_file = fopen ( sbc_filename , " wb " ) ;
2017-06-13 15:32:44 +02:00
# endif
2018-07-20 16:13:33 +02:00
btstack_ring_buffer_init ( & sbc_frame_ring_buffer , sbc_frame_storage , sizeof ( sbc_frame_storage ) ) ;
btstack_ring_buffer_init ( & decoded_audio_ring_buffer , decoded_audio_storage , sizeof ( decoded_audio_storage ) ) ;
2019-02-21 12:03:23 +01:00
btstack_resample_init ( & resample_instance , configuration . num_channels ) ;
2018-07-20 16:13:33 +02:00
// setup audio playback
2019-02-06 17:51:13 +01:00
const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance ( ) ;
2018-07-20 16:13:33 +02:00
if ( audio ) {
2019-02-06 17:51:13 +01:00
audio - > init ( NUM_CHANNELS , configuration . sampling_frequency , & playback_handler ) ;
2017-06-13 15:32:44 +02:00
}
audio_stream_started = 0 ;
media_initialized = 1 ;
return 0 ;
}
2019-03-04 16:37:54 +01:00
static void media_processing_start ( void ) {
if ( ! media_initialized ) return ;
// setup audio playback
const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance ( ) ;
if ( audio ) {
audio - > start_stream ( ) ;
}
audio_stream_started = 1 ;
}
2018-10-11 11:35:11 +02:00
2019-03-04 16:37:54 +01:00
static void media_processing_pause ( void ) {
if ( ! media_initialized ) return ;
// stop audio playback
audio_stream_started = 0 ;
const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance ( ) ;
if ( audio ) {
audio - > stop_stream ( ) ;
}
}
static void media_processing_close ( void ) {
2017-06-13 15:32:44 +02:00
if ( ! media_initialized ) return ;
media_initialized = 0 ;
2018-07-20 16:13:33 +02:00
audio_stream_started = 0 ;
2019-03-04 16:37:54 +01:00
sbc_frame_size = 0 ;
2017-07-10 14:02:35 +02:00
2019-03-04 22:19:03 +01:00
# ifdef STORE_TO_WAV_FILE
2017-06-13 15:32:44 +02:00
wav_writer_close ( ) ;
2020-03-19 10:47:49 +01:00
uint32_t total_frames_nr = state . good_frames_nr + state . bad_frames_nr + state . zero_frames_nr ;
2017-06-13 15:32:44 +02:00
2020-03-19 10:47:49 +01:00
printf ( " WAV Writer: Decoding done. Processed %u SBC frames: \n - %d good \n - %d bad \n " , total_frames_nr , state . good_frames_nr , total_frames_nr - state . good_frames_nr ) ;
printf ( " WAV Writer: Wrote %u audio frames to wav file: %s \n " , audio_frame_count , wav_filename ) ;
2017-06-13 15:32:44 +02:00
# endif
2017-07-10 14:02:35 +02:00
2019-03-04 22:25:57 +01:00
# ifdef STORE_TO_SBC_FILE
2017-06-13 15:32:44 +02:00
fclose ( sbc_file ) ;
# endif
2018-07-20 16:13:33 +02:00
// stop audio playback
2019-02-06 17:51:13 +01:00
const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance ( ) ;
2018-07-20 16:13:33 +02:00
if ( audio ) {
2019-03-04 16:37:54 +01:00
printf ( " close stream \n " ) ;
2018-07-20 16:13:33 +02:00
audio - > close ( ) ;
}
2017-06-13 15:32:44 +02:00
}
2017-10-31 12:52:56 +01:00
/* @section Handle Media Data Packet
*
2020-10-07 11:48:56 +02:00
* @ text Here the audio data , are received through the handle_l2cap_media_data_packet callback .
2017-10-31 12:52:56 +01:00
* Currently , only the SBC media codec is supported . Hence , the media data consists of the media packet header and the SBC packet .
2020-10-07 11:48:56 +02:00
* The SBC frame will be stored in a ring buffer for later processing ( instead of decoding it to PCM right away which would require a much larger buffer ) .
2018-07-20 16:13:33 +02:00
* If the audio stream wasn ' t started already and there are enough SBC frames in the ring buffer , start playback .
2017-10-31 12:52:56 +01:00
*/
static int read_media_data_header ( uint8_t * packet , int size , int * offset , avdtp_media_packet_header_t * media_header ) ;
static int read_sbc_header ( uint8_t * packet , int size , int * offset , avdtp_sbc_codec_header_t * sbc_header ) ;
2017-08-29 14:34:08 +02:00
static void handle_l2cap_media_data_packet ( uint8_t seid , uint8_t * packet , uint16_t size ) {
UNUSED ( seid ) ;
2017-06-13 15:32:44 +02:00
int pos = 0 ;
2019-03-04 16:37:54 +01:00
2017-06-13 15:32:44 +02:00
avdtp_media_packet_header_t media_header ;
2017-10-31 12:52:56 +01:00
if ( ! read_media_data_header ( packet , size , & pos , & media_header ) ) return ;
2017-06-13 15:32:44 +02:00
avdtp_sbc_codec_header_t sbc_header ;
2017-10-31 12:52:56 +01:00
if ( ! read_sbc_header ( packet , size , & pos , & sbc_header ) ) return ;
2017-06-13 15:32:44 +02:00
2019-03-04 22:25:57 +01:00
# ifdef STORE_TO_SBC_FILE
fwrite ( packet + pos , size - pos , 1 , sbc_file ) ;
# endif
2018-10-11 11:35:11 +02:00
2019-03-04 22:25:57 +01:00
const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance ( ) ;
2018-10-11 11:35:11 +02:00
// process data right away if there's no audio implementation active, e.g. on posix systems to store as .wav
if ( ! audio ) {
btstack_sbc_decoder_process_data ( & state , 0 , packet + pos , size - pos ) ;
return ;
}
2017-06-13 15:32:44 +02:00
// store sbc frame size for buffer management
sbc_frame_size = ( size - pos ) / sbc_header . num_frames ;
2018-07-20 16:13:33 +02:00
2019-02-21 12:03:23 +01:00
int status = btstack_ring_buffer_write ( & sbc_frame_ring_buffer , packet + pos , size - pos ) ;
if ( status ) {
printf ( " Error storing samples in SBC ring buffer!!! \n " ) ;
}
2017-06-13 15:32:44 +02:00
// decide on audio sync drift based on number of sbc frames in queue
2018-07-20 16:13:33 +02:00
int sbc_frames_in_buffer = btstack_ring_buffer_bytes_available ( & sbc_frame_ring_buffer ) / sbc_frame_size ;
2019-02-21 12:03:23 +01:00
uint32_t resampling_factor ;
2019-02-21 15:17:25 +01:00
// nomimal factor (fixed-point 2^16) and compensation offset
2019-11-19 17:11:36 +01:00
uint32_t nomimal_factor = 0x10000 ;
2019-02-21 15:17:25 +01:00
uint32_t compensation = 0x00100 ;
2017-06-13 15:32:44 +02:00
if ( sbc_frames_in_buffer < OPTIMAL_FRAMES_MIN ) {
2019-02-21 15:17:25 +01:00
resampling_factor = nomimal_factor - compensation ; // stretch samples
2017-06-13 15:32:44 +02:00
} else if ( sbc_frames_in_buffer < = OPTIMAL_FRAMES_MAX ) {
2019-02-21 15:17:25 +01:00
resampling_factor = nomimal_factor ; // nothing to do
2017-06-13 15:32:44 +02:00
} else {
2019-02-21 15:17:25 +01:00
resampling_factor = nomimal_factor + compensation ; // compress samples
2017-06-13 15:32:44 +02:00
}
2019-02-21 12:03:23 +01:00
btstack_resample_set_factor ( & resample_instance , resampling_factor ) ;
2018-07-20 16:13:33 +02:00
// start stream if enough frames buffered
2019-02-21 12:03:23 +01:00
if ( ! audio_stream_started & & sbc_frames_in_buffer > = OPTIMAL_FRAMES_MIN ) {
2021-01-15 15:26:49 +01:00
media_processing_start ( ) ;
2017-10-31 12:52:56 +01:00
}
}
static int read_sbc_header ( uint8_t * packet , int size , int * offset , avdtp_sbc_codec_header_t * sbc_header ) {
int sbc_header_len = 12 ; // without crc
int pos = * offset ;
if ( size - pos < sbc_header_len ) {
printf ( " Not enough data to read SBC header, expected %d, received %d \n " , sbc_header_len , size - pos ) ;
2017-11-24 15:16:33 +01:00
return 0 ;
2017-10-31 12:52:56 +01:00
}
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 + + ;
* offset = pos ;
2017-11-24 15:16:33 +01:00
return 1 ;
2017-10-31 12:52:56 +01:00
}
static int read_media_data_header ( uint8_t * packet , int size , int * offset , avdtp_media_packet_header_t * media_header ) {
int media_header_len = 12 ; // without crc
int pos = * offset ;
if ( size - pos < media_header_len ) {
printf ( " Not enough data to read media packet header, expected %d, received %d \n " , media_header_len , size - pos ) ;
2017-11-24 15:16:33 +01:00
return 0 ;
2017-10-31 12:52:56 +01:00
}
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 ;
* offset = pos ;
2017-11-24 15:16:33 +01:00
return 1 ;
2017-10-31 12:52:56 +01:00
}
2020-11-19 13:25:14 +01:00
static void dump_sbc_configuration ( media_codec_configuration_sbc_t configuration ) {
2017-06-13 15:32:44 +02:00
printf ( " - num_channels: %d \n " , configuration . num_channels ) ;
printf ( " - sampling_frequency: %d \n " , configuration . sampling_frequency ) ;
printf ( " - channel_mode: %d \n " , configuration . channel_mode ) ;
printf ( " - block_length: %d \n " , configuration . block_length ) ;
printf ( " - subbands: %d \n " , configuration . subbands ) ;
printf ( " - allocation_method: %d \n " , configuration . allocation_method ) ;
printf ( " - bitpool_value [%d, %d] \n " , configuration . min_bitpool_value , configuration . max_bitpool_value ) ;
2017-07-07 16:42:37 +02:00
printf ( " \n " ) ;
2017-06-13 15:32:44 +02:00
}
2020-04-24 17:25:21 +02:00
static void avrcp_packet_handler ( uint8_t packet_type , uint16_t channel , uint8_t * packet , uint16_t size ) {
2017-06-13 15:32:44 +02:00
UNUSED ( channel ) ;
UNUSED ( size ) ;
uint16_t local_cid ;
uint8_t status = 0xFF ;
2017-08-11 11:26:34 +02:00
bd_addr_t adress ;
2017-07-21 14:48:45 +02:00
if ( packet_type ! = HCI_EVENT_PACKET ) return ;
if ( hci_event_packet_get_type ( packet ) ! = HCI_EVENT_AVRCP_META ) return ;
switch ( packet [ 2 ] ) {
case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED : {
local_cid = avrcp_subevent_connection_established_get_avrcp_cid ( packet ) ;
status = avrcp_subevent_connection_established_get_status ( packet ) ;
if ( status ! = ERROR_CODE_SUCCESS ) {
2020-04-24 17:25:21 +02:00
printf ( " AVRCP: Connection failed: status 0x%02x \n " , status ) ;
2020-04-24 11:59:12 +02:00
avrcp_cid = 0 ;
2017-07-21 14:48:45 +02:00
return ;
}
2017-07-25 16:03:13 +02:00
2020-04-24 11:59:12 +02:00
avrcp_cid = local_cid ;
2017-10-27 10:21:06 +02:00
avrcp_connected = 1 ;
2017-08-11 11:26:34 +02:00
avrcp_subevent_connection_established_get_bd_addr ( packet , adress ) ;
2020-04-24 17:25:21 +02:00
printf ( " AVRCP: Connected to %s, cid 0x%02x \n " , bd_addr_to_str ( adress ) , avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
2017-07-21 14:48:45 +02:00
// automatically enable notifications
2020-04-24 11:59:12 +02:00
avrcp_controller_enable_notification ( avrcp_cid , AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED ) ;
avrcp_controller_enable_notification ( avrcp_cid , AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED ) ;
avrcp_controller_enable_notification ( avrcp_cid , AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED ) ;
2017-07-21 14:48:45 +02:00
return ;
}
2020-04-24 17:25:21 +02:00
2017-07-21 14:48:45 +02:00
case AVRCP_SUBEVENT_CONNECTION_RELEASED :
2020-04-24 17:25:21 +02:00
printf ( " AVRCP: Channel released: cid 0x%02x \n " , avrcp_subevent_connection_released_get_avrcp_cid ( packet ) ) ;
2020-04-24 11:59:12 +02:00
avrcp_cid = 0 ;
2017-10-27 10:21:06 +02:00
avrcp_connected = 0 ;
2017-07-21 14:48:45 +02:00
return ;
default :
break ;
}
2020-04-24 17:25:21 +02:00
}
2017-06-13 15:32:44 +02:00
2020-04-24 17:25:21 +02:00
static void avrcp_controller_packet_handler ( uint8_t packet_type , uint16_t channel , uint8_t * packet , uint16_t size ) {
UNUSED ( channel ) ;
UNUSED ( size ) ;
uint8_t status = 0xFF ;
if ( packet_type ! = HCI_EVENT_PACKET ) return ;
if ( hci_event_packet_get_type ( packet ) ! = HCI_EVENT_AVRCP_META ) return ;
2017-07-21 14:48:45 +02:00
status = packet [ 5 ] ;
2020-04-24 11:59:12 +02:00
if ( ! avrcp_cid ) return ;
2017-07-21 14:48:45 +02:00
2017-10-31 12:52:56 +01:00
// ignore INTERIM status
2018-04-24 14:39:58 +02:00
if ( status = = AVRCP_CTYPE_RESPONSE_INTERIM ) {
2018-06-19 12:37:12 +02:00
switch ( packet [ 2 ] ) {
case AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_POS_CHANGED : {
uint32_t playback_position_ms = avrcp_subevent_notification_playback_pos_changed_get_playback_position_ms ( packet ) ;
if ( playback_position_ms = = AVRCP_NO_TRACK_SELECTED_PLAYBACK_POSITION_CHANGED ) {
2019-02-21 16:45:43 +01:00
printf ( " AVRCP Controller: playback position changed, no track is selected \n " ) ;
2018-06-19 12:37:12 +02:00
}
break ;
}
default :
break ;
}
2018-04-24 14:39:58 +02:00
return ;
}
2017-07-21 14:48:45 +02:00
2020-06-23 11:38:26 +02:00
memset ( avrcp_subevent_value , 0 , sizeof ( avrcp_subevent_value ) ) ;
2017-07-21 14:48:45 +02:00
switch ( packet [ 2 ] ) {
2018-06-19 12:37:12 +02:00
case AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_POS_CHANGED :
2019-03-04 16:37:54 +01:00
printf ( " AVRCP Controller: Playback position changed, position %d ms \n " , ( unsigned int ) avrcp_subevent_notification_playback_pos_changed_get_playback_position_ms ( packet ) ) ;
2018-06-19 12:37:12 +02:00
break ;
2017-07-21 14:48:45 +02:00
case AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED :
2019-03-04 16:37:54 +01:00
printf ( " AVRCP Controller: Playback status changed %s \n " , avrcp_play_status2str ( avrcp_subevent_notification_playback_status_changed_get_play_status ( packet ) ) ) ;
2017-07-21 14:48:45 +02:00
return ;
case AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED :
2019-03-04 16:37:54 +01:00
printf ( " AVRCP Controller: Playing content changed \n " ) ;
2017-07-21 14:48:45 +02:00
return ;
case AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED :
2019-03-04 16:37:54 +01:00
printf ( " AVRCP Controller: Track changed \n " ) ;
2017-07-21 14:48:45 +02:00
return ;
case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED :
2019-03-04 16:37:54 +01:00
printf ( " AVRCP Controller: Absolute volume changed %d \n " , avrcp_subevent_notification_volume_changed_get_absolute_volume ( packet ) ) ;
2017-07-21 14:48:45 +02:00
return ;
case AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED :
2019-03-04 16:37:54 +01:00
printf ( " AVRCP Controller: Changed \n " ) ;
2017-07-21 14:48:45 +02:00
return ;
case AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE : {
uint8_t shuffle_mode = avrcp_subevent_shuffle_and_repeat_mode_get_shuffle_mode ( packet ) ;
uint8_t repeat_mode = avrcp_subevent_shuffle_and_repeat_mode_get_repeat_mode ( packet ) ;
2019-02-21 16:45:43 +01:00
printf ( " AVRCP Controller: %s, %s \n " , avrcp_shuffle2str ( shuffle_mode ) , avrcp_repeat2str ( repeat_mode ) ) ;
2017-07-21 14:48:45 +02:00
break ;
}
2019-02-21 13:00:21 +01:00
case AVRCP_SUBEVENT_NOW_PLAYING_TRACK_INFO :
2019-02-21 16:45:43 +01:00
printf ( " AVRCP Controller: Track: %d \n " , avrcp_subevent_now_playing_track_info_get_track ( packet ) ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_SUBEVENT_NOW_PLAYING_TOTAL_TRACKS_INFO :
2019-02-21 16:45:43 +01:00
printf ( " AVRCP Controller: Total Tracks: %d \n " , avrcp_subevent_now_playing_total_tracks_info_get_total_tracks ( packet ) ) ;
2019-02-21 13:00:21 +01:00
break ;
2017-10-12 17:14:28 +02:00
case AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO :
if ( avrcp_subevent_now_playing_title_info_get_value_len ( packet ) > 0 ) {
2020-06-23 11:38:26 +02:00
memcpy ( avrcp_subevent_value , avrcp_subevent_now_playing_title_info_get_value ( packet ) , avrcp_subevent_now_playing_title_info_get_value_len ( packet ) ) ;
printf ( " AVRCP Controller: Title: %s \n " , avrcp_subevent_value ) ;
2017-10-12 17:14:28 +02:00
}
2017-07-21 14:48:45 +02:00
break ;
2017-10-12 17:14:28 +02:00
case AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO :
if ( avrcp_subevent_now_playing_artist_info_get_value_len ( packet ) > 0 ) {
2020-06-23 11:38:26 +02:00
memcpy ( avrcp_subevent_value , avrcp_subevent_now_playing_artist_info_get_value ( packet ) , avrcp_subevent_now_playing_artist_info_get_value_len ( packet ) ) ;
printf ( " AVRCP Controller: Artist: %s \n " , avrcp_subevent_value ) ;
2017-10-12 17:14:28 +02:00
}
break ;
case AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO :
if ( avrcp_subevent_now_playing_album_info_get_value_len ( packet ) > 0 ) {
2020-06-23 11:38:26 +02:00
memcpy ( avrcp_subevent_value , avrcp_subevent_now_playing_album_info_get_value ( packet ) , avrcp_subevent_now_playing_album_info_get_value_len ( packet ) ) ;
printf ( " AVRCP Controller: Album: %s \n " , avrcp_subevent_value ) ;
2017-10-12 17:14:28 +02:00
}
break ;
case AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO :
if ( avrcp_subevent_now_playing_genre_info_get_value_len ( packet ) > 0 ) {
2020-06-23 11:38:26 +02:00
memcpy ( avrcp_subevent_value , avrcp_subevent_now_playing_genre_info_get_value ( packet ) , avrcp_subevent_now_playing_genre_info_get_value_len ( packet ) ) ;
printf ( " AVRCP Controller: Genre: %s \n " , avrcp_subevent_value ) ;
2017-10-12 17:14:28 +02:00
}
break ;
2017-07-21 14:48:45 +02:00
case AVRCP_SUBEVENT_PLAY_STATUS :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Controller: Song length % " PRIu32 " ms, Song position % " PRIu32 " ms, Play status %s \n " ,
2017-07-21 14:48:45 +02:00
avrcp_subevent_play_status_get_song_length ( packet ) ,
avrcp_subevent_play_status_get_song_position ( packet ) ,
avrcp_play_status2str ( avrcp_subevent_play_status_get_play_status ( packet ) ) ) ;
break ;
2019-02-21 22:28:07 +01:00
2017-07-21 14:48:45 +02:00
case AVRCP_SUBEVENT_OPERATION_COMPLETE :
2020-08-26 11:06:49 +02:00
printf ( " AVRCP Controller: %s complete \n " , avrcp_operation2str ( avrcp_subevent_operation_complete_get_operation_id ( packet ) ) ) ;
2017-07-21 14:48:45 +02:00
break ;
2019-02-21 22:28:07 +01:00
2017-07-21 14:48:45 +02:00
case AVRCP_SUBEVENT_OPERATION_START :
2020-08-26 11:06:49 +02:00
printf ( " AVRCP Controller: %s start \n " , avrcp_operation2str ( avrcp_subevent_operation_start_get_operation_id ( packet ) ) ) ;
2017-06-13 15:32:44 +02:00
break ;
2020-09-05 16:36:49 +02:00
2020-09-04 17:15:29 +02:00
case AVRCP_SUBEVENT_NOTIFICATION_EVENT_TRACK_REACHED_END :
printf ( " AVRCP Controller: Track reached end \n " ) ;
break ;
2020-09-05 16:36:49 +02:00
case AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE :
printf ( " A2DP Sink : Set Player App Value %s \n " , avrcp_ctype2str ( avrcp_subevent_player_application_value_response_get_command_type ( packet ) ) ) ;
break ;
2017-06-13 15:32:44 +02:00
default :
2019-03-04 16:37:54 +01:00
printf ( " AVRCP Controller: Event 0x%02x is not parsed \n " , packet [ 2 ] ) ;
2017-06-13 15:32:44 +02:00
break ;
2017-07-21 14:48:45 +02:00
}
2017-06-13 15:32:44 +02:00
}
2020-06-26 16:53:55 +02:00
static void avrcp_volume_changed ( uint8_t volume ) {
const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance ( ) ;
if ( audio ) {
audio - > set_volume ( volume ) ;
}
}
2019-02-21 13:00:21 +01:00
static void avrcp_target_packet_handler ( uint8_t packet_type , uint16_t channel , uint8_t * packet , uint16_t size ) {
UNUSED ( channel ) ;
UNUSED ( size ) ;
if ( packet_type ! = HCI_EVENT_PACKET ) return ;
if ( hci_event_packet_get_type ( packet ) ! = HCI_EVENT_AVRCP_META ) return ;
2020-06-26 16:53:55 +02:00
uint8_t volume ;
2019-02-21 16:45:43 +01:00
2019-02-21 13:00:21 +01:00
switch ( packet [ 2 ] ) {
case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED :
2019-02-21 16:45:43 +01:00
volume = avrcp_subevent_notification_volume_changed_get_absolute_volume ( packet ) ;
volume_percentage = volume * 100 / 127 ;
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : Volume set to %d%% (%d) \n " , volume_percentage , volume ) ;
2020-06-26 16:53:55 +02:00
avrcp_volume_changed ( volume ) ;
2019-02-21 13:00:21 +01:00
break ;
2020-04-24 17:25:21 +02:00
2019-02-21 13:00:21 +01:00
case AVRCP_SUBEVENT_EVENT_IDS_QUERY :
2020-06-25 15:27:24 +02:00
# ifdef SUPPORT_VOLUME_CHANGE_NOTIFICATION
2020-06-17 22:09:23 +02:00
avrcp_target_supported_events ( avrcp_cid , events_num , events , sizeof ( events ) ) ;
2020-06-25 15:27:24 +02:00
# else
avrcp_target_supported_events ( avrcp_cid , 0 , NULL , 0 ) ;
# endif
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_SUBEVENT_COMPANY_IDS_QUERY :
2020-06-17 22:09:23 +02:00
avrcp_target_supported_companies ( avrcp_cid , companies_num , companies , sizeof ( companies ) ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_SUBEVENT_OPERATION : {
avrcp_operation_id_t operation_id = avrcp_subevent_operation_get_operation_id ( packet ) ;
switch ( operation_id ) {
case AVRCP_OPERATION_ID_PLAY :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : PLAY \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_PAUSE :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : PAUSE \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_STOP :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : STOP \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_REWIND :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : REWIND \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_FAST_FORWARD :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : FAST_FORWARD \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_FORWARD :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : FORWARD \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_BACKWARD :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : BACKWARD \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_SKIP :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : SKIP \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_MUTE :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : MUTE \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_CHANNEL_UP :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : CHANNEL_UP \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_CHANNEL_DOWN :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : CHANNEL_DOWN \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_SELECT :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : SELECT \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_UP :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : UP \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_DOWN :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : DOWN \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_LEFT :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : LEFT \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_RIGHT :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : RIGTH \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
case AVRCP_OPERATION_ID_ROOT_MENU :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : ROOT_MENU \n " ) ;
2019-02-21 13:00:21 +01:00
break ;
default :
return ;
}
break ;
}
default :
2019-02-21 22:28:07 +01:00
printf ( " AVRCP Target : Event 0x%02x is not parsed \n " , packet [ 2 ] ) ;
2019-02-21 13:00:21 +01:00
break ;
}
}
2018-07-20 16:13:33 +02:00
static void hci_packet_handler ( uint8_t packet_type , uint16_t channel , uint8_t * packet , uint16_t size ) {
2017-06-13 15:32:44 +02:00
UNUSED ( channel ) ;
UNUSED ( size ) ;
2017-08-11 11:26:34 +02:00
if ( packet_type ! = HCI_EVENT_PACKET ) return ;
2017-12-13 14:30:53 +01:00
if ( hci_event_packet_get_type ( packet ) = = HCI_EVENT_PIN_CODE_REQUEST ) {
2018-07-20 16:13:33 +02:00
bd_addr_t address ;
2017-12-13 14:30:53 +01:00
printf ( " Pin code request - using '0000' \n " ) ;
hci_event_pin_code_request_get_bd_addr ( packet , address ) ;
gap_pin_code_response ( address , " 0000 " ) ;
}
2018-07-20 16:13:33 +02:00
}
static void a2dp_sink_packet_handler ( uint8_t packet_type , uint16_t channel , uint8_t * packet , uint16_t size ) {
UNUSED ( channel ) ;
UNUSED ( size ) ;
bd_addr_t address ;
uint8_t status ;
2020-11-19 13:25:14 +01:00
uint8_t channel_mode ;
uint8_t allocation_method ;
2018-07-20 16:13:33 +02:00
if ( packet_type ! = HCI_EVENT_PACKET ) return ;
2017-08-11 11:26:34 +02:00
if ( hci_event_packet_get_type ( packet ) ! = HCI_EVENT_A2DP_META ) return ;
switch ( packet [ 2 ] ) {
case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION :
2019-02-21 22:28:07 +01:00
printf ( " A2DP Sink : Received non SBC codec - not implemented \n " ) ;
2017-08-11 11:26:34 +02:00
break ;
case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION : {
2019-02-21 22:28:07 +01:00
printf ( " A2DP Sink : Received SBC codec configuration \n " ) ;
2017-08-11 11:26:34 +02:00
sbc_configuration . reconfigure = a2dp_subevent_signaling_media_codec_sbc_configuration_get_reconfigure ( packet ) ;
sbc_configuration . num_channels = a2dp_subevent_signaling_media_codec_sbc_configuration_get_num_channels ( packet ) ;
sbc_configuration . sampling_frequency = a2dp_subevent_signaling_media_codec_sbc_configuration_get_sampling_frequency ( packet ) ;
sbc_configuration . block_length = a2dp_subevent_signaling_media_codec_sbc_configuration_get_block_length ( packet ) ;
sbc_configuration . subbands = a2dp_subevent_signaling_media_codec_sbc_configuration_get_subbands ( packet ) ;
sbc_configuration . min_bitpool_value = a2dp_subevent_signaling_media_codec_sbc_configuration_get_min_bitpool_value ( packet ) ;
sbc_configuration . max_bitpool_value = a2dp_subevent_signaling_media_codec_sbc_configuration_get_max_bitpool_value ( packet ) ;
2020-11-19 13:25:14 +01:00
allocation_method = a2dp_subevent_signaling_media_codec_sbc_configuration_get_allocation_method ( packet ) ;
channel_mode = a2dp_subevent_signaling_media_codec_sbc_configuration_get_channel_mode ( packet ) ;
// Adapt Bluetooth spec definition to SBC Encoder expected input
sbc_configuration . allocation_method = ( btstack_sbc_allocation_method_t ) ( allocation_method - 1 ) ;
sbc_configuration . num_channels = SBC_CHANNEL_MODE_STEREO ;
switch ( channel_mode ) {
case AVDTP_SBC_JOINT_STEREO :
sbc_configuration . channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO ;
break ;
case AVDTP_SBC_STEREO :
sbc_configuration . channel_mode = SBC_CHANNEL_MODE_STEREO ;
break ;
case AVDTP_SBC_DUAL_CHANNEL :
sbc_configuration . channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL ;
break ;
case AVDTP_SBC_MONO :
sbc_configuration . channel_mode = SBC_CHANNEL_MODE_MONO ;
sbc_configuration . num_channels = 1 ;
break ;
default :
btstack_assert ( false ) ;
break ;
}
2017-08-11 11:26:34 +02:00
dump_sbc_configuration ( sbc_configuration ) ;
if ( sbc_configuration . reconfigure ) {
media_processing_close ( ) ;
2017-06-13 15:32:44 +02:00
}
2017-08-11 11:26:34 +02:00
// prepare media processing
media_processing_init ( sbc_configuration ) ;
2017-06-13 15:32:44 +02:00
break ;
2017-08-11 11:26:34 +02:00
}
case A2DP_SUBEVENT_STREAM_ESTABLISHED :
a2dp_subevent_stream_established_get_bd_addr ( packet , address ) ;
2017-08-25 17:47:32 +02:00
status = a2dp_subevent_stream_established_get_status ( packet ) ;
if ( status ) {
2019-02-21 22:28:07 +01:00
printf ( " A2DP Sink : Streaming connection failed, status 0x%02x \n " , status ) ;
2017-08-25 17:47:32 +02:00
break ;
}
2017-10-25 16:37:33 +02:00
2020-06-23 11:38:26 +02:00
a2dp_cid = a2dp_subevent_stream_established_get_a2dp_cid ( packet ) ;
2019-02-21 22:28:07 +01:00
printf ( " A2DP Sink : Streaming connection is established, address %s, cid 0x%02X, local seid %d \n " , bd_addr_to_str ( address ) , a2dp_cid , a2dp_local_seid ) ;
2020-11-22 22:58:37 +01:00
# ifdef HAVE_BTSTACK_STDIN
// use address for outgoing connections
memcpy ( device_addr , address , 6 ) ;
# endif
2017-08-11 11:26:34 +02:00
break ;
case A2DP_SUBEVENT_STREAM_STARTED :
2019-03-04 16:37:54 +01:00
printf ( " A2DP Sink : Stream started \n " ) ;
2021-01-15 15:26:49 +01:00
// audio stream is started when buffer reaches minimal level
2017-08-11 11:26:34 +02:00
break ;
case A2DP_SUBEVENT_STREAM_SUSPENDED :
2019-03-04 16:37:54 +01:00
printf ( " A2DP Sink : Stream paused \n " ) ;
media_processing_pause ( ) ;
2017-06-13 15:32:44 +02:00
break ;
2017-08-11 11:26:34 +02:00
case A2DP_SUBEVENT_STREAM_RELEASED :
2019-03-04 16:37:54 +01:00
printf ( " A2DP Sink : Stream released \n " ) ;
2017-08-11 11:26:34 +02:00
media_processing_close ( ) ;
break ;
2020-06-23 11:38:26 +02:00
2017-08-30 11:05:20 +02:00
case A2DP_SUBEVENT_SIGNALING_CONNECTION_RELEASED :
2019-03-04 16:37:54 +01:00
printf ( " A2DP Sink : Signaling connection released \n " ) ;
2018-10-11 11:35:11 +02:00
media_processing_close ( ) ;
2017-08-30 11:05:20 +02:00
break ;
2020-06-23 11:38:26 +02:00
2017-08-11 11:26:34 +02:00
default :
2019-03-04 16:37:54 +01:00
printf ( " A2DP Sink : Not parsed 0x%02x \n " , packet [ 2 ] ) ;
2017-08-11 11:26:34 +02:00
break ;
2017-06-13 15:32:44 +02:00
}
}
# ifdef HAVE_BTSTACK_STDIN
static void show_usage ( void ) {
bd_addr_t iut_address ;
gap_local_bd_addr ( iut_address ) ;
printf ( " \n --- Bluetooth AVDTP Sink/AVRCP Connection Test Console %s --- \n " , bd_addr_to_str ( iut_address ) ) ;
2017-10-25 16:37:33 +02:00
printf ( " b - AVDTP Sink create connection to addr %s \n " , bd_addr_to_str ( device_addr ) ) ;
2017-06-13 15:32:44 +02:00
printf ( " B - AVDTP Sink disconnect \n " ) ;
2020-04-24 11:59:12 +02:00
printf ( " c - AVRCP create connection to addr %s \n " , bd_addr_to_str ( device_addr ) ) ;
printf ( " C - AVRCP disconnect \n " ) ;
2017-06-13 15:32:44 +02:00
2019-09-20 14:19:40 +02:00
printf ( " w - delay report \n " ) ;
2017-06-13 15:32:44 +02:00
printf ( " \n --- Bluetooth AVRCP Commands %s --- \n " , bd_addr_to_str ( iut_address ) ) ;
printf ( " O - get play status \n " ) ;
printf ( " j - get now playing info \n " ) ;
printf ( " k - play \n " ) ;
printf ( " K - stop \n " ) ;
2017-06-14 14:12:09 +02:00
printf ( " L - pause \n " ) ;
printf ( " u - start fast forward \n " ) ;
printf ( " U - stop fast forward \n " ) ;
2017-06-13 15:32:44 +02:00
printf ( " n - start rewind \n " ) ;
printf ( " N - stop rewind \n " ) ;
printf ( " i - forward \n " ) ;
printf ( " I - backward \n " ) ;
printf ( " M - mute \n " ) ;
printf ( " r - skip \n " ) ;
printf ( " q - query repeat and shuffle mode \n " ) ;
printf ( " v - repeat single track \n " ) ;
printf ( " x - repeat all tracks \n " ) ;
printf ( " X - disable repeat mode \n " ) ;
printf ( " z - shuffle all tracks \n " ) ;
printf ( " Z - disable shuffle mode \n " ) ;
2018-04-24 14:39:58 +02:00
printf ( " a/A - register/deregister TRACK_CHANGED \n " ) ;
2019-02-21 13:00:21 +01:00
printf ( " R/P - register/deregister PLAYBACK_POS_CHANGED \n " ) ;
2018-04-24 14:39:58 +02:00
2019-02-21 16:45:43 +01:00
printf ( " \n --- Volume Control --- \n " ) ;
printf ( " t - volume up for 10 percent \n " ) ;
printf ( " T - volume down for 10 percent \n " ) ;
2017-06-13 15:32:44 +02:00
printf ( " --- \n " ) ;
}
# endif
2017-07-19 14:17:58 +02:00
# ifdef HAVE_BTSTACK_STDIN
2017-06-13 15:32:44 +02:00
static void stdin_process ( char cmd ) {
2017-08-09 16:45:27 +02:00
uint8_t status = ERROR_CODE_SUCCESS ;
2020-06-26 16:53:55 +02:00
uint8_t volume ;
2017-06-13 15:32:44 +02:00
switch ( cmd ) {
case ' b ' :
2019-02-21 16:45:43 +01:00
status = a2dp_sink_establish_stream ( device_addr , a2dp_local_seid , & a2dp_cid ) ;
printf ( " - Create AVDTP connection to addr %s, and local seid %d, expected cid 0x%02x. \n " , bd_addr_to_str ( device_addr ) , a2dp_local_seid , a2dp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' B ' :
2017-08-30 11:05:20 +02:00
printf ( " - AVDTP disconnect from addr %s. \n " , bd_addr_to_str ( device_addr ) ) ;
2020-09-04 17:15:29 +02:00
a2dp_sink_disconnect ( a2dp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' c ' :
2020-04-24 11:59:12 +02:00
printf ( " - Create AVRCP connection to addr %s. \n " , bd_addr_to_str ( device_addr ) ) ;
status = avrcp_connect ( device_addr , & avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' C ' :
2020-04-24 11:59:12 +02:00
printf ( " - AVRCP disconnect from addr %s. \n " , bd_addr_to_str ( device_addr ) ) ;
status = avrcp_disconnect ( avrcp_cid ) ;
2019-02-21 13:00:21 +01:00
break ;
2020-04-24 11:59:12 +02:00
2017-06-13 15:32:44 +02:00
case ' \n ' :
case ' \r ' :
break ;
2019-09-20 14:19:40 +02:00
case ' w ' :
printf ( " Send delay report \n " ) ;
avdtp_sink_delay_report ( a2dp_cid , a2dp_local_seid , 100 ) ;
break ;
2019-02-21 16:45:43 +01:00
// Volume Control
case ' t ' :
2019-02-21 22:50:26 +01:00
volume_percentage = volume_percentage < = 90 ? volume_percentage + 10 : 100 ;
2020-06-26 16:53:55 +02:00
volume = volume_percentage * 127 / 100 ;
printf ( " - volume up for 10 percent, %d%% (%d) \n " , volume_percentage , volume ) ;
status = avrcp_target_volume_changed ( avrcp_cid , volume ) ;
avrcp_volume_changed ( volume ) ;
2019-02-21 16:45:43 +01:00
break ;
case ' T ' :
2019-02-21 22:50:26 +01:00
volume_percentage = volume_percentage > = 10 ? volume_percentage - 10 : 0 ;
2020-06-26 16:53:55 +02:00
volume = volume_percentage * 127 / 100 ;
printf ( " - volume down for 10 percent, %d%% (%d) \n " , volume_percentage , volume ) ;
status = avrcp_target_volume_changed ( avrcp_cid , volume ) ;
avrcp_volume_changed ( volume ) ;
2019-02-21 16:45:43 +01:00
break ;
2017-06-13 15:32:44 +02:00
case ' O ' :
2019-02-21 16:45:43 +01:00
printf ( " - get play status \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_get_play_status ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' j ' :
2019-02-21 16:45:43 +01:00
printf ( " - get now playing info \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_get_now_playing_info ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' k ' :
2019-02-21 16:45:43 +01:00
printf ( " - play \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_play ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' K ' :
2019-02-21 16:45:43 +01:00
printf ( " - stop \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_stop ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' L ' :
2019-02-21 16:45:43 +01:00
printf ( " - pause \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_pause ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' u ' :
2019-02-21 16:45:43 +01:00
printf ( " - start fast forward \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_press_and_hold_fast_forward ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' U ' :
2019-02-21 16:45:43 +01:00
printf ( " - stop fast forward \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_release_press_and_hold_cmd ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' n ' :
2019-02-21 16:45:43 +01:00
printf ( " - start rewind \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_press_and_hold_rewind ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' N ' :
2019-02-21 16:45:43 +01:00
printf ( " - stop rewind \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_release_press_and_hold_cmd ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' i ' :
2019-02-21 16:45:43 +01:00
printf ( " - forward \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_forward ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' I ' :
2019-02-21 16:45:43 +01:00
printf ( " - backward \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_backward ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' M ' :
2019-02-21 16:45:43 +01:00
printf ( " - mute \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_mute ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' r ' :
2019-02-21 16:45:43 +01:00
printf ( " - skip \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_skip ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' q ' :
2019-02-21 16:45:43 +01:00
printf ( " - query repeat and shuffle mode \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_query_shuffle_and_repeat_modes ( avrcp_cid ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' v ' :
2019-02-21 16:45:43 +01:00
printf ( " - repeat single track \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_set_repeat_mode ( avrcp_cid , AVRCP_REPEAT_MODE_SINGLE_TRACK ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' x ' :
2019-02-21 16:45:43 +01:00
printf ( " - repeat all tracks \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_set_repeat_mode ( avrcp_cid , AVRCP_REPEAT_MODE_ALL_TRACKS ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' X ' :
2019-02-21 16:45:43 +01:00
printf ( " - disable repeat mode \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_set_repeat_mode ( avrcp_cid , AVRCP_REPEAT_MODE_OFF ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' z ' :
2019-02-21 16:45:43 +01:00
printf ( " - shuffle all tracks \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_set_shuffle_mode ( avrcp_cid , AVRCP_SHUFFLE_MODE_ALL_TRACKS ) ;
2017-06-13 15:32:44 +02:00
break ;
case ' Z ' :
2019-02-21 16:45:43 +01:00
printf ( " - disable shuffle mode \n " ) ;
2020-04-24 11:59:12 +02:00
status = avrcp_controller_set_shuffle_mode ( avrcp_cid , AVRCP_SHUFFLE_MODE_OFF ) ;
2017-06-13 15:32:44 +02:00
break ;
2018-04-24 14:39:58 +02:00
case ' a ' :
2019-02-21 16:45:43 +01:00
printf ( " AVRCP: enable notification TRACK_CHANGED \n " ) ;
2020-04-24 11:59:12 +02:00
avrcp_controller_enable_notification ( avrcp_cid , AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED ) ;
2018-04-24 14:39:58 +02:00
break ;
case ' A ' :
2019-02-21 16:45:43 +01:00
printf ( " AVRCP: disable notification TRACK_CHANGED \n " ) ;
2020-04-24 11:59:12 +02:00
avrcp_controller_disable_notification ( avrcp_cid , AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED ) ;
2018-04-24 14:39:58 +02:00
break ;
2019-02-21 13:00:21 +01:00
case ' R ' :
2019-02-21 16:45:43 +01:00
printf ( " AVRCP: enable notification PLAYBACK_POS_CHANGED \n " ) ;
2020-04-24 11:59:12 +02:00
avrcp_controller_enable_notification ( avrcp_cid , AVRCP_NOTIFICATION_EVENT_PLAYBACK_POS_CHANGED ) ;
2018-06-19 12:37:12 +02:00
break ;
2019-02-21 13:00:21 +01:00
case ' P ' :
2019-02-21 16:45:43 +01:00
printf ( " AVRCP: disable notification PLAYBACK_POS_CHANGED \n " ) ;
2020-04-24 11:59:12 +02:00
avrcp_controller_disable_notification ( avrcp_cid , AVRCP_NOTIFICATION_EVENT_PLAYBACK_POS_CHANGED ) ;
2018-06-19 12:37:12 +02:00
break ;
2018-04-24 14:39:58 +02:00
2017-06-13 15:32:44 +02:00
default :
show_usage ( ) ;
2017-08-09 16:45:27 +02:00
return ;
}
if ( status ! = ERROR_CODE_SUCCESS ) {
2020-09-04 17:15:29 +02:00
printf ( " Could not perform command, status 0x%02x \n " , status ) ;
2017-06-13 15:32:44 +02:00
}
}
# endif
int btstack_main ( int argc , const char * argv [ ] ) ;
int btstack_main ( int argc , const char * argv [ ] ) {
2018-07-20 16:13:33 +02:00
UNUSED ( argc ) ;
2017-06-13 15:32:44 +02:00
( void ) argv ;
2018-07-20 16:13:33 +02:00
a2dp_and_avrcp_setup ( ) ;
# ifdef HAVE_BTSTACK_STDIN
// parse human readable Bluetooth address
sscanf_bd_addr ( device_addr_string , device_addr ) ;
btstack_stdin_setup ( stdin_process ) ;
# endif
2017-06-13 15:32:44 +02:00
// turn on!
2017-10-31 12:52:56 +01:00
printf ( " Starting BTstack ... \n " ) ;
2017-06-13 15:32:44 +02:00
hci_power_control ( HCI_POWER_ON ) ;
return 0 ;
}
2020-11-22 22:58:37 +01:00
/* EXAMPLE_END */