Audio System调节音量分析

本文详细解析了Android系统中音量调节的实现过程,包括调整音量、设置音量、音量变化UI更新及发送音量变更广播等内容。文章深入剖析了从AudioManager到AudioService直至底层AudioPolicyService的调用流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.调节音量:
先从上层往下一步步的走吧,和大家分享一下刨根问底的乐趣。
在应用中先实例化一个AudioManager:
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
am对象就可以调用关于音量调节的方法:这些方法的定义都在audiomanager.java中。
在audiomanager.java文件中的方法调用audioservice.java的方法:

  1. public void adjustStreamVolume(int streamType, int direction, int flags) 方法体

public void adjustStreamVolume(int streamType, int direction, int flags) {
IAudioService service = getService();
try {
service.adjustStreamVolume(streamType, direction, flags);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustStreamVolume", e);
}
}
调用IAudioService service = getService();获得audio服务,下面是服务接口:
service.adjustStreamVolume(streamType, direction, flags);他们在IAudioService.java中定义。
在audioservice.java中有该方法的实现。
public void adjustStreamVolume(int streamType, int direction, int flags) {
ensureValidDirection(direction);
ensureValidStreamType(streamType);
VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
boolean adjustVolume = true;
// If either the client forces allowing ringer modes for this adjustment,
// or the stream type is one that is affected by ringer modes
if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0

streamType == AudioSystem.STREAM_RING) {
// Check if the ringer mode changes with this volume adjustment. If
// it does, it will handle adjusting the volume, so we won't below
adjustVolume = checkForRingerModeChange(oldIndex, direction);
}
// If stream is muted, adjust last audible index only
int index;
if (streamState.muteCount() != 0) {
index = streamState.mLastAudibleIndex;
} else {
if (adjustVolume && streamState.adjustIndex(direction)) {
// Post message to set system volume (it in turn will post a message
// to persist). Do not change volume if stream is muted sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,streamState, 0);
}
index = streamState.mIndex;
}
// UI
mVolumePanel.postVolumeChanged(streamType, flags);
// Broadcast Intent
sendVolumeUpdate(streamType, oldIndex, index);
}
在这个函数中:

  1. checkForRingerModeChange(oldIndex, direction);

oldIndex是以前的音量,direction表示增加还是降低音量。这个函数用于检测改变铃声模式,如振动,静音等,是通过调用setRingerMode函数来实现的。而setRingerMode通过发送广播(调用broadcastRingerMode函数)来通知模式的改变。

  1. sendMsg(): 发送消息,处理这个消息的是handleMessage()函数,调用setStreamVolumeIndex来调节音量,

