Android中声音处理流程
16lz
2021-01-23
在android\frameworks\base\media\java\android\media\AudioManager.java中
/** * @hide */ public void handleKeyDown(KeyEvent event, int stream) { int keyCode = event.getKeyCode(); Log.d("zmq","AudioManager handleKeyDown keyCode = "+keyCode); switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: /* * Adjust the volume in on key down since it is more * responsive to the user. 在按键按下去的时候处理,给用户响应更快。 */ int flags = FLAG_SHOW_UI | FLAG_VIBRATE; //1|16得到的值为17Log.d("zmq","AudioManager handleKeyDown flags = "+flags);Log.d("zmq","AudioManager handleKeyDown mUseMasterVolume = "+mUseMasterVolume); if (mUseMasterVolume) { //使用主音量,在config中定义,为true adjustMasterVolume( keyCode == KeyEvent.KEYCODE_VOLUME_UP ? ADJUST_RAISE : ADJUST_LOWER, flags); } else { adjustSuggestedStreamVolume( keyCode == KeyEvent.KEYCODE_VOLUME_UP ? ADJUST_RAISE : ADJUST_LOWER, stream, flags); } break; case KeyEvent.KEYCODE_VOLUME_MUTE: if (event.getRepeatCount() == 0) { if (mUseMasterVolume) { setMasterMute(!isMasterMute()); } else { // TODO: Actually handle MUTE. } } break; } } /** * Show a toast containing the current volume. * * @see #adjustStreamVolume(int, int, int) * @see #adjustVolume(int, int) * @see #setStreamVolume(int, int, int) * @see #setRingerMode(int) */ public static final int FLAG_SHOW_UI = 1 << 0; //其值为1 /** * Whether to vibrate if going into the vibrate ringer mode. */ public static final int FLAG_VIBRATE = 1 << 4; //其值为16 private final boolean mUseMasterVolume; //在AudioManager的构造函数中进行初始化 /** * @hide */ public AudioManager(Context context) { mContext = context; mUseMasterVolume = mContext.getResources().getBoolean( com.android.internal.R.bool.config_useMasterVolume); //在config.xml中定义初始值 mUseVolumeKeySounds = mContext.getResources().getBoolean( com.android.internal.R.bool.config_useVolumeKeySounds); }
变量config_useMasterVolume在android\device\mstar\mango\overlay\frameworks\base\core\res\res\values\config.xml中定义
true /** * Adjusts the master volume for the device's audio amplifier. * @param steps The number of volume steps to adjust. A positive * value will raise the volume. * @param flags One or more flags. * @hide */ public void adjustMasterVolume(int steps, int flags) { Log.d("zmq","AudioManager adjustMasterVolume "); IAudioService service = getService(); try { service.adjustMasterVolume(steps, flags, mContext.getOpPackageName()); } catch (RemoteException e) { Log.e(TAG, "Dead object in adjustMasterVolume", e); } }
调用到android\frameworks\base\media\java\android\media\AudioService.java中,真正有实现功能的地方
/** @see AudioManager#adjustMasterVolume(int, int) */ public void adjustMasterVolume(int steps, int flags, String callingPackage) { Log.d("zmq","AudioService adjustMasterVolume"); if (mUseFixedVolume) { return; } ensureValidSteps(steps); //确保steps的绝对值不超过4Log.d("zmq","AudioService adjustMasterVolume AudioSystem.getMasterVolume() = " +AudioSystem.getMasterVolume()); int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME); //需要*100,从AudioSystem中得到的是0.0~1.0Log.d("zmq","AudioService adjustMasterVolume volume1 = "+volume); int delta = 0; //需要增加的音量坡度 int numSteps = Math.abs(steps);Log.d("zmq","AudioService adjustMasterVolume numSteps = "+numSteps); int direction = steps > 0 ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER;Log.d("zmq", "AudioService adjustMasterVolume direction = "+direction); for (int i = 0; i < numSteps; ++i) { delta = findVolumeDelta(direction, volume);Log.d("zmq","AudioService adjustMasterVolume delta = "+delta); volume += delta;Log.d("zmq", "AudioService adjustMasterVolume volume2 = "+volume); } //Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps); setMasterVolume(volume, flags, callingPackage); } private final boolean mUseFixedVolume; mUseFixedVolume = mContext.getResources().getBoolean( com.android.internal.R.bool.config_useFixedVolume);
变量在config_useFixedVolume在android\frameworks\base\core\res\res\values\config.xml中
false private void ensureValidSteps(int steps) { if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) { throw new IllegalArgumentException("Bad volume adjust steps " + steps); } }// Maximum volume adjust steps allowed in a single batch call. private static final int MAX_BATCH_VOLUME_ADJUST_STEPS = 4;
调用到android\frameworks\base\media\java\android\media\AudioSystem.java中,会调用本地层,先不做追踪。 public static native float getMasterVolume();
// Internally master volume is a float in the 0.0 - 1.0 range, // but to support integer based AudioManager API we translate it to 0 - 100 private static final int MAX_MASTER_VOLUME = 100;private int findVolumeDelta(int direction, int volume) {Log.d("zmq"," AudioService findVolumeDelta"); int delta = 0; if (direction == AudioManager.ADJUST_RAISE) { if (volume == MAX_MASTER_VOLUME) { return 0; } // This is the default value if we make it to the end delta = mMasterVolumeRamp[1]; //为1 Log.d("zmq", "AudioService findVolumeDelta delta = "+delta); // If we're raising the volume move down the ramp array until we // find the volume we're above and use that groups delta. for (int i = mMasterVolumeRamp.length - 1; i > 1; i -= 2) { // 没有执行 Log.d("zmq","AudioService findVolumeDelta for "); if (volume >= mMasterVolumeRamp[i - 1]) { delta = mMasterVolumeRamp[i]; break; } } } else if (direction == AudioManager.ADJUST_LOWER){ if (volume == 0) { return 0; } int length = mMasterVolumeRamp.length; Log.d("zmq","AudioService findVolumeDelta length = "+length); // This is the default value if we make it to the end delta = -mMasterVolumeRamp[length - 1];Log.d("zmq","AudioService findVolumeDelta delta = "+delta); // If we're lowering the volume move up the ramp array until we // find the volume we're below and use the group below it's delta for (int i = 2; i < length; i += 2) { //没有执行Log.d("zmq","AudioService findVolumeDelta for "); if (volume <= mMasterVolumeRamp[i]) { delta = -mMasterVolumeRamp[i - 1]; break; } } } return delta; }private final int[] mMasterVolumeRamp; mMasterVolumeRamp = context.getResources().getIntArray( com.android.internal.R.array.config_masterVolumeRamp);
变量config_masterVolumeRamp在android\frameworks\base\core\res\res\values\config.xml中
- 0
- 5
一般可以在android\device\mstar\mango\overlay\frameworks\base\core\res\res\values\config.xml中覆盖 - 0
- 1
public void setMasterVolume(int volume, int flags, String callingPackage) {Log.d("zmq","AudioService setMasterVolume"); if (mUseFixedVolume) { return; } if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } if (volume < 0) { volume = 0; } else if (volume > MAX_MASTER_VOLUME) { volume = MAX_MASTER_VOLUME; } doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags); }
doSetMasterVolume经过了原厂的修改
private void doSetMasterVolume(float volume, int flags) { boolean bSetA2dpVolume = false; if ((isPipOn() == false) && isBluetoothA2dpOn()) { bSetA2dpVolume = true; } else { bSetA2dpVolume = false; } if (isMasterMute() ) { if(bSetA2dpVolume){ AudioSystem.setA2dpMute(false); } else { //disappear mute AudioSystem.setMasterMute(false); // Post a persist master volume msg sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, false ? 1 : 0, 0, null, PERSIST_DELAY); } sendMasterMuteUpdate(false, flags); } int oldVolume = getMasterVolume();AudioSystem.setMasterVolume(volume); int newVolume = getMasterVolume(); if (newVolume != oldVolume) { // Post a persist master volume msg if(bSetA2dpVolume){ sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE, Math.round(volume * (float)1000.0), 1, null, PERSIST_DELAY); } else { sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE, Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY); } }// Send the volume update regardless whether there was a change. sendMasterVolumeUpdate(flags, oldVolume, newVolume); }
最终调用AudioSystem.java中的setMasterVolume。 下面为显示音量UI部分 // UI update and Broadcast Intent private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) { mVolumePanel.postMasterVolumeChanged(flags); Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION); intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume); intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume); sendBroadcastToAll(intent); }
在android\frameworks\base\core\java\android\view\VolumePanel.java中
public void postMasterVolumeChanged(int flags) { postVolumeChanged(STREAM_MASTER, flags); } public void postVolumeChanged(int streamType, int flags) { if (hasMessages(MSG_VOLUME_CHANGED)) return; synchronized (this) { if (mStreamControls == null) { createSliders(); } } removeMessages(MSG_FREE_RESOURCES); obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget(); } private void createSliders() { LayoutInflater inflater = (LayoutInflater) mContext .getSystemService(Context.LAYOUT_INFLATER_SERVICE); mStreamControls = new HashMap(STREAMS.length); Resources res = mContext.getResources(); for (int i = 0; i < STREAMS.length; i++) { //STREAMS.length=8 StreamResources streamRes = STREAMS[i]; int streamType = streamRes.streamType; if (mVoiceCapable && streamRes == StreamResources.NotificationStream) { streamRes = StreamResources.RingerStream; } StreamControl sc = new StreamControl(); sc.streamType = streamType; sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null); sc.group.setTag(sc); sc.icon = (ImageView) sc.group.findViewById(R.id.stream_icon); sc.icon.setTag(sc); sc.icon.setContentDescription(res.getString(streamRes.descRes)); sc.iconRes = streamRes.iconRes; sc.iconMuteRes = streamRes.iconMuteRes; sc.icon.setImageResource(sc.iconRes); sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar); int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO || streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0; sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);sc.seekbarView.setProgress(getStreamVolume(sc.streamType)); sc.seekbarView.setOnSeekBarChangeListener(this); sc.seekbarView.setTag(sc); sc.seekBarValue = (TextView) sc.group.findViewById(R.id.seekbarvalue);sc.seekBarValue.setText(String.valueOf(getStreamVolume(sc.streamType))); mStreamControls.put(streamType, sc); } } public void handleMessage(Message msg) { switch (msg.what) { case MSG_VOLUME_CHANGED: { onVolumeChanged(msg.arg1, msg.arg2); break; } case MSG_MUTE_CHANGED: { onMuteChanged(msg.arg1, msg.arg2); break; } case MSG_FREE_RESOURCES: { onFreeResources(); break; } case MSG_STOP_SOUNDS: { onStopSounds(); break; } case MSG_PLAY_SOUND: { onPlaySound(msg.arg1, msg.arg2); break; } case MSG_VIBRATE: { onVibrate(); break; } case MSG_TIMEOUT: { if (mDialog.isShowing()) { // MStar Android Patch Begin // Don't dismiss mute panel. if ((mActiveStreamType != -1) && (!isMuted(mActiveStreamType))) { mDialog.dismiss(); mActiveStreamType = -1; } // MStar Android Patch End } synchronized (sConfirmSafeVolumeLock) { if (sConfirmSafeVolumeDialog != null) { sConfirmSafeVolumeDialog.dismiss(); } } break; } case MSG_RINGER_MODE_CHANGED: { if (mDialog.isShowing()) { updateStates(); } break; } case MSG_REMOTE_VOLUME_CHANGED: { onRemoteVolumeChanged(msg.arg1, msg.arg2); break; } case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN: onRemoteVolumeUpdateIfShown(); break; case MSG_SLIDER_VISIBILITY_CHANGED: onSliderVisibilityChanged(msg.arg1, msg.arg2); break; case MSG_DISPLAY_SAFE_VOLUME_WARNING: onDisplaySafeVolumeWarning(msg.arg1); break; } }/** * Override this if you have other work to do when the volume changes (for * example, vibrating, playing a sound, etc.). Make sure to call through to * the superclass implementation. */ protected void onVolumeChanged(int streamType, int flags) { if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")"); if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { synchronized (this) { if (mActiveStreamType != streamType) { reorderSliders(streamType); } onShowVolumeChanged(streamType, flags); } } if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) { removeMessages(MSG_PLAY_SOUND); sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY); } if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) { removeMessages(MSG_PLAY_SOUND); removeMessages(MSG_VIBRATE); onStopSounds(); } removeMessages(MSG_FREE_RESOURCES); sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY); resetTimeout(); } private void reorderSliders(int activeStreamType) { mSliderGroup.removeAllViews(); StreamControl active = mStreamControls.get(activeStreamType); if (active == null) { Log.e("VolumePanel", "Missing stream type! - " + activeStreamType); mActiveStreamType = -1; } else { mSliderGroup.addView(active.group); mActiveStreamType = activeStreamType; active.group.setVisibility(View.VISIBLE); updateSlider(active); } addOtherVolumes(); }/** Update the mute and progress state of a slider */ private void updateSlider(StreamControl sc) { sc.seekbarView.setProgress(getStreamVolume(sc.streamType)); final boolean muted = isMuted(sc.streamType); // Force reloading the image resource sc.icon.setImageDrawable(null); sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes); if (((sc.streamType == AudioManager.STREAM_RING) || (sc.streamType == AudioManager.STREAM_NOTIFICATION)) && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) { sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate); } if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) { // never disable touch interactions for remote playback, the muting is not tied to // the state of the phone. sc.seekbarView.setEnabled(true); } else if ((sc.streamType != mAudioManager.getMasterStreamType() && muted) || (sConfirmSafeVolumeDialog != null)) { sc.seekbarView.setEnabled(false); } else { sc.seekbarView.setEnabled(true); } }private void addOtherVolumes() { if (!mShowCombinedVolumes) return; for (int i = 0; i < STREAMS.length; i++) { // Skip the phone specific ones and the active one final int streamType = STREAMS[i].streamType; if (!STREAMS[i].show || streamType == mActiveStreamType) { continue; } StreamControl sc = mStreamControls.get(streamType); mSliderGroup.addView(sc.group); updateSlider(sc); } } protected void onShowVolumeChanged(int streamType, int flags) { int index = getStreamVolume(streamType); mRingIsSilent = false; if (LOGD) { Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType + ", flags: " + flags + "), index: " + index); } // get max volume for progress bar int max = getStreamMaxVolume(streamType); switch (streamType) { case AudioManager.STREAM_RING: {// setRingerIcon(); Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri( mContext, RingtoneManager.TYPE_RINGTONE); if (ringuri == null) { mRingIsSilent = true; } break; } case AudioManager.STREAM_MUSIC: { // Special case for when Bluetooth is active for music if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) & (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) { setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute); } else { setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute); } break; } case AudioManager.STREAM_VOICE_CALL: { /* * For in-call voice call volume, there is no inaudible volume. * Rescale the UI control so the progress bar doesn't go all * the way to zero and don't show the mute icon. */ index++; max++; break; } case AudioManager.STREAM_ALARM: { break; } case AudioManager.STREAM_NOTIFICATION: { Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri( mContext, RingtoneManager.TYPE_NOTIFICATION); if (ringuri == null) { mRingIsSilent = true; } break; } case AudioManager.STREAM_BLUETOOTH_SCO: { /* * For in-call voice call volume, there is no inaudible volume. * Rescale the UI control so the progress bar doesn't go all * the way to zero and don't show the mute icon. */ index++; max++; break; } case AudioService.STREAM_REMOTE_MUSIC: { if (LOGD) { Log.d(TAG, "showing remote volume "+index+" over "+ max); } break; } } StreamControl sc = mStreamControls.get(streamType); if (sc != null) { if (sc.seekbarView.getMax() != max) { sc.seekbarView.setMax(max); } sc.seekbarView.setProgress(index); if (((flags & AudioManager.FLAG_FIXED_VOLUME) != 0) || (streamType != mAudioManager.getMasterStreamType() && streamType != AudioService.STREAM_REMOTE_MUSIC && isMuted(streamType)) || sConfirmSafeVolumeDialog != null) { sc.seekbarView.setEnabled(false); } else { sc.seekbarView.setEnabled(true); } } if (!mDialog.isShowing()) { int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType; // when the stream is for remote playback, use -1 to reset the stream type evaluation mAudioManager.forceVolumeControlStream(stream); mDialog.setContentView(mView); // Showing dialog - use collapsed state if (mShowCombinedVolumes) { collapse(); } mDialog.show(); } // Do a little vibrate if applicable (only when going into vibrate mode) if ((streamType != AudioService.STREAM_REMOTE_MUSIC) && ((flags & AudioManager.FLAG_VIBRATE) != 0) && mAudioService.isStreamAffectedByRingerMode(streamType) && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) { sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY); } } protected void onPlaySound(int streamType, int flags) { if (hasMessages(MSG_STOP_SOUNDS)) { removeMessages(MSG_STOP_SOUNDS); // Force stop right now onStopSounds(); } synchronized (this) { ToneGenerator toneGen = getOrCreateToneGenerator(streamType); if (toneGen != null) { toneGen.startTone(ToneGenerator.TONE_PROP_BEEP); sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION); } } } protected void onStopSounds() { synchronized (this) { int numStreamTypes = AudioSystem.getNumStreamTypes(); for (int i = numStreamTypes - 1; i >= 0; i--) { ToneGenerator toneGen = mToneGenerators[i]; if (toneGen != null) { toneGen.stopTone(); } } } } protected void onFreeResources() { synchronized (this) { for (int i = mToneGenerators.length - 1; i >= 0; i--) { if (mToneGenerators[i] != null) { mToneGenerators[i].release(); } mToneGenerators[i] = null; } } }
framework层调用,不涉及JNI层。 更多相关文章
- Android--自定义SeekBarPreference控件
- [AndroidTips]Android预定义样式
- Android中自定义属性的使用
- 修改官方Twitter For Android,自定义 API