I. Use of ToneGenerator
See com. android. contacts. dialpad. DialpadFragment
ToneGenerator can only play the defined TONE_TYPE in ToneGenerator.
1. Constant Declaration
[Java]/** the length of the Tone sound. Unit: milliseconds */
Private static final int TONE_LENGTH_MS = 150;
/** Main volume ratio: Play Tone at 80% main volume */
Private static final int TONE_RELATIVE_VOLUME = 80;
/** Audio type of the main volume */
Private static final int DIAL_TONE_STREAM_TYPE = AudioManager. STREAM_MUSIC;
/** Length of the Tone sound, in the unit of milliseconds */
Private static final int TONE_LENGTH_MS = 150;
/** Main volume ratio: Play Tone at 80% main volume */
Private static final int TONE_RELATIVE_VOLUME = 80;
/** Audio type of the main volume */
Private static final int DIAL_TONE_STREAM_TYPE = AudioManager. STREAM_MUSIC;
2. Variable Declaration
[Java] // Tone audio player
Private ToneGenerator mToneGenerator;
// Synchronization lock related to Tone
Private Object mToneGeneratorLock = new Object ();
// Sets the Tone sound playback.
Private boolean mDTMFToneEnabled;
// Tone audio player
Private ToneGenerator mToneGenerator;
// Synchronization lock related to Tone
Private Object mToneGeneratorLock = new Object ();
// Sets the Tone sound playback.
Private boolean mDTMFToneEnabled;
3. Tone Initialization
[Java] public void onResume (){
Super. onResume ();
// Read the set value
MDTMFToneEnabled = Settings. System. getInt (getActivity (). getContentResolver (),
Settings. System. DTMF_TONE_WHEN_DIALING, 1) = 1;
// Failure does not matter. It is not important.
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
// 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 ();
// Read the set value
MDTMFToneEnabled = Settings. System. getInt (getActivity (). getContentResolver (),
Settings. System. DTMF_TONE_WHEN_DIALING, 1) = 1;
// Failure does not matter. It is not important.
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
// 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. Release Tone Resources
[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. Play Tone
[Java]/**
* Play the Tone of TONE_LENGTH_MS milliseconds.
* Tone is played only when the Tone mode is selected and not in the mute mode.
* @ Param tone a tone code from {@ linkToneGenerator}
*/
Void playTone (int tone ){
// If the setting is not selected, it will not be played.
If (! MDTMFToneEnabled ){
Return;
}
// Do not play in the mute mode. You need to check every time, because the mute mode can be set without Activity switching.
// The one in the setting is not required, because the Activity must be set first.
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 );
}
}
/**
* Play the Tone of TONE_LENGTH_MS milliseconds.
* Tone is played only when the Tone mode is selected and not in the mute mode.
* @ Param tone a tone code from {@ linkToneGenerator}
*/
Void playTone (int tone ){
// If the setting is not selected, it will not be played.
If (! MDTMFToneEnabled ){
Return;
}
// Do not play in the mute mode. You need to check every time, because the mute mode can be set without Activity switching.
// The one in the setting is not required, because the Activity must be set first.
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 );
}
}
II. Implementation of ToneGenerator
Related code location:
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
Multiple ToneType types are defined and java interfaces are provided.
2. Android_media_ToneGenerator.cpp
Forward requests from the Java layer to the Native layer.
A sentence in android_media_ToneGenerator_native_setup cannot be understood:
[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/20366f;
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/20366f;
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;
}
The calculated value is directly set to AudioTrack, but the volume in AudioTrack should be 0 ~ The percentage of 1.0f is correct. Why do we need such a formula? Is it a Bug. Test it !!
3. ToneGenerator. cpp
Audio data is generated based on the frequency, length, and other information of the defined Tone, and then handed to AudioTrack for playback.
3. mTonePlaybackThread in AudioPolicyService
I thought this thread was dedicated to processing Tone audio devices, but according to the above, it turns out that it was directly using AudioTrack. This is strange, and the AudioSystem does not provide the corresponding interface. This is even more strange. Isn't it intended for outsiders to use it. After another Retrieval, it was found that it was an interface for non-synchronous Tone playing for AudioPolicyManagerBase.
After AudioCommandThread processing, it is still handed over to ToneGenerator for processing.
4. RingtoneManager and Ringtone
The category of the playing ringtone. It is not specific. It is played through MediaPlayer.