private void setStreamVolumeIndex(int stream, int index) {
AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
}
现在又跑到audiosystem.java中。调用audiosystem.java中的setStreamVolumeIndex
public static native int setStreamVolumeIndex(int stream, int index);通过JNI调用再看看audiosystem.cpp。在audiosystem.cpp中的setStreamVolumeIndex如下:
status_t AudioSystem::setStreamVolumeIndex(stream_type stream, int index)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->setStreamVolumeIndex(stream, index);
}
其实,最终是通过AudioPolicyService的对应函数实现的。再看看AudioPolicyService.cpp文件中的setStreamVolumeIndex。
status_t AudioPolicyService::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
{
if (mpPolicyManager == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
return BAD_VALUE;
}
return mpPolicyManager->setStreamVolumeIndex(stream, index);
}
mpPolicyManager->setStreamVolumeIndex(stream, index);
是在AudioPolicyInterface.h中声明,在AudioPolicyManagerBase.cpp中从新定义的成员函数。
#include <hardware_legacy/AudioPolicyInterface.h>有此为证!
status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
return BAD_VALUE;
}
// Force max volume if stream cannot be muted
if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax;
LOGD("setStreamVolumeIndex() stream %d, index %d", stream, index);
mStreams[stream].mIndexCur = index;
// compute and apply stream volume on all outputs according to connected device
status_t status = NO_ERROR;
for (size_t i = 0; i < mOutputs.size(); i++) {
status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(info), mOutputs.valueAt(info)->device());
if (volStatus != NO_ERROR) {
status = volStatus;
}
}
return status;
}
在AudioPolicyManagerBase.cpp中找到checkAndSetVolume。
status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs, bool force)
{
// do not change actual stream volume if the stream is muted
if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
LOGV("checkAndSetVolume() stream %d muted count %d", stream, mOutputs.valueFor(output)->mMuteCount[stream]);
return NO_ERROR;
}
// do not change in call volume if bluetooth is connected and vice versa
if((stream==AudioSystem::VOICE_CALL&&mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||(stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) {
LOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
stream, mForceUse[AudioSystem::FOR_COMMUNICATION]);
return INVALID_OPERATION;
}
float volume = computeVolume(stream, index, output, device);
// We actually change the volume if:
// - the float value returned by computeVolume() changed
// - the force flag is set
if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
(stream == AudioSystem::VOICE_CALL) || force) {
mOutputs.valueFor(output)->mCurVolume[stream] = volume;
LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
if (stream == AudioSystem::VOICE_CALL ||
stream == AudioSystem::DTMF ||
stream == AudioSystem::BLUETOOTH_SCO) {
// offset value to reflect actual hardware volume that never reaches 0
// 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
volume = 0.01 + 0.99 * volume;
}
mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
}
if (stream == AudioSystem::VOICE_CALL ||
stream == AudioSystem::BLUETOOTH_SCO) {
float voiceVolume;
// Force voice volume to max for bluetooth SCO as volume is managed by the headset
if (stream == AudioSystem::VOICE_CALL) {
voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
} else {
voiceVolume = 1.0;
}
if (voiceVolume != mLastVoiceVolume && output == mHardwareOutput) {
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
mLastVoiceVolume = voiceVolume;
}
}
return NO_ERROR;
}
mpClientInterface对象不在该文件中声明,但是它由构造函数AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)的参数传递过来。现在找一下AudioPolicyManagerBase在哪里被实例化,经查找在AudioPolicyService::AudioPolicyService()
: BnAudioPolicyService() , mpPolicyManager(NULL)
{
char value[PROPERTY_VALUE_MAX];
// start tone playback thread
mTonePlaybackThread = new AudioCommandThread(String8(""));
// start audio commands thread
mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread"));
#if defined (GENERIC_AUDIO) || defined (AUDIO_POLICY_TEST)
mpPolicyManager = new AudioPolicyManagerBase(this);
LOGV("build for GENERIC_AUDIO - using generic audio policy");
#else
// if running in emulation - use the emulator driver
if (property_get("ro.kernel.qemu", value, 0)) {
LOGV("Running in emulation - using generic audio policy");
mpPolicyManager = new AudioPolicyManagerBase(this);
}
else {
LOGV("Using hardware specific audio policy");
mpPolicyManager = createAudioPolicyManager(this);
}
#endif
// load properties
property_get("ro.camera.sound.forced", value, "0");
mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value);
}中被实例化。
在AudioPolicyService类继承了public BnAudioPolicyService, public AudioPolicyClientInterface,
public IBinder::DeathRecipient三个类,在AudioPolicyService中找到函数setStreamVolume和setVoiceVolume
status_t AudioPolicyService::setStreamVolume(AudioSystem::stream_type stream,
float volume,
audio_io_handle_t output,
int delayMs)
{
return mAudioCommandThread->volumeCommand((int)stream, volume, (int)output, delayMs);
}
status_t AudioPolicyService::setVoiceVolume(float volume, int delayMs)
{
return mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
}
他们都是调用mAudioCommandThread类中的函数,实现音量的改变。
status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume, int delayMs)
{
status_t status = NO_ERROR;
AudioCommand *command = new AudioCommand();
command->mCommand = SET_VOICE_VOLUME;
VoiceVolumeData *data = new VoiceVolumeData();
data->mVolume = volume;
command->mParam = data;
if (delayMs == 0) {
command->mWaitStatus = true;
} else {
command->mWaitStatus = false;
}
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);
LOGV("AudioCommandThread() adding set voice volume volume %f", volume);
mWaitWorkCV.signal();
if (command->mWaitStatus) {
command->mCond.wait(mLock);
status = command->mStatus;
mWaitWorkCV.signal();
}
return status;
}

