/**************************************************************************** NAME linkPolicyPhonebookAccessComplete DESCRIPTION set the link policy requirements back after phonebook access, based on current device audio state RETURNS void */ void linkPolicyPhonebookAccessComplete(Sink sink) { typed_bdaddr taddr; uint16 DeviceId; uint16 StreamId; uint8 i; bool a2dpSetting = FALSE; /* If device is in the stream a2dp state, use a2dp link policy */ for_all_a2dp(i) { DeviceId = theSink.a2dp_link_data->device_id[i]; StreamId = theSink.a2dp_link_data->stream_id[i]; if( SinkGetBdAddr(sink, &taddr) && BdaddrIsSame(&theSink.a2dp_link_data->bd_addr[i], &taddr.addr) ) { a2dpSetting = TRUE; if(A2dpMediaGetState(DeviceId, StreamId)== a2dp_stream_streaming) linkPolicyUseA2dpSettings(DeviceId, StreamId, A2dpMediaGetSink(DeviceId, StreamId)); else linkPolicyUseHfpSettings(hfp_primary_link, sink); } } /* Otherwise, use hfp link policy */ if(!a2dpSetting) { linkPolicyUseHfpSettings(hfp_primary_link, sink); } }
void A2dpSendMediaPacketTestExtra(uint16 device_id, uint16 stream_id) { static const uint8 pkt[] = { 0x80, 0x01, 0x50, 0xcf, 0xcb, 0xd7, 0xd0, 0x20, 0xfa, 0x84, 0xea, 0x9b, 0x05, 0x9c, 0xbd, 0x3b, 0xff, 0xfc, 0xca, 0x88, 0x78, 0x33, 0x98, 0x86, 0x85, 0x66, 0x7f, 0xbe, 0xee, 0xdd }; Sink media_sink = A2dpMediaGetSink( device_id, stream_id ); uint8 *dest = SinkMap(media_sink) + SinkClaim(media_sink, sizeof(pkt)); memmove(dest, pkt, sizeof(pkt)); SinkFlush(media_sink, sizeof(pkt)); }
/**************************************************************************** NAME CheckVolumeA2dp DESCRIPTION check whether any a2dp connections are present and if these are currently active and routing audio to the headset, if that is the case adjust the volume up or down as appropriate RETURNS void */ bool CheckVolumeA2dp(volume_direction dir) { uint8 index; /* check both possible instances of a2dp connection */ for(index = a2dp_primary; index < (a2dp_secondary+1); index++) { /* is a2dp connected? */ if(theHeadset.a2dp_link_data->connected[index]) { /* check whether the a2dp connection is present and streaming data and that the audio is routed */ if(theHeadset.sco_sink && (theHeadset.sco_sink == A2dpMediaGetSink(theHeadset.a2dp_link_data->device_id[index], theHeadset.a2dp_link_data->stream_id[index]))) { /* get current volume for this profile */ uint16 lOldVol = theHeadset.a2dp_link_data->gAvVolumeLevel[index]; /* change up or down */ if(dir == increase_volume) { /* increase volume by one level up to maximum */ theHeadset.a2dp_link_data->gAvVolumeLevel[index] = theHeadset.audioData.gVolMaps[lOldVol].IncVol ; /* limit to maximum and send notification event when at max level */ if(theHeadset.a2dp_link_data->gAvVolumeLevel[index] >= VOLUME_A2DP_MAX_LEVEL) { theHeadset.a2dp_link_data->gAvVolumeLevel[index] = VOLUME_A2DP_MAX_LEVEL; MessageSend ( &theHeadset.task , EventVolumeMax , 0 ); } } /* decrease volume */ else { /* decrease volume by one level down to minimum */ theHeadset.a2dp_link_data->gAvVolumeLevel[index] = theHeadset.audioData.gVolMaps[lOldVol].DecVol ; /* limit to minimum and send notification event when at min level */ if(theHeadset.a2dp_link_data->gAvVolumeLevel[index] == VOLUME_A2DP_MIN_LEVEL) { MessageSend ( &theHeadset.task , EventVolumeMin , 0 ); } } #ifdef ENABLE_AVRCP { uint16 vol_step_change = 0; if (lOldVol > theHeadset.a2dp_link_data->gAvVolumeLevel[index]) vol_step_change = lOldVol - theHeadset.a2dp_link_data->gAvVolumeLevel[index]; else vol_step_change = theHeadset.a2dp_link_data->gAvVolumeLevel[index] - lOldVol; headsetAvrcpVolumeStepChange(dir, vol_step_change); } #endif #if 1 VolumeSetA2dp(index, lOldVol, dir); #else VolumeSetA2dp(index, lOldVol); #endif /* volume adjusted for a A2DP media stream */ return TRUE; } } } /* no routed a2dp media streams found */ return FALSE; }
/**************************************************************************** NAME sinkCheckPartyModeAudio DESCRIPTION Called when checking for PartyMode being enabled, if not enabled no action is taken, when enabled decisions are made as to what audio should be playing or paused/resumed etc RETURNS bool false if party mode not enabled, true is party mode enabled and action has been taken with regards to the routing of the audio source */ bool sinkCheckPartyModeAudio(audio_sources requested_source, audio_source_status * lAudioStatus) { uint8 index; /* check whether party mode is enabled and configured and if the currently routed audio is one of the a2dp streams */ if((theSink.PartyModeEnabled)&&(theSink.features.PartyMode)&&(lAudioStatus->audio_routed)&& ((lAudioStatus->a2dpSinkPri == lAudioStatus->audio_routed)||(lAudioStatus->a2dpSinkSec == lAudioStatus->audio_routed)) ) { /* determine the PartyMode operating mode required */ switch(theSink.features.PartyMode) { /* simple barge-in mode of operation, a new audio source streaming music disconnects any currently streaming device */ case partymode_barge_in: PTY_DEBUG(("PTY: bargein\n")); /* if the current streaming audio A2DP pri and a stream for A2DP sec is available, switch to that */ if((lAudioStatus->a2dpSinkPri == lAudioStatus->audio_routed)&&(lAudioStatus->a2dpStateSec == a2dp_stream_streaming)) { PTY_DEBUG(("PTY: drop current pri route new sec\n")); /* drop bluetooth connection to device currently streaming */ for(index = a2dp_primary; index < (a2dp_secondary+1); index++) { /* is a2dp connected? */ if(theSink.a2dp_link_data->connected[index]) { /* check whether the a2dp connection is present and streaming data and that the audio is routed */ if(lAudioStatus->audio_routed == A2dpMediaGetSink(theSink.a2dp_link_data->device_id[index], theSink.a2dp_link_data->stream_id[index])) { /* disconnect a2dp audio device */ sinkPartyModeDisconnectDevice(index); } } } /* route the audio from the new device */ A2dpRouteAudio(a2dp_secondary, lAudioStatus->a2dpSinkSec); } /* if the current streaming audio A2DP sec and a stream for A2DP pri is available, switch to that */ else if((lAudioStatus->a2dpSinkSec == lAudioStatus->audio_routed)&&(lAudioStatus->a2dpStatePri == a2dp_stream_streaming)) { PTY_DEBUG(("PTY: drop current sec route new pri\n")); /* drop bluetooth connection to device currently streaming */ for(index = a2dp_primary; index < (a2dp_secondary+1); index++) { /* is a2dp connected? */ if(theSink.a2dp_link_data->connected[index]) { /* check whether the a2dp connection is present and streaming data and that the audio is routed */ if(lAudioStatus->audio_routed == A2dpMediaGetSink(theSink.a2dp_link_data->device_id[index], theSink.a2dp_link_data->stream_id[index])) { /* disconnect a2dp audio device */ sinkPartyModeDisconnectDevice(index); } } } /* route the audio from the new device */ A2dpRouteAudio(a2dp_primary, lAudioStatus->a2dpSinkPri); } /* no action has been taken */ else return TRUE; break; /* more complex use case, a new streaming audio source is paused using avrcp until the current playing track completes */ case partymode_avrcp_control: PTY_DEBUG(("PTY: avrcp ctrl AG1[%x] AG2[%x] AVRCP1[%x] AVRCP[%x] \n",lAudioStatus->a2dpStatePri,lAudioStatus->a2dpStateSec,theSink.avrcp_link_data->play_status[a2dp_primary],theSink.avrcp_link_data->play_status[a2dp_secondary])); /* if the current streaming audio is A2DP pri and a stream for A2DP sec is available, pause that and wait for the current track to finish playing */ if((lAudioStatus->a2dpSinkPri == lAudioStatus->audio_routed)&&(lAudioStatus->a2dpStatePri == a2dp_stream_streaming)&& ((lAudioStatus->a2dpStateSec == a2dp_stream_streaming)&& (sinkCheckAvrcpStateMatch(a2dp_secondary, avrcp_play_status_playing))) ) { PTY_DEBUG(("PTY: suspend a2dp sec audio until pri track finished \n")); SuspendA2dpStream(a2dp_secondary); /* set paused flag */ theSink.rundata->partymode_pause.audio_source_secondary_paused = TRUE; } /* if the current streaming audio A2DP sec and a stream for A2DP pri is available, switch to that */ else if((lAudioStatus->a2dpSinkSec == lAudioStatus->audio_routed)&&(lAudioStatus->a2dpStateSec == a2dp_stream_streaming)&& ((lAudioStatus->a2dpStatePri == a2dp_stream_streaming)&& (sinkCheckAvrcpStateMatch(a2dp_primary, avrcp_play_status_playing))) ) { PTY_DEBUG(("PTY: suspend a2dp pri audio until sec track finished \n")); SuspendA2dpStream(a2dp_primary); /* set paused flag */ theSink.rundata->partymode_pause.audio_source_primary_paused = TRUE; } /* check if currently routed primary source is still valid, disconnect if not */ else if((lAudioStatus->a2dpSinkPri == lAudioStatus->audio_routed)&&(lAudioStatus->a2dpStatePri != a2dp_stream_streaming)) { /* check if other source is present and paused */ a2dp_link_priority link = sinkPartyModeCheckForOtherPausedSource(a2dp_primary); PTY_DEBUG(("PTY: pri source not valid, disconnect\n")); /* disconnect a2dp primary audio */ audioDisconnectActiveSink(); /* disconnect primary audio device */ if(deviceManagerNumConnectedDevs() > 1) sinkPartyModeDisconnectDevice(a2dp_primary); /* check for other paused audio source to route */ if((link != a2dp_invalid)&&(theSink.rundata->partymode_pause.audio_source_secondary_paused)) { PTY_DEBUG(("PTY: resume paused sec\n")); /* resume paused device */ ResumeA2dpStream(a2dp_secondary, lAudioStatus->a2dpStateSec, lAudioStatus->a2dpSinkSec); } return TRUE; } /* check if currently routed secondary source is still valid, disconnect if not */ else if((lAudioStatus->a2dpSinkSec == lAudioStatus->audio_routed)&&(lAudioStatus->a2dpStateSec != a2dp_stream_streaming)) { /* check if other source is present and paused */ a2dp_link_priority link = sinkPartyModeCheckForOtherPausedSource(a2dp_secondary); PTY_DEBUG(("PTY: sec source not valid, disconnect\n")); /* disconnect a2dp secondary audio */ audioDisconnectActiveSink(); /* disconnect secondary audio device */ if(deviceManagerNumConnectedDevs() > 1) sinkPartyModeDisconnectDevice(a2dp_secondary); /* check for other paused audio source to route */ if((link != a2dp_invalid)&&(theSink.rundata->partymode_pause.audio_source_primary_paused)) { PTY_DEBUG(("PTY: resume paused pri\n")); /* resume paused device */ ResumeA2dpStream(a2dp_primary, lAudioStatus->a2dpStatePri, lAudioStatus->a2dpSinkPri); } return TRUE; } /* take no action at this time */ else { PTY_DEBUG(("PTY: avrcp - no action - AR[%x] Pri[%x] Sec[%x]\n",(uint16)lAudioStatus->audio_routed,lAudioStatus->a2dpStatePri,lAudioStatus->a2dpStateSec)); return TRUE; } break; /* not a valid configuration of operating mode */ default: PTY_DEBUG(("PTY: invalid mode - no action\n")); /* default to standard audio routing mode of operation */ return FALSE; break; } return TRUE; } /* no audio routed or party mode not enabled */ else { /* party mode not active or no audio currently routed, take no action and let standard audio routing functions take control */ PTY_DEBUG(("PTY: NOT ACTIVE [%d] or NO AUDIO ROUTED AR[%x] Pri[%x] Sec[%x]\n",(theSink.PartyModeEnabled && theSink.features.PartyMode) ,(uint16)lAudioStatus->audio_routed ,(uint16)lAudioStatus->a2dpSinkPri ,(uint16)lAudioStatus->a2dpSinkSec)); return FALSE; } }