参照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音很难听,所以才用软件来生成的。