status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream,
float volume,
int output,
int delayMs)
{
status_t status = NO_ERROR;
AudioCommand *command = new AudioCommand();
command->mCommand = SET_VOLUME;
VolumeData *data = new VolumeData();
data->mStream = stream;
data->mVolume = volume;
data->mIO = output;
command->mParam = data;
if (delayMs == 0) {
command->mWaitStatus = true;
} else {
command->mWaitStatus = false;
}
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);
LOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d",
stream, volume, output);
mWaitWorkCV.signal();
if (command->mWaitStatus) {
command->mCond.wait(mLock);
status = command->mStatus;
mWaitWorkCV.signal();
}
return status;
}
以上两个函数中都是自动加锁后,开始调节音量。
Mutex::Autolock _l(mLock);
insertCommand_l(command, delayMs);函数定义如下:
// insertCommand_l() must be called with mLock held
void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
{
ssize_t i;
Vector <AudioCommand *> removedCommands;
command->mTime = systemTime() + milliseconds(delayMs);
// acquire wake lock to make sure delayed commands are processed
if (mName != "" && mAudioCommands.isEmpty()) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string());
}
// check same pending commands with later time stamps and eliminate them
for (i = mAudioCommands.size()1; i >= 0; i-) {
AudioCommand *command2 = mAudioCommands[i];
// commands are sorted by increasing time stamp: no need to scan the rest of mAudioCommands
if (command2->mTime <= command->mTime) break;
if (command2->mCommand != command->mCommand) continue;
switch (command->mCommand) {
case SET_PARAMETERS: {
ParametersData *data = (ParametersData *)command->mParam;
ParametersData *data2 = (ParametersData *)command2->mParam;
if (data->mIO != data2->mIO) break;
LOGV("Comparing parameter command %s to new command %s",
data2->mKeyValuePairs.string(), data->mKeyValuePairs.string());
AudioParameter param = AudioParameter(data->mKeyValuePairs);
AudioParameter param2 = AudioParameter(data2->mKeyValuePairs);
for (size_t j = 0; j < param.size(); j++) {
String8 key;
String8 value;
param.getAt(j, key, value);
for (size_t k = 0; k < param2.size(); k++) {
String8 key2;
String8 value2;
param2.getAt(k, key2, value2);
if (key2 == key) {
param2.remove(key2);
LOGV("Filtering out parameter %s", key2.string());
break;
}
}
}
// if all keys have been filtered out, remove the command.
// otherwise, update the key value pairs
if (param2.size() == 0) {
removedCommands.add(command2);
} else {
data2->mKeyValuePairs = param2.toString();
}
} break;
case SET_VOLUME: {
VolumeData *data = (VolumeData *)command->mParam;
VolumeData *data2 = (VolumeData *)command2->mParam;
if (data->mIO != data2->mIO) break;
if (data->mStream != data2->mStream) break;
LOGV("Filtering out volume command on output %d for stream %d",
data->mIO, data->mStream);
removedCommands.add(command2);
} break;
case SET_FM_VOLUME: {
removedCommands.add(command2);
} break;
case START_TONE:
case STOP_TONE:
default:
break;
}
}
// remove filtered commands
for (size_t j = 0; j < removedCommands.size(); j++) {
// removed commands always have time stamps greater than current command
for (size_t k = i + 1; k < mAudioCommands.size(); k++) {
if (mAudioCommands[k] == removedCommands[j]) {
LOGV("suppressing command: %d", mAudioCommands[k]->mCommand);
mAudioCommands.removeAt(k);
break;
}
}
}
removedCommands.clear();
// insert command at the right place according to its time stamp
LOGV("inserting command: %d at index %d, num commands %d",
command->mCommand, (int)i+1, mAudioCommands.size());
mAudioCommands.insertAt(command, i + 1);
}
3. mVolumePanel.postVolumeChanged(streamType, flags); //更新音量改变UI.
4. sendVolumeUpdate:发送AudioManager.VOLUME_CHANGED_ACTION,处理intent的目的是用于播放那个短促的蜂鸣声(见ToneGenerator.java的startTone)。
5. ToneGenerator.java的startTone的分析: 通过jni调用到ToneGenerator.cpp的startTone函数。

  1. public void adjustVolume(int direction, int flags) 方法体

public void adjustVolume(int direction, int flags) {
IAudioService service = getService();
try {
service.adjustVolume(direction, flags);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustVolume", e);
}
}
调用IAudioService service = getService();
service.adjustVolume(direction, flags);他们在IAudioService.java中定义。在audioservice.java中有该方法的实现。查找方法同上

  1. public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags)

