背景知识

  • 对于音乐类型的音频资源,可以通过MediaPlayer来播放;
  • 对于音调,可以通过ToneGenerator来播放;
  • 对于提示音,可以通过Ringtone来播放;
  • 对于游戏中的音频资源,可以通过SoundPool来播放。

 

ToneGenerator提供了对DTMF音(ITU-T Q.23),以及呼叫监督音(3GPP TS 22.001)、专用音(3GPP TS 31.111)中规定的音频的支持,根据呼叫状态和漫游状态,该文件产生的音频路径为下行音频或者传输给扬声器或耳机。需要说明的是,DTMF音为WAV格式,相关的音频类型定义位于ToneGenerator.h文件中

 

代码分析

1. java上层调用,每次new都会重新创建一个AudioTrack

ToneGenerator.java (frameworks\base\media\java\android\media)
android_media_ToneGenerator.cpp (frameworks\base\core\jni)

比如:

mToneGenerator = new ToneGenerator(AudioManager.STREAM_DTMF, 80);

2. ToneGenerator.cpp (frameworks\av\media\libaudioclient)

//每个按键音的定义,主要是频率,因为这是自动生成的PCM数据
const ToneGenerator::ToneDescriptor ToneGenerator::sToneDescriptors[] = {
{ .segments = { { .duration = ToneGenerator::TONEGEN_INF, .waveFreq = { 1336, 941, 0 }, 0, 0},
{ .duration = 0 , .waveFreq = { 0 }, 0, 0}},
.repeatCnt = ToneGenerator::TONEGEN_INF,
.repeatSegment = 0 }, // TONE_DTMF_0
...
}
 
ToneGenerator::ToneGenerator(audio_stream_type_t streamType, float volume, bool threadCanCallJava) {
//获取对应audio流的采样率
if (AudioSystem::getOutputSamplingRate(&mSamplingRate, streamType) != NO_ERROR) {
 
// Generate tone by chunks of 20 ms to keep cadencing precision
mProcessSize = (mSamplingRate * 20) / 1000;
 
if (initAudioTrack()) {
}
 
bool ToneGenerator::initAudioTrack() { //初始化播放tone的audiotrack
// Open audio track in mono, PCM 16bit, default sampling rate.
mpAudioTrack = new AudioTrack();
attr = AudioSystem::streamTypeToAttributes(streamType); //streamType = STREAM_DTMF
status_t status = mpAudioTrack->set(
AUDIO_STREAM_DEFAULT,
0, // sampleRate
AUDIO_FORMAT_PCM_16_BIT, //一次采样用16bit的数据
AUDIO_CHANNEL_OUT_MONO, //单声道
frameCount, //mProcessSize
AUDIO_OUTPUT_FLAG_FAST,
audioCallback,
this, // user
0, // notificationFrames
0, // sharedBuffer
mThreadCanCallJava,
AUDIO_SESSION_ALLOCATE,
AudioTrack::TRANSFER_CALLBACK,
nullptr,
AUDIO_UID_INVALID,
-1,
&attr);
mpAudioTrack->setVolume(mVolume);
}
 
//播放对应按键音, durationMs = -1
bool ToneGenerator::startTone(tone_type toneType, int durationMs) {
// Get descriptor for requested tone
mpNewToneDesc = &sToneDescriptors[toneType];
 
if (mState == TONE_INIT) {
if (prepareWave()) {
ALOGV("Immediate start, time %d", (unsigned int)(systemTime()/1000000));
lResult = true;
mState = TONE_STARTING;
if (clock_gettime(CLOCK_MONOTONIC, &mStartTime) != 0) {
mStartTime.tv_sec = 0;
}
mLock.unlock();
mpAudioTrack->start(); //开始播放声音,会调用audioCallback回调函数
mLock.lock();
if (mState == TONE_STARTING) {
ALOGV("Wait for start callback");
lStatus = mWaitCbkCond.waitRelative(mLock, seconds(3));//等待声音数据准备好
if (lStatus != NO_ERROR) {
ALOGE("--- Immediate start timed out, status %d", lStatus);
mState = TONE_IDLE;
lResult = false;
}
}
} else {
mState = TONE_IDLE;
}
} else {
 
}
 
bool ToneGenerator::prepareWave() {
// Remove existing wave generators if any
clearWaveGens(); //清除mWaveGens
mpToneDesc = mpNewToneDesc;
mMaxSmp = TONEGEN_INF;//因为mDurationMs == -1,设置播放数据无限大
 
while (mpToneDesc->segments[segmentIdx].duration) {
// Get total number of sine waves: needed to adapt sine wave gain.
unsigned int lNumWaves = numWaves(segmentIdx);//得到有两种频率的声音
unsigned int freqIdx = 0;
unsigned int frequency = mpToneDesc->segments[segmentIdx].waveFreq[freqIdx];
while (frequency) {
// Instantiate a wave generator if ot already done for this frequency
if (mWaveGens.indexOfKey(frequency) == NAME_NOT_FOUND) {
ToneGenerator::WaveGenerator *lpWaveGen =
new ToneGenerator::WaveGenerator(mSamplingRate, //采样率
frequency, //声音频率
TONEGEN_GAIN/lNumWaves); //声音大小
mWaveGens.add(frequency, lpWaveGen);
}
frequency = mpNewToneDesc->segments[segmentIdx].waveFreq[++freqIdx];
}
segmentIdx++;
}
}
 
//只要开始播放,audiotrack会通过该回调函数一直请求数据,一直到stop
void ToneGenerator::audioCallback(int event, void* user, void *info) {
AudioTrack::Buffer *buffer = static_cast<AudioTrack::Buffer *>(info); //获取存放声音的buffer
ToneGenerator *lpToneGen = static_cast<ToneGenerator *>(user);
int16_t *lpOut = buffer->i16;
unsigned int lNumSmp = buffer->size/sizeof(int16_t); //存放声音缓存的大小
const ToneDescriptor *lpToneDesc = lpToneGen->mpToneDesc; //获取按键音的描述参数
 
while (lNumSmp) {
switch (lpToneGen->mState) {
case TONE_PLAYING:
lWaveCmd = WaveGenerator::WAVEGEN_CONT;
break;
case TONE_STARTING:
ALOGV("Starting Cbk");
lWaveCmd = WaveGenerator::WAVEGEN_START;
break;
case TONE_STOPPED:
ALOGV("Stopped Cbk");
goto audioCallback_EndLoop;
}
 
// Exit if tone sequence is over, 用于检验无限播放时的退出
if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0 ||
lpToneGen->mTotalSmp > lpToneGen->mMaxSmp) {
if (lpToneGen->mState == TONE_PLAYING) {
lpToneGen->mState = TONE_STOPPING;
}
if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) {
goto audioCallback_EndLoop;
}
// fade out before stopping if maximum duration reached
lWaveCmd = WaveGenerator::WAVEGEN_STOP;
lpToneGen->mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below
}
 
if (lGenSmp) {
unsigned int lFreqIdx = 0;
uint16_t lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[lFreqIdx];
while (lFrequency != 0) {
WaveGenerator *lpWaveGen = lpToneGen->mWaveGens.valueFor(lFrequency);
lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd); //根据频率生成pcm数据
lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[++lFreqIdx];
}
}
 
audioCallback_EndLoop:
switch (lpToneGen->mState) {
case TONE_STARTING:
ALOGV("Cbk starting track");
lpToneGen->mState = TONE_PLAYING;
lSignal = true;
break;
case TONE_STOPPING:
ALOGV("Cbk Stopping");
lpToneGen->mState = TONE_STOPPED;
// Force loop exit
lNumSmp = 0;
break;
case TONE_STOPPED:
lpToneGen->mState = TONE_INIT;
ALOGV("Cbk Stopped track");
lpToneGen->mpAudioTrack->stop(); //停止播放声音,整个循环从此退出!!!
// Force loop exit
lNumSmp = 0;
buffer->size = 0;
lSignal = true;
break;
}
if (lSignal)
lpToneGen->mWaitCbkCond.broadcast();
}
}
 
