am db603b0b: Merge change 24114 into eclair

Merge commit 'db603b0b2c7c06ea78b8aa0bdc7ea8f08b335c3d' into eclair-plus-aosp

* commit 'db603b0b2c7c06ea78b8aa0bdc7ea8f08b335c3d':
  Fix issue 1992233: DTMF tones on Sholes is really long.
This commit is contained in:
Eric Laurent
2009-09-08 23:07:51 -07:00
committed by Android Git Automerger
6 changed files with 120 additions and 66 deletions

View File

@@ -78333,6 +78333,19 @@
visibility="public" visibility="public"
> >
</method> </method>
<method name="startTone"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="toneType" type="int">
</parameter>
</method>
<method name="startTone" <method name="startTone"
return="boolean" return="boolean"
abstract="false" abstract="false"
@@ -78345,6 +78358,8 @@
> >
<parameter name="toneType" type="int"> <parameter name="toneType" type="int">
</parameter> </parameter>
<parameter name="durationMs" type="int">
</parameter>
</method> </method>
<method name="stopTone" <method name="stopTone"
return="void" return="void"

View File

@@ -38,7 +38,7 @@ struct fields_t {
}; };
static fields_t fields; static fields_t fields;
static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, jint toneType) { static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, jint toneType, jint durationMs) {
LOGV("android_media_ToneGenerator_startTone: %x\n", (int)thiz); LOGV("android_media_ToneGenerator_startTone: %x\n", (int)thiz);
ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz, ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
@@ -48,7 +48,7 @@ static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz,
return false; return false;
} }
return lpToneGen->startTone(toneType); return lpToneGen->startTone(toneType, durationMs);
} }
static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) { static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) {
@@ -120,7 +120,7 @@ static void android_media_ToneGenerator_native_finalize(JNIEnv *env,
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = { static JNINativeMethod gMethods[] = {
{ "startTone", "(I)Z", (void *)android_media_ToneGenerator_startTone }, { "startTone", "(II)Z", (void *)android_media_ToneGenerator_startTone },
{ "stopTone", "()V", (void *)android_media_ToneGenerator_stopTone }, { "stopTone", "()V", (void *)android_media_ToneGenerator_stopTone },
{ "release", "()V", (void *)android_media_ToneGenerator_release }, { "release", "()V", (void *)android_media_ToneGenerator_release },
{ "native_setup", "(II)V", (void *)android_media_ToneGenerator_native_setup }, { "native_setup", "(II)V", (void *)android_media_ToneGenerator_native_setup },

View File

@@ -154,7 +154,7 @@ public:
ToneGenerator(int streamType, float volume); ToneGenerator(int streamType, float volume);
~ToneGenerator(); ~ToneGenerator();
bool startTone(int toneType); bool startTone(int toneType, int durationMs = -1);
void stopTone(); void stopTone();
bool isInited() { return (mState == TONE_IDLE)?false:true;} bool isInited() { return (mState == TONE_IDLE)?false:true;}
@@ -246,6 +246,7 @@ private:
// NOTE: because mTotalSmp, mNextSegSmp are stored on 32 bit, current design will operate properly // NOTE: because mTotalSmp, mNextSegSmp are stored on 32 bit, current design will operate properly
// only if tone duration is less than about 27 Hours(@44100Hz sampling rate). If this time is exceeded, // only if tone duration is less than about 27 Hours(@44100Hz sampling rate). If this time is exceeded,
// no crash will occur but tone sequence will show a glitch. // no crash will occur but tone sequence will show a glitch.
unsigned int mMaxSmp; // Maximum number of audio samples played (maximun tone duration)
unsigned short mCurSegment; // Current segment index in ToneDescriptor segments[] unsigned short mCurSegment; // Current segment index in ToneDescriptor segments[]
unsigned short mCurCount; // Current sequence repeat count unsigned short mCurCount; // Current sequence repeat count

View File

@@ -1160,7 +1160,7 @@ AudioFlinger::MixerThread::~MixerThread()
bool AudioFlinger::MixerThread::threadLoop() bool AudioFlinger::MixerThread::threadLoop()
{ {
unsigned long sleepTime = kBufferRecoveryInUsecs; unsigned long sleepTime = 0;
int16_t* curBuf = mMixBuffer; int16_t* curBuf = mMixBuffer;
Vector< sp<Track> > tracksToRemove; Vector< sp<Track> > tracksToRemove;
size_t enabledTracks = 0; size_t enabledTracks = 0;
@@ -1215,6 +1215,7 @@ bool AudioFlinger::MixerThread::threadLoop()
} }
standbyTime = systemTime() + kStandbyTimeInNsecs; standbyTime = systemTime() + kStandbyTimeInNsecs;
sleepTime = 0;
continue; continue;
} }
} }
@@ -1222,14 +1223,31 @@ bool AudioFlinger::MixerThread::threadLoop()
enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove); enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove);
} }
if (LIKELY(enabledTracks)) {
// mix buffers...
mAudioMixer->process(curBuf);
// output audio to hardware // output audio to hardware
if (mSuspended) { if (mSuspended) {
usleep(kMaxBufferRecoveryInUsecs); usleep(kMaxBufferRecoveryInUsecs);
} else {
if (LIKELY(enabledTracks)) {
// mix buffers...
mAudioMixer->process(curBuf);
sleepTime = 0;
standbyTime = systemTime() + kStandbyTimeInNsecs;
} else { } else {
sleepTime += kBufferRecoveryInUsecs;
// There was nothing to mix this round, which means all
// active tracks were late. Sleep a little bit to give
// them another chance. If we're too late, write 0s to audio
// hardware to avoid underrun.
if (sleepTime < kMaxBufferRecoveryInUsecs) {
usleep(kBufferRecoveryInUsecs);
} else {
memset (curBuf, 0, mixBufferSize);
sleepTime = 0;
}
}
// sleepTime == 0 means PCM data were written to mMixBuffer[]
if (sleepTime == 0) {
mLastWriteTime = systemTime(); mLastWriteTime = systemTime();
mInWrite = true; mInWrite = true;
int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize); int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize);
@@ -1237,24 +1255,11 @@ bool AudioFlinger::MixerThread::threadLoop()
mNumWrites++; mNumWrites++;
mInWrite = false; mInWrite = false;
mStandby = false; mStandby = false;
nsecs_t temp = systemTime(); nsecs_t delta = systemTime() - mLastWriteTime;
standbyTime = temp + kStandbyTimeInNsecs;
nsecs_t delta = temp - mLastWriteTime;
if (delta > maxPeriod) { if (delta > maxPeriod) {
LOGW("write blocked for %llu msecs", ns2ms(delta)); LOGW("write blocked for %llu msecs", ns2ms(delta));
mNumDelayedWrites++; mNumDelayedWrites++;
} }
sleepTime = kBufferRecoveryInUsecs;
}
} else {
// There was nothing to mix this round, which means all
// active tracks were late. Sleep a little bit to give
// them another chance. If we're too late, the audio
// hardware will zero-fill for us.
// LOGV("thread %p no buffers - usleep(%lu)", this, sleepTime);
usleep(sleepTime);
if (sleepTime < kMaxBufferRecoveryInUsecs) {
sleepTime += kBufferRecoveryInUsecs;
} }
} }
@@ -1568,7 +1573,7 @@ AudioFlinger::DirectOutputThread::~DirectOutputThread()
bool AudioFlinger::DirectOutputThread::threadLoop() bool AudioFlinger::DirectOutputThread::threadLoop()
{ {
unsigned long sleepTime = kBufferRecoveryInUsecs; unsigned long sleepTime = 0;
sp<Track> trackToRemove; sp<Track> trackToRemove;
sp<Track> activeTrack; sp<Track> activeTrack;
nsecs_t standbyTime = systemTime(); nsecs_t standbyTime = systemTime();
@@ -1618,6 +1623,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
} }
standbyTime = systemTime() + kStandbyTimeInNsecs; standbyTime = systemTime() + kStandbyTimeInNsecs;
sleepTime = 0;
continue; continue;
} }
} }
@@ -1710,46 +1716,48 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
} }
} }
if (activeTrack != 0) { // output audio to hardware
AudioBufferProvider::Buffer buffer; if (mSuspended) {
size_t frameCount = mFrameCount; usleep(kMaxBufferRecoveryInUsecs);
curBuf = (int8_t *)mMixBuffer; } else {
// output audio to hardware if (activeTrack != 0) {
mLastWriteTime = systemTime(); AudioBufferProvider::Buffer buffer;
mInWrite = true; size_t frameCount = mFrameCount;
while(frameCount) { curBuf = (int8_t *)mMixBuffer;
buffer.frameCount = frameCount; // output audio to hardware
activeTrack->getNextBuffer(&buffer); while(frameCount) {
if (UNLIKELY(buffer.raw == 0)) { buffer.frameCount = frameCount;
memset(curBuf, 0, frameCount * mFrameSize); activeTrack->getNextBuffer(&buffer);
break; if (UNLIKELY(buffer.raw == 0)) {
memset(curBuf, 0, frameCount * mFrameSize);
break;
}
memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
frameCount -= buffer.frameCount;
curBuf += buffer.frameCount * mFrameSize;
activeTrack->releaseBuffer(&buffer);
} }
memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize); sleepTime = 0;
frameCount -= buffer.frameCount; standbyTime = systemTime() + kStandbyTimeInNsecs;
curBuf += buffer.frameCount * mFrameSize;
activeTrack->releaseBuffer(&buffer);
}
if (mSuspended) {
usleep(kMaxBufferRecoveryInUsecs);
} else { } else {
sleepTime += kBufferRecoveryInUsecs;
if (sleepTime < kMaxBufferRecoveryInUsecs) {
usleep(kBufferRecoveryInUsecs);
} else {
memset (mMixBuffer, 0, mFrameCount * mFrameSize);
sleepTime = 0;
}
}
// sleepTime == 0 means PCM data were written to mMixBuffer[]
if (sleepTime == 0) {
mLastWriteTime = systemTime();
mInWrite = true;
int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize); int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
if (bytesWritten) mBytesWritten += bytesWritten; if (bytesWritten) mBytesWritten += bytesWritten;
mNumWrites++; mNumWrites++;
mInWrite = false; mInWrite = false;
mStandby = false; mStandby = false;
nsecs_t temp = systemTime();
standbyTime = temp + kStandbyTimeInNsecs;
sleepTime = kBufferRecoveryInUsecs;
}
} else {
// There was nothing to mix this round, which means all
// active tracks were late. Sleep a little bit to give
// them another chance. If we're too late, the audio
// hardware will zero-fill for us.
//LOGV("no buffers - usleep(%lu)", sleepTime);
usleep(sleepTime);
if (sleepTime < kMaxBufferRecoveryInUsecs) {
sleepTime += kBufferRecoveryInUsecs;
} }
} }