方法体
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
IAudioService service = getService();
try {
service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustVolume", e);
}
}
调用IAudioService service = getService();
service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags);
他们在IAudioService.java文件中定义。在audioservice.java中有该方法的实现。
查找方法同上
4.public void setStreamVolume(int streamType, int index, int flags) 方法体
public void setStreamVolume(int streamType, int index, int flags) {
IAudioService service = getService();
try {
service.setStreamVolume(streamType, index, flags);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setStreamVolume", e);
}
}
调用IAudioService service = getService();
service.setStreamVolume(streamType, index, flags);
他们在IAudioService.java文件中定义。在audioservice.java中有该方法的实现。
现在转到IAudioService.java文件:IAudioService.java文件是接口文件,在audioservice.java文件中实现音量调节函数的定义。
查找方法同上

 

### Java AudioSystem 类的功能和用法 #### 1. AudioSystem 类的定义与功能 `AudioSystem` 是 Java Sound API 的核心类之一,位于 `javax.sound.sampled` 包中。它提供了静态方法来处理音频资源,例如加载音频文件、获取音频输入流、管理音频行(如 `Clip` 和 `SourceDataLine`)等[^1]。通过 `AudioSystem`,开发者可以方便地读取音频文件并将其转换为 `AudioInputStream` 对象,从而进一步操作音频数据。 #### 2. 主要功能 以下是 `AudioSystem` 提供的主要功能及其作用: - **音频文件的读取**: 使用 `getAudioInputStream(File file)` 方法可以从指定的音频文件中获取 `AudioInputStream` 对象[^1]。 ```java File audioFile = new File("example.wav"); AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFile); ``` - **音频格式的转换**: 如果需要将音频数据转换为目标格式,可以使用 `getAudioInputStream(AudioFormat.Encoding targetEncoding, AudioInputStream sourceStream)` 方法进行格式转换。 ```java AudioFormat targetFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, false); AudioInputStream convertedStream = AudioSystem.getAudioInputStream(targetFormat, audioStream); ``` - **音频行的管理**: `AudioSystem` 提供了获取音频行的方法,例如 `getClip()` 和 `getSourceDataLine(AudioFormat format)`。这些方法用于播放或录制音频数据[^1]。 ```java Clip clip = AudioSystem.getClip(); clip.open(audioStream); clip.start(); ``` - **音频设备的信息查询**: 可以通过 `getMixerInfo()` 方法获取系统中可用的音频混音器信息[^1]。 ```java Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo(); for (Mixer.Info info : mixerInfos) { System.out.println(info.getName()); } ``` #### 3. 使用示例 以下是一个完整的示例程序,展示如何使用 `AudioSystem` 播放一个 WAV 格式的音频文件: ```java import javax.sound.sampled.*; import java.io.File; public class AudioPlayerExample { public static void main(String[] args) { try { File audioFile = new File("example.wav"); AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFile); Clip clip = AudioSystem.getClip(); clip.open(audioStream); clip.start(); // 等待用户输入以避免程序立即退出 System.in.read(); clip.stop(); clip.close(); } catch (Exception e) { e.printStackTrace(); } } } ``` #### 4. 异常处理 在使用 `AudioSystem` 时,可能会遇到以下异常: - **UnsupportedAudioFileException**:当尝试加载不支持的音频文件格式时抛出。 - **IOException**:读取音频文件时发生 I/O 错误。 - **LineUnavailableException**:系统资源不足,无法获取音频行。 #### 5. 音频格式的支持 `AudioSystem` 默认支持 WAV 和 AIFF 格式的音频文件。如果需要处理 MP3 文件,则需要引入第三方库,例如 JLayer 或 FFmpeg[^3]。 #### 6. Android 中的 AudioSystem 在 Android 平台中,`AudioSystem` 是一个本地接口,封装了底层的 C++ 实现(如 `AudioSystem.cpp`)。它通过 Binder 机制实现跨进程调用,与 `AudioFlinger` 和 `AudioPolicyService` 等服务交互[^2]。Android 中的 `AudioSystem` 主要用于音频会话管理和音量控制等操作[^4]。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值