void ToneGenerator::stopTone() {
ALOGV("stopTone");
if (mState != TONE_IDLE && mState != TONE_INIT) {
if (mState == TONE_PLAYING || mState == TONE_STARTING || mState == TONE_RESTARTING) {
struct timespec stopTime;
// If the start time is valid, make sure that the number of audio samples produced
// corresponds at least to the time between the start and stop commands.
// This is needed in case of cold start of the output stream.
if ((mStartTime.tv_sec != 0) && (clock_gettime(CLOCK_MONOTONIC, &stopTime) == 0)) {
time_t sec = stopTime.tv_sec - mStartTime.tv_sec;
auto nsec = stopTime.tv_nsec - mStartTime.tv_nsec;
if (nsec < 0) {
--sec;
nsec += 1000000000;
}
 
if ((sec + 1) > ((time_t)(INT_MAX / mSamplingRate))) {
mMaxSmp = sec * mSamplingRate;
} else {
// mSamplingRate is always > 1000
sec = sec * 1000 + nsec / 1000000; // duration in milliseconds
//通过设置最大的声音播放数据大小来控制播放结束
mMaxSmp = (unsigned int)(((int64_t)sec * mSamplingRate) / 1000);
}
ALOGV("stopTone() forcing mMaxSmp to %d, total for far %d", mMaxSmp, mTotalSmp);
} else {
mState = TONE_STOPPING;
}
}
ALOGV("waiting cond");
status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(3));
}

 

小结

1. 按键音的播放是通过startTone, stopTone来控制播放时长的。而不是靠duration延时来控制播放时间的

2. 按键音的采样率(48000hz)是根据播放流的类型(AudioManager.STREAM_DTMF)来决定的。

采样频率一般共分为11025Hz、22050Hz、24000Hz、44100Hz、48000Hz五个等级

  • 11025Hz能达到AM调幅广播的声音品质
  • 22050Hz和24000HZ能达到FM调频广播的声音品质
  • 44100Hz则是理论上的CD音质界限
  • 48000Hz则更加精确一些。ouser a

3. 如何减少自动生成的音频数据