参照com.android.contacts.dialpad.DialpadFragment
ToneGenerator只能播放在ToneGenerator中定义好的TONE_TYPE。
/** Tone音的长度,单位:milliseconds */ private static final int TONE_LENGTH_MS = 150; /** 主音量的比例:以80%的主音量播放Tone音 */ private static final int TONE_RELATIVE_VOLUME = 80; /** 主音量的音频种别 */ private static final int DIAL_TONE_STREAM_TYPE =AudioManager.STREAM_MUSIC;
// Tone音播放器 private ToneGenerator mToneGenerator; // Tone相关的同步锁 private Object mToneGeneratorLock = new Object(); // 设定中的Tone音播放设置 private boolean mDTMFToneEnabled;
public void onResume() { super.onResume(); // 读取设定的值 mDTMFToneEnabled =Settings.System.getInt(getActivity().getContentResolver(), Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1; // 失败了也无所谓,不是啥重要的东西 synchronized (mToneGeneratorLock) { if (mToneGenerator == null) { try { // we want the user to be ableto control the volume of the dial tones // outside of a call, so we usethe stream type that is also mapped to the // volume control keys for thisactivity mToneGenerator = newToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME); getActivity().setVolumeControlStream(DIAL_TONE_STREAM_TYPE); } catch (RuntimeException e) { Log.w(TAG, "Exceptioncaught while creating local tone generator: " + e); mToneGenerator = null; } } } }
public voidonPause() { super.onPause(); synchronized (mToneGeneratorLock) { if (mToneGenerator != null) { mToneGenerator.release(); mToneGenerator = null; } } }
/** * 播放TONE_LENGTH_MS milliseconds的Tone音. * 只有在设定中选择了播放Tone,并且不是静音模式才会播放Tone音。 * @param tone a tone code from {@linkToneGenerator} */ void playTone(int tone) { // 设定中没有选中的话,就不播 if (!mDTMFToneEnabled) { return; } // 静音模式的时候也不播,需要每次都检查,因为没有Activity切换也能设成静音模式 // 设定中的那个就不需要,因为要设定必须要先切入设定Activity才行 AudioManager audioManager = (AudioManager)getActivity().getSystemService(Context.AUDIO_SERVICE); int ringerMode =audioManager.getRingerMode(); if ((ringerMode == AudioManager.RINGER_MODE_SILENT) || (ringerMode ==AudioManager.RINGER_MODE_VIBRATE)) { return; } synchronized (mToneGeneratorLock) { if (mToneGenerator == null) { Log.w(TAG, "playTone:mToneGenerator == null, tone: " + tone); return; } // Start the new tone (will stop anyplaying tone) mToneGenerator.startTone(tone,TONE_LENGTH_MS); } }
相关代码位置:
ToneGenerator.java:ICS/frameworks/base/media/java/
Android_media_ToneGenerator.cpp:ICS/frameworks/base/core/jni/
ToneGenerator.cpp:ICS/frameworks/base/media/libmedia/
定义了多种ToneType,提供了java的接口
将Java层的请求转发给Native层。
android_media_ToneGenerator_native_setup中的有句话看不懂:
ToneGenerator *lpToneGen= new ToneGenerator(streamType, AudioSystem::linearToLog(volume),true); // change this value tochange volume scaling static const float dBPerStep= 0.5f; // shouldn't need totouch these static const floatdBConvert = -dBPerStep * 2.302585093f / 20.0f; static const floatdBConvertInverse = 1.0f / dBConvert; floatAudioSystem::linearToLog(int volume) { // float v = volume ? exp(float(100 -volume) * dBConvert) : 0; // LOGD("linearToLog(%d)=%f",volume, v); // return v; return volume ? exp(float(100 - volume) *dBConvert) : 0; }
算出来的值会直接设到AudioTrack中,可是AudioTrack中的音量应该是个0~1.0f的百分比才对,为啥需要这么个公式呢,难道是Bug。应该测试一下!!
根据定义的Tone因的频率,长度等信息生成音频数据,最后交给AudioTrack播放。
本来以为这个线程是专门处理Tone音设备的,可是根据上面一看原来是直接走AudioTrack的。这就奇怪了,并且AudioSystem中也没有提供对应的接口,这就更奇怪了,难道它没准备让外面的人用。再一检索,发现原来它是提供给AudioPolicyManagerBase使用的一个非同期播放Tone音的接口。
经过AudioCommandThread的处理,最终还是交给ToneGenerator来处理。
播放铃声的类,没具体看,最后是通过MediaPlayer来播放的。
本来还以为会有个单独硬件来处理Tone音的,最后发现竟然是自动生成后,通过AudioTrack来处理的。
据说是因为硬件生成的Tone音很难听,所以才用软件来生成的。