Android4.0.3中的提示音

一、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來播放的。

 

You May Also Like