View File

@@ -744,7 +744,7 @@ public class ToneGenerator
* This method starts the playback of a tone of the specified type. * This method starts the playback of a tone of the specified type.
* only one tone can play at a time: if a tone is playing while this method is called, * only one tone can play at a time: if a tone is playing while this method is called,
* this tone is stopped and replaced by the one requested. * this tone is stopped and replaced by the one requested.
* @param toneType The type of tone generate chosen from the following list: * @param toneType The type of tone generated chosen from the following list:
* <ul> * <ul>
* <li>{@link #TONE_DTMF_0} * <li>{@link #TONE_DTMF_0}
* <li>{@link #TONE_DTMF_1} * <li>{@link #TONE_DTMF_1}
@@ -846,7 +846,18 @@ public class ToneGenerator
* </ul> * </ul>
* @see #ToneGenerator(int, int) * @see #ToneGenerator(int, int)
*/ */
public native boolean startTone(int toneType); public boolean startTone(int toneType) {
return startTone(toneType, -1);
}
/**
* This method starts the playback of a tone of the specified type for the specified duration.
* @param toneType The type of tone generated @see #startTone(int).
* @param durationMs The tone duration in milliseconds. If the tone is limited in time by definition,
* the actual duration will be the minimum of durationMs and the defined tone duration. Setting durationMs to -1,
* is equivalent to calling #startTone(int).
*/
public native boolean startTone(int toneType, int durationMs);
/** /**
* This method stops the tone currently playing playback. * This method stops the tone currently playing playback.

View File

@@ -791,7 +791,6 @@ const unsigned char ToneGenerator::sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONE
// generators, instantiates output audio track. // generators, instantiates output audio track.
// //
// Input: // Input:
// toneType: Type of tone generated (values in enum tone_type)
// streamType: Type of stream used for tone playback (enum AudioTrack::stream_type) // streamType: Type of stream used for tone playback (enum AudioTrack::stream_type)
// volume: volume applied to tone (0.0 to 1.0) // volume: volume applied to tone (0.0 to 1.0)
// //
@@ -869,13 +868,16 @@ ToneGenerator::~ToneGenerator() {
// Description: Starts tone playback. // Description: Starts tone playback.
// //
// Input: // Input:
// none // toneType: Type of tone generated (values in enum tone_type)
// durationMs: The tone duration in milliseconds. If the tone is limited in time by definition,
// the actual duration will be the minimum of durationMs and the defined tone duration.
// Ommiting or setting durationMs to -1 does not limit tone duration.
// //
// Output: // Output:
// none // none
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool ToneGenerator::startTone(int toneType) { bool ToneGenerator::startTone(int toneType, int durationMs) {
bool lResult = false; bool lResult = false;
if ((toneType < 0) || (toneType >= NUM_TONES)) if ((toneType < 0) || (toneType >= NUM_TONES))
@@ -896,6 +898,17 @@ bool ToneGenerator::startTone(int toneType) {
toneType = getToneForRegion(toneType); toneType = getToneForRegion(toneType);
mpNewToneDesc = &sToneDescriptors[toneType]; mpNewToneDesc = &sToneDescriptors[toneType];
if (durationMs == -1) {
mMaxSmp = TONEGEN_INF;
} else {
if (durationMs > (int)(TONEGEN_INF / mSamplingRate)) {
mMaxSmp = (durationMs / 1000) * mSamplingRate;
} else {
mMaxSmp = (durationMs * mSamplingRate) / 1000;
}
LOGV("startTone, duration limited to %d ms", durationMs);
}
if (mState == TONE_INIT) { if (mState == TONE_INIT) {
if (prepareWave()) { if (prepareWave()) {
LOGV("Immediate start, time %d\n", (unsigned int)(systemTime()/1000000)); LOGV("Immediate start, time %d\n", (unsigned int)(systemTime()/1000000));
@@ -1102,11 +1115,17 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) {
// Exit if tone sequence is over // Exit if tone sequence is over
if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) { if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0 ||
lpToneGen->mTotalSmp > lpToneGen->mMaxSmp) {
if (lpToneGen->mState == TONE_PLAYING) { if (lpToneGen->mState == TONE_PLAYING) {
lpToneGen->mState = TONE_STOPPING; lpToneGen->mState = TONE_STOPPING;
} }
goto audioCallback_EndLoop; if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) {
goto audioCallback_EndLoop;
}
// fade out before stopping if maximum duraiton reached
lWaveCmd = WaveGenerator::WAVEGEN_STOP;
lpToneGen->mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below
} }
if (lpToneGen->mTotalSmp > lpToneGen->mNextSegSmp) { if (lpToneGen->mTotalSmp > lpToneGen->mNextSegSmp) {