| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121 |
- /*
- * 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
- *
- */
- #define BTSTACK_FILE__ "a2dp_sink_demo.c"
- /*
- * a2dp_sink_demo.c
- */
- // *****************************************************************************
- /* EXAMPLE_START(a2dp_sink_demo): Receive audio stream and control its playback.
- *
- * @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,
- * 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.
- *
- * @text To test with a remote device, e.g. a mobile phone,
- * 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.
- *
- * @text For more info on BTstack audio, see our blog post
- * [A2DP Sink and Source on STM32 F4 Discovery Board](http://bluekitchen-gmbh.com/a2dp-sink-and-source-on-stm32-f4-discovery-board/).
- *
- */
- // *****************************************************************************
- #include <inttypes.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "btstack.h"
- #include "btstack_resample.h"
- //#define AVRCP_BROWSING_ENABLED
- // 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
- #define SUPPORT_VOLUME_CHANGE_NOTIFICATION
- #ifdef HAVE_BTSTACK_STDIN
- #include "btstack_stdin.h"
- #endif
- #include "btstack_ring_buffer.h"
- #ifdef HAVE_POSIX_FILE_IO
- #include "wav_util.h"
- #define STORE_TO_SBC_FILE
- #define STORE_TO_WAV_FILE
- #endif
- #define NUM_CHANNELS 2
- #define BYTES_PER_FRAME (2*NUM_CHANNELS)
- #define MAX_SBC_FRAME_SIZE 120
- // SBC Decoder for WAV file or live playback
- static btstack_sbc_decoder_state_t state;
- static btstack_sbc_mode_t mode = SBC_MODE_STANDARD;
- // ring buffer for SBC Frames
- // below 30: add samples, 30-40: fine, above 40: drop samples
- #define OPTIMAL_FRAMES_MIN 30
- #define OPTIMAL_FRAMES_MAX 40
- #define ADDITIONAL_FRAMES 20
- 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;
- // rest buffer for not fully used sbc frames, with additional frames for resampling
- static uint8_t decoded_audio_storage[(128+16) * BYTES_PER_FRAME];
- static btstack_ring_buffer_t decoded_audio_ring_buffer;
-
- static int audio_stream_started;
- // temp storage of lower-layer request
- static int16_t * request_buffer;
- static int request_frames;
- #define STORE_FROM_PLAYBACK
- // WAV File
- #ifdef STORE_TO_WAV_FILE
- static uint32_t audio_frame_count = 0;
- static char * wav_filename = "av2dp_sink_demo.wav";
- #endif
- #ifdef STORE_TO_SBC_FILE
- static FILE * sbc_file;
- static char * sbc_filename = "av2dp_sink_demo.sbc";
- #endif
- typedef struct {
- int reconfigure;
- int num_channels;
- int sampling_frequency;
- int channel_mode;
- int block_length;
- int subbands;
- int allocation_method;
- int min_bitpool_value;
- int max_bitpool_value;
- int frames_per_buffer;
- } avdtp_media_codec_configuration_sbc_t;
- static avdtp_media_codec_configuration_sbc_t sbc_configuration;
- static int volume_percentage = 0;
- #ifdef SUPPORT_VOLUME_CHANGE_NOTIFICATION
- static uint8_t events_num = 3;
- static uint8_t events[] = {
- AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED,
- AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED,
- AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED
- };
- #endif
- static uint8_t companies_num = 1;
- static uint8_t companies[] = {
- 0x00, 0x19, 0x58 //BT SIG registered CompanyID
- };
- #ifdef HAVE_BTSTACK_STDIN
- // pts:
- static const char * device_addr_string = "6C:72:E7:10:22:EE";
- // 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;
- #endif
- static btstack_packet_callback_registration_t hci_event_callback_registration;
- static uint8_t sdp_avdtp_sink_service_buffer[150];
- static uint8_t sdp_avrcp_target_service_buffer[150];
- static uint8_t sdp_avrcp_controller_service_buffer[200];
- static uint16_t a2dp_cid = 0;
- static uint8_t a2dp_local_seid = 0;
- static uint16_t avrcp_cid = 0;
- static uint8_t avrcp_connected = 0;
- static uint8_t avrcp_subevent_value[100];
- 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
- };
- static uint8_t media_sbc_codec_configuration[] = {
- (AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO,
- (AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS,
- 2, 53
- };
- static int media_initialized = 0;
- static btstack_resample_t resample_instance;
- /* @section Main Application Setup
- *
- * @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.
- * - 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).
- * - 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.
- * - 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.
- * - 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.
- *
- * @text To announce A2DP Sink and AVRCP services, you need to create corresponding
- * SDP records and register them with the SDP service.
- *
- * @text Note, currently only the SBC codec is supported.
- * 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.
- * The initialization of the SBC decoder requires a callback that handles PCM data:
- * - 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.
- */
- /* LISTING_START(MainConfiguration): Setup Audio Sink and AVRCP Controller services */
- static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
- static void a2dp_sink_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * event, uint16_t event_size);
- static void handle_l2cap_media_data_packet(uint8_t seid, uint8_t *packet, uint16_t size);
- static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
- static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
- static void avrcp_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
- #ifdef HAVE_BTSTACK_STDIN
- static void stdin_process(char cmd);
- #endif
- static int a2dp_and_avrcp_setup(void){
- l2cap_init();
- // Initialize AVDTP Sink
- a2dp_sink_init();
- a2dp_sink_register_packet_handler(&a2dp_sink_packet_handler);
- a2dp_sink_register_media_handler(&handle_l2cap_media_data_packet);
- 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");
- return 1;
- }
- a2dp_local_seid = avdtp_local_seid(local_stream_endpoint);
- // Initialize AVRCP service.
- avrcp_init();
- avrcp_register_packet_handler(&avrcp_packet_handler);
-
- // Initialize AVRCP Controller
- avrcp_controller_init();
- avrcp_controller_register_packet_handler(&avrcp_controller_packet_handler);
-
- // Initialize AVRCP Target
- avrcp_target_init();
- avrcp_target_register_packet_handler(&avrcp_target_packet_handler);
-
- // Initialize SDP
- sdp_init();
- // setup AVDTP sink
- memset(sdp_avdtp_sink_service_buffer, 0, sizeof(sdp_avdtp_sink_service_buffer));
- a2dp_sink_create_sdp_record(sdp_avdtp_sink_service_buffer, 0x10001, AVDTP_SINK_FEATURE_MASK_HEADPHONE, NULL, NULL);
- sdp_register_service(sdp_avdtp_sink_service_buffer);
-
- // setup AVRCP Controller
- memset(sdp_avrcp_controller_service_buffer, 0, sizeof(sdp_avrcp_controller_service_buffer));
- uint16_t controller_supported_features = AVRCP_FEATURE_MASK_CATEGORY_PLAYER_OR_RECORDER;
- #ifdef AVRCP_BROWSING_ENABLED
- controller_supported_features |= AVRCP_FEATURE_MASK_BROWSING;
- #endif
- avrcp_controller_create_sdp_record(sdp_avrcp_controller_service_buffer, 0x10002, controller_supported_features, NULL, NULL);
- sdp_register_service(sdp_avrcp_controller_service_buffer);
-
- // setup AVRCP Target
- memset(sdp_avrcp_target_service_buffer, 0, sizeof(sdp_avrcp_target_service_buffer));
- uint16_t target_supported_features = AVRCP_FEATURE_MASK_CATEGORY_MONITOR_OR_AMPLIFIER;
- avrcp_target_create_sdp_record(sdp_avrcp_target_service_buffer, 0x10003, target_supported_features, NULL, NULL);
- sdp_register_service(sdp_avrcp_target_service_buffer);
- gap_set_local_name("A2DP Sink Demo 00:00:00:00:00:00");
- gap_discoverable_control(1);
- gap_set_class_of_device(0x200408);
- /* Register for HCI events */
- hci_event_callback_registration.callback = &hci_packet_handler;
- hci_add_event_handler(&hci_event_callback_registration);
- #ifdef HAVE_POSIX_FILE_IO
- if (!btstack_audio_sink_get_instance()){
- printf("No audio playback.\n");
- } else {
- printf("Audio playback supported.\n");
- }
- #ifdef STORE_TO_WAV_FILE
- printf("Audio will be stored to \'%s\' file.\n", wav_filename);
- #endif
- #endif
- return 0;
- }
- /* LISTING_END */
- static void playback_handler(int16_t * buffer, uint16_t num_audio_frames){
- #ifdef STORE_TO_WAV_FILE
- int wav_samples = num_audio_frames * NUM_CHANNELS;
- int16_t * wav_buffer = buffer;
- #endif
-
- // called from lower-layer but guaranteed to be on main thread
- if (sbc_frame_size == 0){
- memset(buffer, 0, num_audio_frames * BYTES_PER_FRAME);
- return;
- }
- // first fill from resampled audio
- uint32_t bytes_read;
- btstack_ring_buffer_read(&decoded_audio_ring_buffer, (uint8_t *) buffer, num_audio_frames * BYTES_PER_FRAME, &bytes_read);
- buffer += bytes_read / NUM_CHANNELS;
- num_audio_frames -= bytes_read / BYTES_PER_FRAME;
- // then start decoding sbc frames using request_* globals
- request_buffer = buffer;
- request_frames = num_audio_frames;
- 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);
- }
- #ifdef STORE_TO_WAV_FILE
- audio_frame_count += num_audio_frames;
- wav_writer_write_int16(wav_samples, wav_buffer);
- #endif
- }
- static void handle_pcm_data(int16_t * data, int num_audio_frames, int num_channels, int sample_rate, void * context){
- UNUSED(sample_rate);
- UNUSED(context);
- UNUSED(num_channels); // must be stereo == 2
- const btstack_audio_sink_t * audio_sink = btstack_audio_sink_get_instance();
- if (!audio_sink){
- #ifdef STORE_TO_WAV_FILE
- audio_frame_count += num_audio_frames;
- wav_writer_write_int16(num_audio_frames * NUM_CHANNELS, data);
- #endif
- return;
- }
- // resample into request buffer - add some additional space for resampling
- int16_t output_buffer[(128+16) * NUM_CHANNELS]; // 16 * 8 * 2
- uint32_t resampled_frames = btstack_resample_block(&resample_instance, data, num_audio_frames, output_buffer);
- // store data in btstack_audio buffer first
- 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;
- // and rest in ring buffer
- 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");
- }
- }
- }
- static int media_processing_init(avdtp_media_codec_configuration_sbc_t configuration){
- if (media_initialized) return 0;
- btstack_sbc_decoder_init(&state, mode, handle_pcm_data, NULL);
- #ifdef STORE_TO_WAV_FILE
- wav_writer_open(wav_filename, configuration.num_channels, configuration.sampling_frequency);
- #endif
- #ifdef STORE_TO_SBC_FILE
- sbc_file = fopen(sbc_filename, "wb");
- #endif
- 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));
- btstack_resample_init(&resample_instance, configuration.num_channels);
- // setup audio playback
- const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance();
- if (audio){
- audio->init(NUM_CHANNELS, configuration.sampling_frequency, &playback_handler);
- }
- audio_stream_started = 0;
- media_initialized = 1;
- return 0;
- }
- 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;
- }
- 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){
- if (!media_initialized) return;
- media_initialized = 0;
- audio_stream_started = 0;
- sbc_frame_size = 0;
- #ifdef STORE_TO_WAV_FILE
- wav_writer_close();
- uint32_t total_frames_nr = state.good_frames_nr + state.bad_frames_nr + state.zero_frames_nr;
- 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);
- #endif
- #ifdef STORE_TO_SBC_FILE
- fclose(sbc_file);
- #endif
- // stop audio playback
- const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance();
- if (audio){
- printf("close stream\n");
- audio->close();
- }
- }
- /* @section Handle Media Data Packet
- *
- * @text Media data packets, in this case the audio data, are received through the handle_l2cap_media_data_packet callback.
- * Currently, only the SBC media codec is supported. Hence, the media data consists of the media packet header and the SBC packet.
- * 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)
- * If the audio stream wasn't started already and there are enough SBC frames in the ring buffer, start playback.
- */
- 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);
- 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 STORE_TO_SBC_FILE
- fwrite(packet+pos, size-pos, 1, sbc_file);
- #endif
- const btstack_audio_sink_t * audio = btstack_audio_sink_get_instance();
- // 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;
- }
- // store sbc frame size for buffer management
- sbc_frame_size = (size-pos)/ sbc_header.num_frames;
-
- 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");
- }
- // decide on audio sync drift based on number of sbc frames in queue
- int sbc_frames_in_buffer = btstack_ring_buffer_bytes_available(&sbc_frame_ring_buffer) / sbc_frame_size;
- uint32_t resampling_factor;
- // nomimal factor (fixed-point 2^16) and compensation offset
- uint32_t nomimal_factor = 0x10000;
- uint32_t compensation = 0x00100;
- if (sbc_frames_in_buffer < OPTIMAL_FRAMES_MIN){
- resampling_factor = nomimal_factor - compensation; // stretch samples
- } else if (sbc_frames_in_buffer <= OPTIMAL_FRAMES_MAX){
- resampling_factor = nomimal_factor; // nothing to do
- } else {
- resampling_factor = nomimal_factor + compensation; // compress samples
- }
- btstack_resample_set_factor(&resample_instance, resampling_factor);
- // start stream if enough frames buffered
- if (!audio_stream_started && sbc_frames_in_buffer >= OPTIMAL_FRAMES_MIN){
- audio_stream_started = 1;
- // setup audio playback
- if (audio){
- audio->start_stream();
- }
- }
- }
- 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);
- return 0;
- }
- 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;
- return 1;
- }
- 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);
- return 0;
- }
- 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;
- return 1;
- }
- static void dump_sbc_configuration(avdtp_media_codec_configuration_sbc_t configuration){
- 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);
- printf("\n");
- }
- static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
- UNUSED(channel);
- UNUSED(size);
- uint16_t local_cid;
- uint8_t status = 0xFF;
- bd_addr_t adress;
-
- 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){
- printf("AVRCP: Connection failed: status 0x%02x\n", status);
- avrcp_cid = 0;
- return;
- }
-
- avrcp_cid = local_cid;
- avrcp_connected = 1;
- avrcp_subevent_connection_established_get_bd_addr(packet, adress);
- printf("AVRCP: Connected to %s, cid 0x%02x\n", bd_addr_to_str(adress), avrcp_cid);
- // automatically enable notifications
- 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);
- return;
- }
-
- case AVRCP_SUBEVENT_CONNECTION_RELEASED:
- printf("AVRCP: Channel released: cid 0x%02x\n", avrcp_subevent_connection_released_get_avrcp_cid(packet));
- avrcp_cid = 0;
- avrcp_connected = 0;
- return;
- default:
- break;
- }
- }
- 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;
-
- status = packet[5];
- if (!avrcp_cid) return;
- // ignore INTERIM status
- if (status == AVRCP_CTYPE_RESPONSE_INTERIM){
- 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){
- printf("AVRCP Controller: playback position changed, no track is selected\n");
- }
- break;
- }
- default:
- break;
- }
- return;
- }
-
- memset(avrcp_subevent_value, 0, sizeof(avrcp_subevent_value));
- switch (packet[2]){
- case AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_POS_CHANGED:
- printf("AVRCP Controller: Playback position changed, position %d ms\n", (unsigned int) avrcp_subevent_notification_playback_pos_changed_get_playback_position_ms(packet));
- break;
- case AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED:
- printf("AVRCP Controller: Playback status changed %s\n", avrcp_play_status2str(avrcp_subevent_notification_playback_status_changed_get_play_status(packet)));
- return;
- case AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED:
- printf("AVRCP Controller: Playing content changed\n");
- return;
- case AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED:
- printf("AVRCP Controller: Track changed\n");
- return;
- case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED:
- printf("AVRCP Controller: Absolute volume changed %d\n", avrcp_subevent_notification_volume_changed_get_absolute_volume(packet));
- return;
- case AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED:
- printf("AVRCP Controller: Changed\n");
- 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);
- printf("AVRCP Controller: %s, %s\n", avrcp_shuffle2str(shuffle_mode), avrcp_repeat2str(repeat_mode));
- break;
- }
- case AVRCP_SUBEVENT_NOW_PLAYING_TRACK_INFO:
- printf("AVRCP Controller: Track: %d\n", avrcp_subevent_now_playing_track_info_get_track(packet));
- break;
- case AVRCP_SUBEVENT_NOW_PLAYING_TOTAL_TRACKS_INFO:
- printf("AVRCP Controller: Total Tracks: %d\n", avrcp_subevent_now_playing_total_tracks_info_get_total_tracks(packet));
- break;
- case AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO:
- if (avrcp_subevent_now_playing_title_info_get_value_len(packet) > 0){
- 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);
- }
- break;
- case AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO:
- if (avrcp_subevent_now_playing_artist_info_get_value_len(packet) > 0){
- 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);
- }
- break;
-
- case AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO:
- if (avrcp_subevent_now_playing_album_info_get_value_len(packet) > 0){
- 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);
- }
- break;
-
- case AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO:
- if (avrcp_subevent_now_playing_genre_info_get_value_len(packet) > 0){
- 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);
- }
- break;
-
- case AVRCP_SUBEVENT_PLAY_STATUS:
- printf("AVRCP Controller: Song length %"PRIu32" ms, Song position %"PRIu32" ms, Play status %s\n",
- 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;
-
- case AVRCP_SUBEVENT_OPERATION_COMPLETE:
- printf("AVRCP Controller: %s done\n", avrcp_operation2str(avrcp_subevent_operation_complete_get_operation_id(packet)));
- break;
-
- case AVRCP_SUBEVENT_OPERATION_START:
- printf("AVRCP Controller: Start %s\n", avrcp_operation2str(avrcp_subevent_operation_complete_get_operation_id(packet)));
- break;
-
- default:
- printf("AVRCP Controller: Event 0x%02x is not parsed\n", packet[2]);
- break;
- }
- }
- 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);
- }
- }
- 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;
-
- uint8_t volume;
- switch (packet[2]){
- case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED:
- volume = avrcp_subevent_notification_volume_changed_get_absolute_volume(packet);
- volume_percentage = volume * 100 / 127;
- printf("AVRCP Target : Volume set to %d%% (%d)\n", volume_percentage, volume);
- avrcp_volume_changed(volume);
- break;
-
- case AVRCP_SUBEVENT_EVENT_IDS_QUERY:
- #ifdef SUPPORT_VOLUME_CHANGE_NOTIFICATION
- avrcp_target_supported_events(avrcp_cid, events_num, events, sizeof(events));
- #else
- avrcp_target_supported_events(avrcp_cid, 0, NULL, 0);
- #endif
- break;
- case AVRCP_SUBEVENT_COMPANY_IDS_QUERY:
- avrcp_target_supported_companies(avrcp_cid, companies_num, companies, sizeof(companies));
- 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:
- printf("AVRCP Target : PLAY\n");
- break;
- case AVRCP_OPERATION_ID_PAUSE:
- printf("AVRCP Target : PAUSE\n");
- break;
- case AVRCP_OPERATION_ID_STOP:
- printf("AVRCP Target : STOP\n");
- break;
- case AVRCP_OPERATION_ID_REWIND:
- printf("AVRCP Target : REWIND\n");
- break;
- case AVRCP_OPERATION_ID_FAST_FORWARD:
- printf("AVRCP Target : FAST_FORWARD\n");
- break;
- case AVRCP_OPERATION_ID_FORWARD:
- printf("AVRCP Target : FORWARD\n");
- break;
- case AVRCP_OPERATION_ID_BACKWARD:
- printf("AVRCP Target : BACKWARD\n");
- break;
- case AVRCP_OPERATION_ID_SKIP:
- printf("AVRCP Target : SKIP\n");
- break;
- case AVRCP_OPERATION_ID_MUTE:
- printf("AVRCP Target : MUTE\n");
- break;
- case AVRCP_OPERATION_ID_CHANNEL_UP:
- printf("AVRCP Target : CHANNEL_UP\n");
- break;
- case AVRCP_OPERATION_ID_CHANNEL_DOWN:
- printf("AVRCP Target : CHANNEL_DOWN\n");
- break;
- case AVRCP_OPERATION_ID_SELECT:
- printf("AVRCP Target : SELECT\n");
- break;
- case AVRCP_OPERATION_ID_UP:
- printf("AVRCP Target : UP\n");
- break;
- case AVRCP_OPERATION_ID_DOWN:
- printf("AVRCP Target : DOWN\n");
- break;
- case AVRCP_OPERATION_ID_LEFT:
- printf("AVRCP Target : LEFT\n");
- break;
- case AVRCP_OPERATION_ID_RIGHT:
- printf("AVRCP Target : RIGTH\n");
- break;
- case AVRCP_OPERATION_ID_ROOT_MENU:
- printf("AVRCP Target : ROOT_MENU\n");
- break;
- default:
- return;
- }
- break;
- }
- default:
- printf("AVRCP Target : Event 0x%02x is not parsed\n", packet[2]);
- break;
- }
- }
- static void hci_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_PIN_CODE_REQUEST) {
- bd_addr_t address;
- printf("Pin code request - using '0000'\n");
- hci_event_pin_code_request_get_bd_addr(packet, address);
- gap_pin_code_response(address, "0000");
- }
- }
- 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;
- if (packet_type != HCI_EVENT_PACKET) return;
- if (hci_event_packet_get_type(packet) != HCI_EVENT_A2DP_META) return;
- switch (packet[2]){
- case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION:
- printf("A2DP Sink : Received non SBC codec - not implemented\n");
- break;
- case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION:{
- printf("A2DP Sink : Received SBC codec configuration\n");
- 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.channel_mode = a2dp_subevent_signaling_media_codec_sbc_configuration_get_channel_mode(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.allocation_method = a2dp_subevent_signaling_media_codec_sbc_configuration_get_allocation_method(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);
- sbc_configuration.frames_per_buffer = sbc_configuration.subbands * sbc_configuration.block_length;
- dump_sbc_configuration(sbc_configuration);
- if (sbc_configuration.reconfigure){
- media_processing_close();
- }
- // prepare media processing
- media_processing_init(sbc_configuration);
- break;
- }
- case A2DP_SUBEVENT_STREAM_ESTABLISHED:
- a2dp_subevent_stream_established_get_bd_addr(packet, address);
- status = a2dp_subevent_stream_established_get_status(packet);
-
- if (status){
- printf("A2DP Sink : Streaming connection failed, status 0x%02x\n", status);
- break;
- }
-
- a2dp_cid = a2dp_subevent_stream_established_get_a2dp_cid(packet);
- memcpy(device_addr, address, 6);
- 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);
- break;
-
- case A2DP_SUBEVENT_STREAM_STARTED:
- printf("A2DP Sink : Stream started\n");
- media_processing_start();
- break;
-
- case A2DP_SUBEVENT_STREAM_SUSPENDED:
- printf("A2DP Sink : Stream paused\n");
- media_processing_pause();
- break;
-
- case A2DP_SUBEVENT_STREAM_RELEASED:
- printf("A2DP Sink : Stream released\n");
- media_processing_close();
- break;
-
- case A2DP_SUBEVENT_SIGNALING_CONNECTION_RELEASED:
- printf("A2DP Sink : Signaling connection released\n");
- media_processing_close();
- break;
-
- default:
- printf("A2DP Sink : Not parsed 0x%02x\n", packet[2]);
- break;
- }
- }
- #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));
- printf("b - AVDTP Sink create connection to addr %s\n", bd_addr_to_str(device_addr));
- printf("B - AVDTP Sink disconnect\n");
- printf("c - AVRCP create connection to addr %s\n", bd_addr_to_str(device_addr));
- printf("C - AVRCP disconnect\n");
- printf("w - delay report\n");
- 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");
- printf("L - pause\n");
- printf("u - start fast forward\n");
- printf("U - stop fast forward\n");
- 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");
- printf("a/A - register/deregister TRACK_CHANGED\n");
- printf("R/P - register/deregister PLAYBACK_POS_CHANGED\n");
- printf("\n--- Volume Control ---\n");
- printf("t - volume up for 10 percent\n");
- printf("T - volume down for 10 percent\n");
- printf("---\n");
- }
- #endif
- #ifdef HAVE_BTSTACK_STDIN
- static void stdin_process(char cmd){
- uint8_t status = ERROR_CODE_SUCCESS;
- uint8_t volume;
- switch (cmd){
- case 'b':
- 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);
- break;
- case 'B':
- printf(" - AVDTP disconnect from addr %s.\n", bd_addr_to_str(device_addr));
- status = avdtp_sink_disconnect(a2dp_cid);
- break;
- case 'c':
- printf(" - Create AVRCP connection to addr %s.\n", bd_addr_to_str(device_addr));
- status = avrcp_connect(device_addr, &avrcp_cid);
- break;
- case 'C':
- printf(" - AVRCP disconnect from addr %s.\n", bd_addr_to_str(device_addr));
- status = avrcp_disconnect(avrcp_cid);
- break;
-
- case '\n':
- case '\r':
- break;
- case 'w':
- printf("Send delay report\n");
- avdtp_sink_delay_report(a2dp_cid, a2dp_local_seid, 100);
- break;
- // Volume Control
- case 't':
- volume_percentage = volume_percentage <= 90 ? volume_percentage + 10 : 100;
- 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);
- break;
- case 'T':
- volume_percentage = volume_percentage >= 10 ? volume_percentage - 10 : 0;
- 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);
- break;
- case 'O':
- printf(" - get play status\n");
- status = avrcp_controller_get_play_status(avrcp_cid);
- break;
- case 'j':
- printf(" - get now playing info\n");
- status = avrcp_controller_get_now_playing_info(avrcp_cid);
- break;
- case 'k':
- printf(" - play\n");
- status = avrcp_controller_play(avrcp_cid);
- break;
- case 'K':
- printf(" - stop\n");
- status = avrcp_controller_stop(avrcp_cid);
- break;
- case 'L':
- printf(" - pause\n");
- status = avrcp_controller_pause(avrcp_cid);
- break;
- case 'u':
- printf(" - start fast forward\n");
- status = avrcp_controller_press_and_hold_fast_forward(avrcp_cid);
- break;
- case 'U':
- printf(" - stop fast forward\n");
- status = avrcp_controller_release_press_and_hold_cmd(avrcp_cid);
- break;
- case 'n':
- printf(" - start rewind\n");
- status = avrcp_controller_press_and_hold_rewind(avrcp_cid);
- break;
- case 'N':
- printf(" - stop rewind\n");
- status = avrcp_controller_release_press_and_hold_cmd(avrcp_cid);
- break;
- case 'i':
- printf(" - forward\n");
- status = avrcp_controller_forward(avrcp_cid);
- break;
- case 'I':
- printf(" - backward\n");
- status = avrcp_controller_backward(avrcp_cid);
- break;
- case 'M':
- printf(" - mute\n");
- status = avrcp_controller_mute(avrcp_cid);
- break;
- case 'r':
- printf(" - skip\n");
- status = avrcp_controller_skip(avrcp_cid);
- break;
- case 'q':
- printf(" - query repeat and shuffle mode\n");
- status = avrcp_controller_query_shuffle_and_repeat_modes(avrcp_cid);
- break;
- case 'v':
- printf(" - repeat single track\n");
- status = avrcp_controller_set_repeat_mode(avrcp_cid, AVRCP_REPEAT_MODE_SINGLE_TRACK);
- break;
- case 'x':
- printf(" - repeat all tracks\n");
- status = avrcp_controller_set_repeat_mode(avrcp_cid, AVRCP_REPEAT_MODE_ALL_TRACKS);
- break;
- case 'X':
- printf(" - disable repeat mode\n");
- status = avrcp_controller_set_repeat_mode(avrcp_cid, AVRCP_REPEAT_MODE_OFF);
- break;
- case 'z':
- printf(" - shuffle all tracks\n");
- status = avrcp_controller_set_shuffle_mode(avrcp_cid, AVRCP_SHUFFLE_MODE_ALL_TRACKS);
- break;
- case 'Z':
- printf(" - disable shuffle mode\n");
- status = avrcp_controller_set_shuffle_mode(avrcp_cid, AVRCP_SHUFFLE_MODE_OFF);
- break;
- case 'a':
- printf("AVRCP: enable notification TRACK_CHANGED\n");
- avrcp_controller_enable_notification(avrcp_cid, AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED);
- break;
- case 'A':
- printf("AVRCP: disable notification TRACK_CHANGED\n");
- avrcp_controller_disable_notification(avrcp_cid, AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED);
- break;
- case 'R':
- printf("AVRCP: enable notification PLAYBACK_POS_CHANGED\n");
- avrcp_controller_enable_notification(avrcp_cid, AVRCP_NOTIFICATION_EVENT_PLAYBACK_POS_CHANGED);
- break;
- case 'P':
- printf("AVRCP: disable notification PLAYBACK_POS_CHANGED\n");
- avrcp_controller_disable_notification(avrcp_cid, AVRCP_NOTIFICATION_EVENT_PLAYBACK_POS_CHANGED);
- break;
- default:
- show_usage();
- return;
- }
- if (status != ERROR_CODE_SUCCESS){
- printf("Could not perform command, status 0x%2x\n", status);
- }
- }
- #endif
- int btstack_main(int argc, const char * argv[]);
- int btstack_main(int argc, const char * argv[]){
- UNUSED(argc);
- (void)argv;
- 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
- // turn on!
- printf("Starting BTstack ...\n");
- hci_power_control(HCI_POWER_ON);
- return 0;
- }
- /* EXAMPLE_END */
|