You can use these mechanisms to play audio in Android:
Each mechanism allows for audio playback to be performed in Android. For radio
playback or playback from input devices, these options might not suffice,
although each could be coupled with audio capture or the
MediaRecorder
class to first capture the audio and then play it back from Android. For system
apps in particular, the following information can used to connect an input
device to an output mixer in AAOS.
HwAudioSource player
HwAudioSource
connects the audio source device directly to an Android mixer.
Motivations
Several limitations may arise when using a device-to-device or hardware audio patch with Android. Each option is unable to receive media key events such as PLAY, PAUSE, and STOP and, because they circumvent Android's audio stack, each require hardwares to mix the patch into other audio from Android.
Use HwAudioSource
HwAudioSource
is a new type of player designed as a software patch. This
enables apps that use this player to receive media key events and the output
stream to be mixed and routed by Android.
mHwAudioSource = new HwAudioSource.Builder()
.setAudioDeviceInfo(AudioDeviceInfo: info)
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.build())
.build();
mHwAudioSource.play();
mHwAudioSource.stop();
Changes to the audio HAL
With this new player, consider these expectations for the audio HAL. For
example, device/generic/car/emulator/audio/driver/audio_hw.c
.
adev_create_audio_patch
expects the request to establish an audio patch from a device to a mixer.adev_open_input_stream
expects theaudio_source
to beAUDIO_SOURCE_FM_TUNER
.in_read
fills the audio buffer with broadcast radio audio data.
We recommend you configure a tuner device with type AUDIO_DEVICE_IN_FM_TUNER
in audio_policy_configuration.xml
:
<devicePort
tagName="Tuner_source"
type="AUDIO_DEVICE_IN_FM_TUNER"
role="source"
address="tuner0">
<profile
name=""
format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
</devicePort>
With this device configuration, you can facilitate finding the FM radio input
device by using the AudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS
in
conjunction with AudioDeviceInfo.TYPE_FM_TUNER
.
Create audio patches
You can create an audio patch between two audio ports, either a mix port or a device port. Typically, an audio patch from mix port to device port is for playback while the reverse direction is for capture.
For example, an audio patch that routes audio samples from FM_TUNER
source
directly to the media sink bypasses the software mixer. You must then use a
hardware mixer to mix the audio samples from Android and FM_TUNER
for the
sink. When creating an audio patch directly from FM_TUNER
source to the media
sink:
Volume control applies to the media sink and should affect both the Android and
FM_TUNER
audio.Users can switch between Android and
FM_TUNER
audio through a simple app switch (no explicit media source choice is needed).
Automotive implementations might also need to create an audio patch between two
device ports. To do so, you must first declare the device ports and possible
routes in audio_policy_configuration.xml
and then associate mixports with the
device ports.
Sample configuration
See this sample configuration,
device/generic/car/emulator/audio/audio_policy_configuration.xml
.
<audioPolicyConfiguration>
<modules>
<module name="primary" halVersion="3.0">
<attachedDevices>
<item>bus0_media_out</item>
<item>bus1_audio_patch_test_in</item>
</attachedDevices>
<mixPorts>
<mixPort name="mixport_bus0_media_out" role="source"
flags="AUDIO_OUTPUT_FLAG_PRIMARY">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
<mixPort name="mixport_audio_patch_in" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>
</mixPorts>
<devicePorts>
<devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
address="bus0_media_out">
<profile balance="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
<gains>
<gain name="" mode="AUDIO_GAIN_MODE_JOINT"
minValueMB="-8400" maxValueMB="4000" defaultValueMB="0" stepValueMB="100"/>
</gains>
</devicePort>
<devicePort tagName="bus1_audio_patch_test_in" type="AUDIO_DEVICE_IN_BUS" role="source"
address="bus1_audio_patch_test_in">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
<gains>
<gain name="" mode="AUDIO_GAIN_MODE_JOINT"
minValueMB="-8400" maxValueMB="4000" defaultValueMB="0" stepValueMB="100"/>
</gains>
</devicePort>
</devicePorts>
<routes>
<route type="mix" sink="bus0_media_out" sources="mixport_bus0_media_out,bus1_audio_patch_test_in"/>
<route type="mix" sink="mixport_audio_patch_in" sources="bus1_audio_patch_test_in"/>
</routes>
</module>
</modules>
</audioPolicyConfiguration>
Audio driver API
You can use getExternalSources()
to retrieve a list of available sources
(identified by address), then create audio patches between these sources and the
sink ports by audio usages. The corresponding entry points on the Audio HAL
appear in IDevice.hal
:
Interface IDevice {
...
/
* Creates an audio patch between several source and sink ports. The handle
* is allocated by the HAL and must be unique for this audio HAL module.
*
* @param sources patch sources.
* @param sinks patch sinks.
* @return retval operation completion status.
* @return patch created patch handle.
*/
createAudioPatch(vec<AudioPortConfig> sources, vec<AudioPortConfig> sinks)
generates (Result retval, AudioPatchHandle patch);
* Release an audio patch.
*
* @param patch patch handle.
* @return retval operation completion status.
*/
releaseAudioPatch(AudioPatchHandle patch) generates (Result retval);
...
}
Radio tuner
When building a radio app, we recommend you use the HwAudioSource
as it
handles both creating the patch as well as a media session to handle media key
events. Multiple audio sources can be created for the same source and audio
attributes. It's possible to have one for regular radio usage as well as a
second one for traffic announcements.
If recording the FM_TUNER
, in Android 11 the
permission for doing was changed to android.permission.CAPTURE_AUDIO_OUTPUT
.
It no longer goes through the OP_RECORD_AUDIO
permission check, which applies
to microphones only. This shouldn't impact apps since FM_TUNER
already
requires SYSTEM_API
permission for access.
See Implement radio for details about building a radio app.