am 62443f5f: Fix issue 2139634: DTMF tones on Sholes popping, hissing (audio latency too high).
Merge commit '62443f5f4517ba17d911975e695f1ab75bfdbf77' into eclair-mr2 * commit '62443f5f4517ba17d911975e695f1ab75bfdbf77': Fix issue 2139634: DTMF tones on Sholes popping, hissing (audio latency too high).
This commit is contained in:
@@ -248,6 +248,7 @@ private:
|
||||
// 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)
|
||||
int mDurationMs; // Maximum tone duration in ms
|
||||
|
||||
unsigned short mCurSegment; // Current segment index in ToneDescriptor segments[]
|
||||
unsigned short mCurCount; // Current sequence repeat count
|
||||
|
||||
@@ -62,8 +62,6 @@ static const char* kDeadlockedString = "AudioFlinger may be deadlocked\n";
|
||||
static const char* kHardwareLockedString = "Hardware lock is taken\n";
|
||||
|
||||
//static const nsecs_t kStandbyTimeInNsecs = seconds(3);
|
||||
static const unsigned long kBufferRecoveryInUsecs = 2000;
|
||||
static const unsigned long kMaxBufferRecoveryInUsecs = 20000;
|
||||
static const float MAX_GAIN = 4096.0f;
|
||||
|
||||
// retry counts for buffer fill timeout
|
||||
@@ -1070,10 +1068,10 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
|
||||
// in both cases "unstop" the track
|
||||
if (track->isPaused()) {
|
||||
track->mState = TrackBase::RESUMING;
|
||||
LOGV("PAUSED => RESUMING (%d)", track->name());
|
||||
LOGV("PAUSED => RESUMING (%d) on thread %p", track->name(), this);
|
||||
} else {
|
||||
track->mState = TrackBase::ACTIVE;
|
||||
LOGV("? => ACTIVE (%d)", track->name());
|
||||
LOGV("? => ACTIVE (%d) on thread %p", track->name(), this);
|
||||
}
|
||||
// set retry count for buffer fill
|
||||
track->mRetryCount = kMaxTrackStartupRetries;
|
||||
@@ -1175,7 +1173,8 @@ AudioFlinger::MixerThread::~MixerThread()
|
||||
|
||||
bool AudioFlinger::MixerThread::threadLoop()
|
||||
{
|
||||
unsigned long sleepTime = 0;
|
||||
uint32_t sleepTime = 0;
|
||||
uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
|
||||
int16_t* curBuf = mMixBuffer;
|
||||
Vector< sp<Track> > tracksToRemove;
|
||||
size_t enabledTracks = 0;
|
||||
@@ -1200,6 +1199,7 @@ bool AudioFlinger::MixerThread::threadLoop()
|
||||
// FIXME: Relaxed timing because of a certain device that can't meet latency
|
||||
// Should be reduced to 2x after the vendor fixes the driver issue
|
||||
maxPeriod = seconds(mFrameCount) / mSampleRate * 3;
|
||||
maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
|
||||
}
|
||||
|
||||
const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
|
||||
@@ -1235,7 +1235,6 @@ bool AudioFlinger::MixerThread::threadLoop()
|
||||
}
|
||||
|
||||
standbyTime = systemTime() + kStandbyTimeInNsecs;
|
||||
sleepTime = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1249,28 +1248,23 @@ bool AudioFlinger::MixerThread::threadLoop()
|
||||
sleepTime = 0;
|
||||
standbyTime = systemTime() + kStandbyTimeInNsecs;
|
||||
} else {
|
||||
sleepTime += kBufferRecoveryInUsecs;
|
||||
if (sleepTime > kMaxBufferRecoveryInUsecs) {
|
||||
sleepTime = kMaxBufferRecoveryInUsecs;
|
||||
}
|
||||
// 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 (mBytesWritten != 0 && sleepTime >= kMaxBufferRecoveryInUsecs) {
|
||||
// If no tracks are ready, sleep once for the duration of an output
|
||||
// buffer size, then write 0s to the output
|
||||
if (sleepTime == 0) {
|
||||
sleepTime = maxBufferRecoveryInUsecs;
|
||||
} else if (mBytesWritten != 0) {
|
||||
memset (curBuf, 0, mixBufferSize);
|
||||
sleepTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSuspended) {
|
||||
sleepTime = kMaxBufferRecoveryInUsecs;
|
||||
sleepTime = maxBufferRecoveryInUsecs;
|
||||
}
|
||||
// sleepTime == 0 means we must write to audio hardware
|
||||
if (sleepTime == 0) {
|
||||
mLastWriteTime = systemTime();
|
||||
mInWrite = true;
|
||||
LOGV("mOutput->write() thread %p frames %d", this, mFrameCount);
|
||||
int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize);
|
||||
if (bytesWritten > 0) mBytesWritten += bytesWritten;
|
||||
mNumWrites++;
|
||||
@@ -1393,7 +1387,7 @@ size_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track>
|
||||
// No buffers for this track. Give it a few chances to
|
||||
// fill a buffer, then remove it from active list.
|
||||
if (--(track->mRetryCount) <= 0) {
|
||||
LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
|
||||
LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
|
||||
tracksToRemove->add(track);
|
||||
}
|
||||
// For tracks using static shared memory buffer, make sure that we have
|
||||
@@ -1583,6 +1577,16 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
uint32_t AudioFlinger::MixerThread::getMaxBufferRecoveryInUsecs()
|
||||
{
|
||||
uint32_t time = ((mFrameCount * 1000) / mSampleRate) * 1000;
|
||||
// Add some margin with regard to scheduling precision
|
||||
if (time > 10000) {
|
||||
time -= 10000;
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output)
|
||||
: PlaybackThread(audioFlinger, output),
|
||||
@@ -1598,7 +1602,8 @@ AudioFlinger::DirectOutputThread::~DirectOutputThread()
|
||||
|
||||
bool AudioFlinger::DirectOutputThread::threadLoop()
|
||||
{
|
||||
unsigned long sleepTime = 0;
|
||||
uint32_t sleepTime = 0;
|
||||
uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
|
||||
sp<Track> trackToRemove;
|
||||
sp<Track> activeTrack;
|
||||
nsecs_t standbyTime = systemTime();
|
||||
@@ -1615,6 +1620,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
|
||||
|
||||
if (checkForNewParameters_l()) {
|
||||
mixBufferSize = mFrameCount*mFrameSize;
|
||||
maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
|
||||
}
|
||||
|
||||
// put audio hardware into standby after short delay
|
||||
@@ -1648,7 +1654,6 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
|
||||
}
|
||||
|
||||
standbyTime = systemTime() + kStandbyTimeInNsecs;
|
||||
sleepTime = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1761,23 +1766,16 @@ bool AudioFlinger::DirectOutputThread::threadLoop()
|
||||
sleepTime = 0;
|
||||
standbyTime = systemTime() + kStandbyTimeInNsecs;
|
||||
} else {
|
||||
sleepTime += kBufferRecoveryInUsecs;
|
||||
if (sleepTime > kMaxBufferRecoveryInUsecs) {
|
||||
sleepTime = kMaxBufferRecoveryInUsecs;
|
||||
}
|
||||
// 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 (mBytesWritten != 0 && sleepTime >= kMaxBufferRecoveryInUsecs &&
|
||||
AudioSystem::isLinearPCM(mFormat)) {
|
||||
if (sleepTime == 0) {
|
||||
sleepTime = maxBufferRecoveryInUsecs;
|
||||
} else if (mBytesWritten != 0 && AudioSystem::isLinearPCM(mFormat)) {
|
||||
memset (mMixBuffer, 0, mFrameCount * mFrameSize);
|
||||
sleepTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSuspended) {
|
||||
sleepTime = kMaxBufferRecoveryInUsecs;
|
||||
sleepTime = maxBufferRecoveryInUsecs;
|
||||
}
|
||||
// sleepTime == 0 means we must write to audio hardware
|
||||
if (sleepTime == 0) {
|
||||
@@ -1861,6 +1859,21 @@ bool AudioFlinger::DirectOutputThread::checkForNewParameters_l()
|
||||
return reconfig;
|
||||
}
|
||||
|
||||
uint32_t AudioFlinger::DirectOutputThread::getMaxBufferRecoveryInUsecs()
|
||||
{
|
||||
uint32_t time;
|
||||
if (AudioSystem::isLinearPCM(mFormat)) {
|
||||
time = ((mFrameCount * 1000) / mSampleRate) * 1000;
|
||||
// Add some margin with regard to scheduling precision
|
||||
if (time > 10000) {
|
||||
time -= 10000;
|
||||
}
|
||||
} else {
|
||||
time = 10000;
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread)
|
||||
@@ -1877,13 +1890,15 @@ AudioFlinger::DuplicatingThread::~DuplicatingThread()
|
||||
|
||||
bool AudioFlinger::DuplicatingThread::threadLoop()
|
||||
{
|
||||
unsigned long sleepTime = kBufferRecoveryInUsecs;
|
||||
uint32_t sleepTime = 0;
|
||||
uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
|
||||
int16_t* curBuf = mMixBuffer;
|
||||
Vector< sp<Track> > tracksToRemove;
|
||||
size_t enabledTracks = 0;
|
||||
nsecs_t standbyTime = systemTime();
|
||||
size_t mixBufferSize = mFrameCount*mFrameSize;
|
||||
SortedVector< sp<OutputTrack> > outputTracks;
|
||||
uint32_t writeFrames = 0;
|
||||
|
||||
while (!exitPending())
|
||||
{
|
||||
@@ -1896,6 +1911,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
|
||||
|
||||
if (checkForNewParameters_l()) {
|
||||
mixBufferSize = mFrameCount*mFrameSize;
|
||||
maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
|
||||
}
|
||||
|
||||
const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
|
||||
@@ -1935,7 +1951,6 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
|
||||
}
|
||||
|
||||
standbyTime = systemTime() + kStandbyTimeInNsecs;
|
||||
sleepTime = kBufferRecoveryInUsecs;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1947,29 +1962,30 @@ bool AudioFlinger::DuplicatingThread::threadLoop()
|
||||
// mix buffers...
|
||||
mAudioMixer->process(curBuf);
|
||||
sleepTime = 0;
|
||||
standbyTime = systemTime() + kStandbyTimeInNsecs;
|
||||
writeFrames = mFrameCount;
|
||||
} else {
|
||||
sleepTime += kBufferRecoveryInUsecs;
|
||||
if (sleepTime > kMaxBufferRecoveryInUsecs) {
|
||||
sleepTime = kMaxBufferRecoveryInUsecs;
|
||||
}
|
||||
// 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 (mBytesWritten != 0 && sleepTime >= kMaxBufferRecoveryInUsecs) {
|
||||
memset (curBuf, 0, mixBufferSize);
|
||||
sleepTime = 0;
|
||||
if (sleepTime == 0) {
|
||||
sleepTime = maxBufferRecoveryInUsecs;
|
||||
} else if (mBytesWritten != 0) {
|
||||
// flush remaining overflow buffers in output tracks
|
||||
for (size_t i = 0; i < outputTracks.size(); i++) {
|
||||
if (outputTracks[i]->isActive()) {
|
||||
sleepTime = 0;
|
||||
writeFrames = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mSuspended) {
|
||||
sleepTime = kMaxBufferRecoveryInUsecs;
|
||||
sleepTime = maxBufferRecoveryInUsecs;
|
||||
}
|
||||
// sleepTime == 0 means we must write to audio hardware
|
||||
if (sleepTime == 0) {
|
||||
standbyTime = systemTime() + kStandbyTimeInNsecs;
|
||||
for (size_t i = 0; i < outputTracks.size(); i++) {
|
||||
outputTracks[i]->write(curBuf, mFrameCount);
|
||||
outputTracks[i]->write(curBuf, writeFrames);
|
||||
}
|
||||
mStandby = false;
|
||||
mBytesWritten += mixBufferSize;
|
||||
@@ -2026,7 +2042,6 @@ void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread)
|
||||
LOGV("removeOutputTrack(): unkonwn thread: %p", thread);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// TrackBase constructor must be called with AudioFlinger::mLock held
|
||||
@@ -2300,7 +2315,7 @@ status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider:
|
||||
getNextBuffer_exit:
|
||||
buffer->raw = 0;
|
||||
buffer->frameCount = 0;
|
||||
LOGV("getNextBuffer() no more data");
|
||||
LOGV("getNextBuffer() no more data for track %d on thread %p", mName, mThread.unsafe_get());
|
||||
return NOT_ENOUGH_DATA;
|
||||
}
|
||||
|
||||
@@ -2341,7 +2356,7 @@ void AudioFlinger::PlaybackThread::Track::stop()
|
||||
if (playbackThread->mActiveTracks.indexOf(this) < 0) {
|
||||
reset();
|
||||
}
|
||||
LOGV("(> STOPPED) => STOPPED (%d)", mName);
|
||||
LOGV("(> STOPPED) => STOPPED (%d) on thread %p", mName, playbackThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2354,7 +2369,7 @@ void AudioFlinger::PlaybackThread::Track::pause()
|
||||
Mutex::Autolock _l(thread->mLock);
|
||||
if (mState == ACTIVE || mState == RESUMING) {
|
||||
mState = PAUSING;
|
||||
LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName);
|
||||
LOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2566,7 +2581,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
|
||||
|
||||
uint32_t waitTimeLeftMs = mWaitTimeMs;
|
||||
|
||||
if (!mActive) {
|
||||
if (!mActive && frames != 0) {
|
||||
start();
|
||||
sp<ThreadBase> thread = mThread.promote();
|
||||
if (thread != 0) {
|
||||
@@ -2608,7 +2623,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
|
||||
break;
|
||||
}
|
||||
uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() - startTime);
|
||||
// LOGV("OutputTrack::write() waitTimeMs %d waitTimeLeftMs %d", waitTimeMs, waitTimeLeftMs)
|
||||
LOGV("OutputTrack::write() to thread %p waitTimeMs %d waitTimeLeftMs %d", mThread.unsafe_get(), waitTimeMs, waitTimeLeftMs);
|
||||
if (waitTimeLeftMs >= waitTimeMs) {
|
||||
waitTimeLeftMs -= waitTimeMs;
|
||||
} else {
|
||||
@@ -2663,7 +2678,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
|
||||
pInBuffer->i16 = pInBuffer->mBuffer;
|
||||
memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t));
|
||||
mBufferQueue.add(pInBuffer);
|
||||
} else {
|
||||
} else if (mActive) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,6 +524,10 @@ private:
|
||||
bool mMasterMute;
|
||||
SortedVector< wp<Track> > mActiveTracks;
|
||||
|
||||
virtual int getTrackName_l() = 0;
|
||||
virtual void deleteTrackName_l(int name) = 0;
|
||||
virtual uint32_t getMaxBufferRecoveryInUsecs() = 0;
|
||||
|
||||
private:
|
||||
|
||||
friend class AudioFlinger;
|
||||
@@ -539,8 +543,7 @@ private:
|
||||
|
||||
status_t addTrack_l(const sp<Track>& track);
|
||||
void destroyTrack_l(const sp<Track>& track);
|
||||
virtual int getTrackName_l() = 0;
|
||||
virtual void deleteTrackName_l(int name) = 0;
|
||||
|
||||
void readOutputParameters();
|
||||
|
||||
virtual status_t dumpInternals(int fd, const Vector<String16>& args);
|
||||
@@ -571,13 +574,14 @@ private:
|
||||
int streamType);
|
||||
void putTracks(SortedVector < sp<Track> >& tracks,
|
||||
SortedVector < wp<Track> >& activeTracks);
|
||||
virtual int getTrackName_l();
|
||||
virtual void deleteTrackName_l(int name);
|
||||
virtual bool checkForNewParameters_l();
|
||||
virtual status_t dumpInternals(int fd, const Vector<String16>& args);
|
||||
|
||||
protected:
|
||||
size_t prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove);
|
||||
virtual int getTrackName_l();
|
||||
virtual void deleteTrackName_l(int name);
|
||||
virtual uint32_t getMaxBufferRecoveryInUsecs();
|
||||
|
||||
AudioMixer* mAudioMixer;
|
||||
};
|
||||
@@ -591,9 +595,12 @@ private:
|
||||
// Thread virtuals
|
||||
virtual bool threadLoop();
|
||||
|
||||
virtual bool checkForNewParameters_l();
|
||||
|
||||
protected:
|
||||
virtual int getTrackName_l();
|
||||
virtual void deleteTrackName_l(int name);
|
||||
virtual bool checkForNewParameters_l();
|
||||
virtual uint32_t getMaxBufferRecoveryInUsecs();
|
||||
|
||||
private:
|
||||
float mLeftVolume;
|
||||
|
||||
@@ -879,6 +879,7 @@ ToneGenerator::~ToneGenerator() {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool ToneGenerator::startTone(int toneType, int durationMs) {
|
||||
bool lResult = false;
|
||||
status_t lStatus;
|
||||
|
||||
if ((toneType < 0) || (toneType >= NUM_TONES))
|
||||
return lResult;
|
||||
@@ -898,15 +899,16 @@ bool ToneGenerator::startTone(int toneType, int durationMs) {
|
||||
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;
|
||||
mDurationMs = durationMs;
|
||||
|
||||
if (mState == TONE_STOPPED) {
|
||||
LOGV("Start waiting for previous tone to stop");
|
||||
lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
|
||||
if (lStatus != NO_ERROR) {
|
||||
LOGE("--- start wait for stop timed out, status %d", lStatus);
|
||||
mState = TONE_IDLE;
|
||||
return lResult;
|
||||
}
|
||||
LOGV("startTone, duration limited to %d ms", durationMs);
|
||||
}
|
||||
|
||||
if (mState == TONE_INIT) {
|
||||
@@ -919,7 +921,7 @@ bool ToneGenerator::startTone(int toneType, int durationMs) {
|
||||
mLock.lock();
|
||||
if (mState == TONE_STARTING) {
|
||||
LOGV("Wait for start callback");
|
||||
status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
|
||||
lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
|
||||
if (lStatus != NO_ERROR) {
|
||||
LOGE("--- Immediate start timed out, status %d", lStatus);
|
||||
mState = TONE_IDLE;
|
||||
@@ -931,9 +933,8 @@ bool ToneGenerator::startTone(int toneType, int durationMs) {
|
||||
}
|
||||
} else {
|
||||
LOGV("Delayed start\n");
|
||||
|
||||
mState = TONE_RESTARTING;
|
||||
status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
|
||||
lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
|
||||
if (lStatus == NO_ERROR) {
|
||||
if (mState != TONE_IDLE) {
|
||||
lResult = true;
|
||||
@@ -1316,6 +1317,17 @@ bool ToneGenerator::prepareWave() {
|
||||
|
||||
mpToneDesc = mpNewToneDesc;
|
||||
|
||||
if (mDurationMs == -1) {
|
||||
mMaxSmp = TONEGEN_INF;
|
||||
} else {
|
||||
if (mDurationMs > (int)(TONEGEN_INF / mSamplingRate)) {
|
||||
mMaxSmp = (mDurationMs / 1000) * mSamplingRate;
|
||||
} else {
|
||||
mMaxSmp = (mDurationMs * mSamplingRate) / 1000;
|
||||
}
|
||||
LOGV("prepareWave, duration limited to %d ms", mDurationMs);
|
||||
}
|
||||
|
||||
while (mpToneDesc->segments[segmentIdx].duration) {
|
||||
// Get total number of sine waves: needed to adapt sine wave gain.
|
||||
unsigned int lNumWaves = numWaves(segmentIdx);
|
||||
|
||||
Reference in New Issue
Block a user