一、ToneGenerator的使用
參照com.android.contacts.dialpad.DialpadFragment
ToneGenerator只能播放在ToneGenerator中定義好的TONE_TYPE。
1、常量申明
[java] /** 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音的長度,單位: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;
2、變數申明
[java] // Tone音播放器
private ToneGenerator mToneGenerator;
// Tone相關的同步鎖
private Object mToneGeneratorLock = new Object();
// 設定中的Tone音播放設定
private boolean mDTMFToneEnabled;
// Tone音播放器
private ToneGenerator mToneGenerator;
// Tone相關的同步鎖
private Object mToneGeneratorLock = new Object();
// 設定中的Tone音播放設定
private boolean mDTMFToneEnabled;
3、Tone的初始化
[java] 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 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;
}
}
}
}
4、釋放Tone資源
[java] public voidonPause() {
super.onPause();
synchronized (mToneGeneratorLock) {
if (mToneGenerator != null) {
mToneGenerator.release();
mToneGenerator = null;
}
}
}
public voidonPause() {
super.onPause();
synchronized (mToneGeneratorLock) {
if (mToneGenerator != null) {
mToneGenerator.release();
mToneGenerator = null;
}
}
}
5、播放Tone音
[java] /**
* 播放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);
}
}
/**
* 播放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的實現
相關代碼位置:
ToneGenerator.java:ICS/frameworks/base/media/java/
Android_media_ToneGenerator.cpp:ICS/frameworks/base/core/jni/
ToneGenerator.cpp:ICS/frameworks/base/media/libmedia/
1、ToneGenerator.java
定義了多種ToneType,提供了java的介面
2、Android_media_ToneGenerator.cpp
將Java層的請求轉寄給Native層。
android_media_ToneGenerator_native_setup中的有句話看不懂:
[java] ToneGenerator *lpToneGen= new ToneGenerator(streamType, <STRONG>AudioSystem::linearToLog(volume)</STRONG>,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;
}
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。應該測試一下!!
3、ToneGenerator.cpp
根據定義的Tone因的頻率,長度等資訊產生音頻資料,最後交給AudioTrack播放。
三、AudioPolicyService中的mTonePlaybackThread
本來以為這個線程是專門處理Tone音裝置的,可是根據上面一看原來是直接走AudioTrack的。這就奇怪了,並且AudioSystem中也沒有提供對應的介面,這就更奇怪了,難道它沒準備讓外面的人用。再一檢索,發現原來它是提供給AudioPolicyManagerBase使用的一個非同期播放Tone音的介面。
經過AudioCommandThread的處理,最終還是交給ToneGenerator來處理。
四、RingtoneManager與Ringtone
播放鈴聲的類,沒具體看,最後是通過MediaPlayer來播放的。