Android 音频源码分析——AndroidRecord录音(一)
Android 音频源码分析——AndroidRecord录音(二)
Android 音频源码分析——AndroidRecord音频数据传输流程
Android 音频源码分析——audioserver启动
Android 音频源码分析——AudioFlinger
Android 音频源码分析——AudioTrack设备选择
基于Andorid9.0源码
以AudioTrack为例,梳理下输出设备选择流程。
音频设备选择的影响因素:
直接从AudioTrack.cpp 开始。
AudioTrack构造器
》AudioTrack::set(){}
》AudioTrack::createTrack_l()
》audioFlinger->createTrack(input, output, &status);
》AudioFlinger::createTrack()
》AudioSystem::getOutputForAttr()
》AudioPolicyService::getOutputForAttr()
》AudioPolicyManager::getOutputForAttr()
status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
{
//获取stream type
*stream = streamTypefromAttributesInt(&attributes)
routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes)
audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
*output = getOutputForDevice(device, session, *stream, config, flags);
}
通过flags和usage映射到 stream type
audio_stream_type_t AudioPolicyManager::streamTypefromAttributesInt(const audio_attributes_t *attr)
{
// flags to stream type mapping
if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) {
return AUDIO_STREAM_ENFORCED_AUDIBLE;
}
if ((attr->flags & AUDIO_FLAG_SCO) == AUDIO_FLAG_SCO) {
return AUDIO_STREAM_BLUETOOTH_SCO;
}
if ((attr->flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) {
return AUDIO_STREAM_TTS;
}
// usage to stream type mapping
switch (attr->usage) {
case AUDIO_USAGE_MEDIA:
case AUDIO_USAGE_GAME:
case AUDIO_USAGE_ASSISTANT:
case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
return AUDIO_STREAM_MUSIC;
case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY:
return AUDIO_STREAM_ACCESSIBILITY;
case AUDIO_USAGE_ASSISTANCE_SONIFICATION:
return AUDIO_STREAM_SYSTEM;
case AUDIO_USAGE_VOICE_COMMUNICATION:
return AUDIO_STREAM_VOICE_CALL;
case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
return AUDIO_STREAM_DTMF;
case AUDIO_USAGE_ALARM:
return AUDIO_STREAM_ALARM;
case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
return AUDIO_STREAM_RING;
case AUDIO_USAGE_NOTIFICATION:
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
case AUDIO_USAGE_NOTIFICATION_EVENT:
return AUDIO_STREAM_NOTIFICATION;
case AUDIO_USAGE_UNKNOWN:
default:
return AUDIO_STREAM_MUSIC;
}
}
getStrategyForAttr会调到Engine的getStrategyForUsage函数
routing_strategy Engine::getStrategyForUsage(audio_usage_t usage)
{
// usage to strategy mapping
switch (usage) {
case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY:
return STRATEGY_ACCESSIBILITY;
case AUDIO_USAGE_MEDIA:
case AUDIO_USAGE_GAME:
case AUDIO_USAGE_ASSISTANT:
case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
case AUDIO_USAGE_ASSISTANCE_SONIFICATION:
return STRATEGY_MEDIA;
case AUDIO_USAGE_VOICE_COMMUNICATION:
return STRATEGY_PHONE;
case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
return STRATEGY_DTMF;
case AUDIO_USAGE_ALARM:
case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
return STRATEGY_SONIFICATION;
case AUDIO_USAGE_NOTIFICATION:
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
case AUDIO_USAGE_NOTIFICATION_EVENT:
return STRATEGY_SONIFICATION_RESPECTFUL;
case AUDIO_USAGE_UNKNOWN:
default:
return STRATEGY_MEDIA;
}
}
AudioAttribute usage属性映射到strategy。
audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
bool fromCache)
{
// Check if an explicit routing request exists for a stream type corresponding to the
// specified strategy and use it in priority over default routing rules.
for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
if (getStrategy((audio_stream_type_t)stream) == strategy) {
audio_devices_t forcedDevice =
mOutputRoutes.getActiveDeviceForStream(
(audio_stream_type_t)stream, mAvailableOutputDevices);
if (forcedDevice != AUDIO_DEVICE_NONE) {
return forcedDevice;
}
}
}
if (fromCache) {
ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x",
strategy, mDeviceForStrategy[strategy]);
return mDeviceForStrategy[strategy];
}
return mEngine->getDeviceForStrategy(strategy);
}
Engine有两种:
frameworks/av/services/audioplicy/enginedefault/src/Engine.cpp 对应使用 audio_policy.conf
frameworks/av/services/audioplicy/engineconfigurable/src/Engine.cpp 对应使用 audio_policy_configuration.xml
这里以default为例,接着分析Engine中获取strategy对应的默认设备类型,代码较长,主要分析下STRATEGY_MEDIA 策略。
audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const
{
DeviceVector availableOutputDevices = mApmObserver->getAvailableOutputDevices();
DeviceVector availableInputDevices = mApmObserver->getAvailableInputDevices();
const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
return getDeviceForStrategyInt(strategy, availableOutputDevices,
availableInputDevices, outputs, (uint32_t)AUDIO_DEVICE_NONE);
}
audio_devices_t Engine::getDeviceForStrategyInt(routing_strategy strategy,
DeviceVector availableOutputDevices,
DeviceVector availableInputDevices,
const SwAudioOutputCollection &outputs,
uint32_t outputDeviceTypesToIgnore) const
{
uint32_t device = AUDIO_DEVICE_NONE;
uint32_t availableOutputDevicesType =
availableOutputDevices.types() & ~outputDeviceTypesToIgnore;
switch (strategy) {
//省略。。。。。。
case STRATEGY_MEDIA: {
uint32_t device2 = AUDIO_DEVICE_NONE;
if (strategy != STRATEGY_SONIFICATION) {
// no sonification on remote submix (e.g. WFD)
if (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
String8("0")) != 0) {
device2 = availableOutputDevices.types() & AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
}
}
if (isInCall() && (strategy == STRATEGY_MEDIA)) {
device = getDeviceForStrategyInt(
STRATEGY_PHONE, availableOutputDevices, availableInputDevices, outputs,
outputDeviceTypesToIgnore);
break;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_HEARING_AID;
}
if ((device2 == AUDIO_DEVICE_NONE) &&
(mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
outputs.isA2dpSupported()) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
}
}
if ((device2 == AUDIO_DEVICE_NONE) &&
(mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] == AUDIO_POLICY_FORCE_SPEAKER)) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_LINE;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADSET;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_HEADSET;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_ACCESSORY;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_DEVICE;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
}
if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) {
// no sonification on aux digital (e.g. HDMI)
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL;
}
if ((device2 == AUDIO_DEVICE_NONE) &&
(mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK] == AUDIO_POLICY_FORCE_ANALOG_DOCK)) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
}
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER;
}
int device3 = AUDIO_DEVICE_NONE;
if (strategy == STRATEGY_MEDIA) {
// ARC, SPDIF and AUX_LINE can co-exist with others.
device3 = availableOutputDevicesType & AUDIO_DEVICE_OUT_HDMI_ARC;
device3 |= (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPDIF);
device3 |= (availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_LINE);
}
device2 |= device3;
// device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or
// STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise
device |= device2;
// If hdmi system audio mode is on, remove speaker out of output list.
if ((strategy == STRATEGY_MEDIA) &&
(mForceUse[AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO] ==
AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) {
device &= ~AUDIO_DEVICE_OUT_SPEAKER;
}
// for STRATEGY_SONIFICATION:
// if SPEAKER was selected, and SPEAKER_SAFE is available, use SPEAKER_SAFE instead
if ((strategy == STRATEGY_SONIFICATION) &&
(device & AUDIO_DEVICE_OUT_SPEAKER) &&
(availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
device &= ~AUDIO_DEVICE_OUT_SPEAKER;
}
} break;
}
return device;
}
如果是拨打电话状态,并且strategy等于STRATEGY_MEDIA,重新调用getDeviceForStrategyInt,strategy使用STRATEGY_PHONE。
判断可用输出设备,优先判断A2dp相关是否可用;
1. AUDIO_DEVICE_OUT_BLUETOOTH_A2DP
2. AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES(普通蓝牙耳机)
3. AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER(蓝牙音箱)
判断用户是否强制设置AUDIO_POLICY_FORCE_SPEAKER 扬声器;
按优先级排序
AUDIO_DEVICE_OUT_WIRED_HEADPHONE //普通 带线耳机
AUDIO_DEVICE_OUT_LINE
AUDIO_DEVICE_OUT_WIRED_HEADSET //线控耳机
AUDIO_DEVICE_OUT_USB_HEADSET //usb耳机
AUDIO_DEVICE_OUT_USB_ACCESSORY
AUDIO_DEVICE_OUT_USB_DEVICE //usb设备
AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET
AUDIO_DEVICE_OUT_AUX_DIGITAL
AUDIO_DEVICE_OUT_SPEAKER //扬声器
audio_io_handle_t AudioPolicyManager::getOutputForDevice(
audio_devices_t device,
audio_session_t session,
audio_stream_type_t stream,
const audio_config_t *config,
audio_output_flags_t *flags)
{
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
//direct output 相关处理 ......
if ((*flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
*flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT);
}
//........
non_direct_output:
// A request for HW A/V sync cannot fallback to a mixed output because time
// stamps are embedded in audio data
if ((*flags & (AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)) != 0) {
return AUDIO_IO_HANDLE_NONE;
}
// ignoring channel mask due to downmix capability in mixer
// open a non direct output
// for non direct outputs, only PCM is supported
if (audio_is_linear_pcm(config->format)) {
// get which output is suitable for the specified stream. The actual
// routing change will happen when startOutput() will be called
SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs);
// at this stage we should ignore the DIRECT flag as no direct output could be found earlier
*flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_DIRECT);
output = selectOutput(outputs, *flags, config->format);
}
return output;
}
通过flag 进行选择音频通路, Mixer或Direct,通常不设置的话,是Mixer 类通路。
直接看non_direct_output部分
接着看getOutputsForDevice
SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(
audio_devices_t device,
const SwAudioOutputCollection& openOutputs)
{
SortedVector<audio_io_handle_t> outputs;
ALOGVV("getOutputsForDevice() device %04x", device);
for (size_t i = 0; i < openOutputs.size(); i++) {
ALOGVV("output %zu isDuplicated=%d device=%04x",
i, openOutputs.valueAt(i)->isDuplicated(),
openOutputs.valueAt(i)->supportedDevices());
if ((device & openOutputs.valueAt(i)->supportedDevices()) == device) {
ALOGVV("getOutputsForDevice() found output %d", openOutputs.keyAt(i));
outputs.add(openOutputs.keyAt(i));
}
}
return outputs;
}
其中openOutputs是AudioPolicyManager::initialize加载hw module时进行添加的。通过遍历openOutputs,获取其支持对应device的所有audio_io_handle_t,并返回。
接着看selectOutput
audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_handle_t>& outputs,
audio_output_flags_t flags,
audio_format_t format)
{
// select one output among several that provide a path to a particular device or set of
// devices (the list was previously build by getOutputsForDevice()).
// The priority is as follows:
// 1: the output with the highest number of requested policy flags
// 2: the output with the bit depth the closest to the requested one
// 3: the primary output
// 4: the first output in the list
if (outputs.size() == 0) {
return AUDIO_IO_HANDLE_NONE;
}
if (outputs.size() == 1) {
return outputs[0];
}
int maxCommonFlags = 0;
audio_io_handle_t outputForFlags = AUDIO_IO_HANDLE_NONE;
audio_io_handle_t outputForPrimary = AUDIO_IO_HANDLE_NONE;
audio_io_handle_t outputForFormat = AUDIO_IO_HANDLE_NONE;
audio_format_t bestFormat = AUDIO_FORMAT_INVALID;
audio_format_t bestFormatForFlags = AUDIO_FORMAT_INVALID;
for (audio_io_handle_t output : outputs) {
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
if (!outputDesc->isDuplicated()) {
// if a valid format is specified, skip output if not compatible
if (format != AUDIO_FORMAT_INVALID) {
if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
if (format != outputDesc->mFormat) {
continue;
}
} else if (!audio_is_linear_pcm(format)) {
continue;
}
if (AudioPort::isBetterFormatMatch(
outputDesc->mFormat, bestFormat, format)) {
outputForFormat = output;
bestFormat = outputDesc->mFormat;
}
}
int commonFlags = popcount(outputDesc->mProfile->getFlags() & flags);
if (commonFlags >= maxCommonFlags) {
if (commonFlags == maxCommonFlags) {
if (format != AUDIO_FORMAT_INVALID
&& AudioPort::isBetterFormatMatch(
outputDesc->mFormat, bestFormatForFlags, format)) {
outputForFlags = output;
bestFormatForFlags = outputDesc->mFormat;
}
} else {
outputForFlags = output;
maxCommonFlags = commonFlags;
bestFormatForFlags = outputDesc->mFormat;
}
ALOGV("selectOutput() commonFlags for output %d, %04x", output, commonFlags);
}
if (outputDesc->mProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
outputForPrimary = output;
}
}
}
if (outputForFlags != AUDIO_IO_HANDLE_NONE) {
return outputForFlags;
}
if (outputForFormat != AUDIO_IO_HANDLE_NONE) {
return outputForFormat;
}
if (outputForPrimary != AUDIO_IO_HANDLE_NONE) {
return outputForPrimary;
}
return outputs[0];
}
在提供的特定一组输出设备中选择一个输出(该列表先前由getOutputsForDevice()构建)。
优先级如下:
getOutputForAttr 至此就选择好了所需要的设备。
上面有用到mForceUse,强制使用配置,其可以修改某些场景的设备优先级。
// current forced use configuration
AudioSystem::forced_config mForceUse[AudioSystem::NUM_FORCE_USE];
/* device categories used for audio_policy->set_force_use()
* These must match the values in AudioSystem.java
*/
typedef enum {
AUDIO_POLICY_FORCE_NONE,
AUDIO_POLICY_FORCE_SPEAKER,
AUDIO_POLICY_FORCE_HEADPHONES,
AUDIO_POLICY_FORCE_BT_SCO,
AUDIO_POLICY_FORCE_BT_A2DP,
AUDIO_POLICY_FORCE_WIRED_ACCESSORY,
AUDIO_POLICY_FORCE_BT_CAR_DOCK,
AUDIO_POLICY_FORCE_BT_DESK_DOCK,
AUDIO_POLICY_FORCE_ANALOG_DOCK,
AUDIO_POLICY_FORCE_DIGITAL_DOCK,
AUDIO_POLICY_FORCE_NO_BT_A2DP, /* A2DP sink is not preferred to speaker or wired HS */
AUDIO_POLICY_FORCE_SYSTEM_ENFORCED,
AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED,
AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER,
AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS,
AUDIO_POLICY_FORCE_ENCODED_SURROUND_MANUAL,
AUDIO_POLICY_FORCE_CFG_CNT,
AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1,
AUDIO_POLICY_FORCE_DEFAULT = AUDIO_POLICY_FORCE_NONE,
} audio_policy_forced_cfg_t;
/* usages used for audio_policy->set_force_use()
* These must match the values in AudioSystem.java
*/
typedef enum {
AUDIO_POLICY_FORCE_FOR_COMMUNICATION,
AUDIO_POLICY_FORCE_FOR_MEDIA,
AUDIO_POLICY_FORCE_FOR_RECORD,
AUDIO_POLICY_FORCE_FOR_DOCK,
AUDIO_POLICY_FORCE_FOR_SYSTEM,
AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO,
AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND,
AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING,
AUDIO_POLICY_FORCE_USE_CNT,
AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1,
} audio_policy_force_use_t;
AudioSystem.java 中setForceUse可以设置优先级,系统应用通常使用这个设置。
public static native int setForceUse(int usage, int config)
Java层的AudioRecord、AudioTrack、MediaRecorder、MediaPlayer都有提供setPreferredDevice接口,通过此接口可以指定音频设备。
如:AudioTrack.java
/**
* Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
* the output from this AudioTrack.
* @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink.
* If deviceInfo is null, default routing is restored.
* @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
* does not correspond to a valid audio output device.
*/
@Override
public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) {
// Do some validation....
if (deviceInfo != null && !deviceInfo.isSink()) {
return false;
}
int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0;
boolean status = native_setOutputDevice(preferredDeviceId);
if (status == true) {
synchronized (this) {
mPreferredDevice = deviceInfo;
}
}
return status;
}
以AudioTrack为例分析下该接口
》AudioTrack.native_setOutputDevice(preferredDeviceId);
》android_media_AudioTrack_setOutputDevice(JNIEnv *env, jobject thiz, jint device_id)
》status_t AudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
AutoMutex lock(mLock);
if (mSelectedDeviceId != deviceId) {
mSelectedDeviceId = deviceId;
if (mStatus == NO_ERROR) {
android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
mProxy->interrupt();
}
}
return NO_ERROR;
}
之后会重新创建Track
》AudioTrack::restoreTrack_l(const char *from)
》createTrack_l()
AudioPolicyService根据状态、策略决定使用的音频设备。