diff options
-rw-r--r-- | api/current.xml | 15 | ||||
-rw-r--r-- | core/jni/android_media_ToneGenerator.cpp | 6 | ||||
-rw-r--r-- | include/media/ToneGenerator.h | 3 | ||||
-rw-r--r-- | libs/audioflinger/AudioFlinger.cpp | 118 | ||||
-rw-r--r-- | media/java/android/media/ToneGenerator.java | 15 | ||||
-rw-r--r-- | media/libmedia/ToneGenerator.cpp | 29 |
6 files changed, 120 insertions, 66 deletions
diff --git a/api/current.xml b/api/current.xml index 2d13363..c8384e4 100644 --- a/api/current.xml +++ b/api/current.xml @@ -78336,6 +78336,19 @@ <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" + return="boolean" + abstract="false" native="true" synchronized="false" static="false" @@ -78345,6 +78358,8 @@ > <parameter name="toneType" type="int"> </parameter> +<parameter name="durationMs" type="int"> +</parameter> </method> <method name="stopTone" return="void" diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp index a4388de..07bb1ab 100644 --- a/core/jni/android_media_ToneGenerator.cpp +++ b/core/jni/android_media_ToneGenerator.cpp @@ -38,7 +38,7 @@ struct fields_t { }; 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); ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz, @@ -48,7 +48,7 @@ static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, return false; } - return lpToneGen->startTone(toneType); + return lpToneGen->startTone(toneType, durationMs); } 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[] = { - { "startTone", "(I)Z", (void *)android_media_ToneGenerator_startTone }, + { "startTone", "(II)Z", (void *)android_media_ToneGenerator_startTone }, { "stopTone", "()V", (void *)android_media_ToneGenerator_stopTone }, { "release", "()V", (void *)android_media_ToneGenerator_release }, { "native_setup", "(II)V", (void *)android_media_ToneGenerator_native_setup }, diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h index eafa661..ea6bf5d 100644 --- a/include/media/ToneGenerator.h +++ b/include/media/ToneGenerator.h @@ -154,7 +154,7 @@ public: ToneGenerator(int streamType, float volume); ~ToneGenerator(); - bool startTone(int toneType); + bool startTone(int toneType, int durationMs = -1); void stopTone(); 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 // 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. + unsigned int mMaxSmp; // Maximum number of audio samples played (maximun tone duration) unsigned short mCurSegment; // Current segment index in ToneDescriptor segments[] unsigned short mCurCount; // Current sequence repeat count diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index 8cb89c3..e2b6b51 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -1160,7 +1160,7 @@ AudioFlinger::MixerThread::~MixerThread() bool AudioFlinger::MixerThread::threadLoop() { - unsigned long sleepTime = kBufferRecoveryInUsecs; + unsigned long sleepTime = 0; int16_t* curBuf = mMixBuffer; Vector< sp<Track> > tracksToRemove; size_t enabledTracks = 0; @@ -1215,6 +1215,7 @@ bool AudioFlinger::MixerThread::threadLoop() } standbyTime = systemTime() + kStandbyTimeInNsecs; + sleepTime = 0; continue; } } @@ -1222,14 +1223,31 @@ bool AudioFlinger::MixerThread::threadLoop() enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove); } - if (LIKELY(enabledTracks)) { - // mix buffers... - mAudioMixer->process(curBuf); - // output audio to hardware - if (mSuspended) { - usleep(kMaxBufferRecoveryInUsecs); + // output audio to hardware + if (mSuspended) { + usleep(kMaxBufferRecoveryInUsecs); + } else { + if (LIKELY(enabledTracks)) { + // mix buffers... + mAudioMixer->process(curBuf); + sleepTime = 0; + standbyTime = systemTime() + kStandbyTimeInNsecs; } 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(); mInWrite = true; int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize); @@ -1237,24 +1255,11 @@ bool AudioFlinger::MixerThread::threadLoop() mNumWrites++; mInWrite = false; mStandby = false; - nsecs_t temp = systemTime(); - standbyTime = temp + kStandbyTimeInNsecs; - nsecs_t delta = temp - mLastWriteTime; + nsecs_t delta = systemTime() - mLastWriteTime; if (delta > maxPeriod) { LOGW("write blocked for %llu msecs", ns2ms(delta)); 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() { - unsigned long sleepTime = kBufferRecoveryInUsecs; + unsigned long sleepTime = 0; sp<Track> trackToRemove; sp<Track> activeTrack; nsecs_t standbyTime = systemTime(); @@ -1618,6 +1623,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop() } standbyTime = systemTime() + kStandbyTimeInNsecs; + sleepTime = 0; continue; } } @@ -1710,46 +1716,48 @@ bool AudioFlinger::DirectOutputThread::threadLoop() } } - if (activeTrack != 0) { - AudioBufferProvider::Buffer buffer; - size_t frameCount = mFrameCount; - curBuf = (int8_t *)mMixBuffer; - // output audio to hardware - mLastWriteTime = systemTime(); - mInWrite = true; - while(frameCount) { - buffer.frameCount = frameCount; - activeTrack->getNextBuffer(&buffer); - if (UNLIKELY(buffer.raw == 0)) { - memset(curBuf, 0, frameCount * mFrameSize); - break; + // output audio to hardware + if (mSuspended) { + usleep(kMaxBufferRecoveryInUsecs); + } else { + if (activeTrack != 0) { + AudioBufferProvider::Buffer buffer; + size_t frameCount = mFrameCount; + curBuf = (int8_t *)mMixBuffer; + // output audio to hardware + while(frameCount) { + buffer.frameCount = frameCount; + activeTrack->getNextBuffer(&buffer); + 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); - frameCount -= buffer.frameCount; - curBuf += buffer.frameCount * mFrameSize; - activeTrack->releaseBuffer(&buffer); - } - if (mSuspended) { - usleep(kMaxBufferRecoveryInUsecs); + sleepTime = 0; + standbyTime = systemTime() + kStandbyTimeInNsecs; } 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); if (bytesWritten) mBytesWritten += bytesWritten; mNumWrites++; mInWrite = 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; } } diff --git a/media/java/android/media/ToneGenerator.java b/media/java/android/media/ToneGenerator.java index c60a1ac..d4ae80f 100644 --- a/media/java/android/media/ToneGenerator.java +++ b/media/java/android/media/ToneGenerator.java @@ -744,7 +744,7 @@ public class ToneGenerator * 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, * 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> * <li>{@link #TONE_DTMF_0} * <li>{@link #TONE_DTMF_1} @@ -846,7 +846,18 @@ public class ToneGenerator * </ul> * @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. diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 799c349..4008bfd 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -791,7 +791,6 @@ const unsigned char ToneGenerator::sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONE // generators, instantiates output audio track. // // Input: -// toneType: Type of tone generated (values in enum tone_type) // streamType: Type of stream used for tone playback (enum AudioTrack::stream_type) // volume: volume applied to tone (0.0 to 1.0) // @@ -869,13 +868,16 @@ ToneGenerator::~ToneGenerator() { // Description: Starts tone playback. // // 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: // none // //////////////////////////////////////////////////////////////////////////////// -bool ToneGenerator::startTone(int toneType) { +bool ToneGenerator::startTone(int toneType, int durationMs) { bool lResult = false; if ((toneType < 0) || (toneType >= NUM_TONES)) @@ -896,6 +898,17 @@ bool ToneGenerator::startTone(int toneType) { toneType = getToneForRegion(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 (prepareWave()) { 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 - if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) { + if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0 || + lpToneGen->mTotalSmp > lpToneGen->mMaxSmp) { if (lpToneGen->mState == TONE_PLAYING) { 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) { |