mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-17 08:30:06 +02:00
Make scrubbing work when timer interval constant is shortened to 1 ms
* scrubbing: Allow scrub timer interval to be 1 ms, without bogus out-of-memory warnings. Less scrub lag: avoid redundant disk traffic in Mixer... Less scrub lag: don't poll for available data, get woken up directly
This commit is contained in:
commit
c3246e3f7b
@ -405,18 +405,28 @@ struct AudioIO::ScrubQueue
|
|||||||
double LastTimeInQueue() const
|
double LastTimeInQueue() const
|
||||||
{
|
{
|
||||||
// Needed by the main thread sometimes
|
// Needed by the main thread sometimes
|
||||||
wxCriticalSectionLocker locker(mUpdating);
|
wxMutexLocker locker(mUpdating);
|
||||||
const Entry &previous = mEntries[(mLeadingIdx + Size - 1) % Size];
|
const Entry &previous = mEntries[(mLeadingIdx + Size - 1) % Size];
|
||||||
return previous.mS1 / mRate;
|
return previous.mS1 / mRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PoisonPill()
|
||||||
|
{
|
||||||
|
// Main thread is shutting down the scrubbing
|
||||||
|
wxMutexLocker locker(mUpdating);
|
||||||
|
mPoisoned = true;
|
||||||
|
mAvailable.Signal();
|
||||||
|
}
|
||||||
|
|
||||||
bool Producer(double end, double maxSpeed, bool bySpeed, bool maySkip)
|
bool Producer(double end, double maxSpeed, bool bySpeed, bool maySkip)
|
||||||
{
|
{
|
||||||
|
wxASSERT(!mPoisoned);
|
||||||
|
|
||||||
// Main thread indicates a scrubbing interval
|
// Main thread indicates a scrubbing interval
|
||||||
|
|
||||||
// MAY ADVANCE mLeadingIdx, BUT IT NEVER CATCHES UP TO mTrailingIdx.
|
// MAY ADVANCE mLeadingIdx, BUT IT NEVER CATCHES UP TO mTrailingIdx.
|
||||||
|
|
||||||
wxCriticalSectionLocker locker(mUpdating);
|
wxMutexLocker locker(mUpdating);
|
||||||
const unsigned next = (mLeadingIdx + 1) % Size;
|
const unsigned next = (mLeadingIdx + 1) % Size;
|
||||||
if (next != mTrailingIdx)
|
if (next != mTrailingIdx)
|
||||||
{
|
{
|
||||||
@ -429,8 +439,10 @@ struct AudioIO::ScrubQueue
|
|||||||
const bool success =
|
const bool success =
|
||||||
(InitEntry(mEntries[mLeadingIdx], startTime, end, maxSpeed,
|
(InitEntry(mEntries[mLeadingIdx], startTime, end, maxSpeed,
|
||||||
bySpeed, &previous, maySkip));
|
bySpeed, &previous, maySkip));
|
||||||
if (success)
|
if (success) {
|
||||||
mLeadingIdx = next;
|
mLeadingIdx = next;
|
||||||
|
mAvailable.Signal();
|
||||||
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -450,7 +462,10 @@ struct AudioIO::ScrubQueue
|
|||||||
|
|
||||||
// MAY ADVANCE mMiddleIdx, WHICH MAY EQUAL mLeadingIdx, BUT DOES NOT PASS IT.
|
// MAY ADVANCE mMiddleIdx, WHICH MAY EQUAL mLeadingIdx, BUT DOES NOT PASS IT.
|
||||||
|
|
||||||
wxCriticalSectionLocker locker(mUpdating);
|
wxMutexLocker locker(mUpdating);
|
||||||
|
while(!mPoisoned && mMiddleIdx == mLeadingIdx)
|
||||||
|
mAvailable.Wait();
|
||||||
|
|
||||||
if (mMiddleIdx != mLeadingIdx)
|
if (mMiddleIdx != mLeadingIdx)
|
||||||
{
|
{
|
||||||
// There is work in the queue
|
// There is work in the queue
|
||||||
@ -463,7 +478,8 @@ struct AudioIO::ScrubQueue
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// next entry is not yet ready
|
wxASSERT(mPoisoned);
|
||||||
|
// We got the shut-down signal
|
||||||
startSample = endSample = duration = -1L;
|
startSample = endSample = duration = -1L;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -475,7 +491,7 @@ struct AudioIO::ScrubQueue
|
|||||||
|
|
||||||
// MAY ADVANCE mTrailingIdx, BUT IT NEVER CATCHES UP TO mMiddleIdx.
|
// MAY ADVANCE mTrailingIdx, BUT IT NEVER CATCHES UP TO mMiddleIdx.
|
||||||
|
|
||||||
wxCriticalSectionLocker locker(mUpdating);
|
wxMutexLocker locker(mUpdating);
|
||||||
|
|
||||||
// Mark entries as partly or fully "consumed" for
|
// Mark entries as partly or fully "consumed" for
|
||||||
// purposes of mTime update. It should not happen that
|
// purposes of mTime update. It should not happen that
|
||||||
@ -678,7 +694,9 @@ private:
|
|||||||
const double mRate;
|
const double mRate;
|
||||||
const long mMinStutter;
|
const long mMinStutter;
|
||||||
wxLongLong mLastScrubTimeMillis;
|
wxLongLong mLastScrubTimeMillis;
|
||||||
mutable wxCriticalSection mUpdating;
|
mutable wxMutex mUpdating;
|
||||||
|
mutable wxCondition mAvailable { mUpdating };
|
||||||
|
bool mPoisoned { false };
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1710,14 +1728,6 @@ int AudioIO::StartStream(const WaveTrackArray &playbackTracks,
|
|||||||
sampleCount playbackMixBufferSize =
|
sampleCount playbackMixBufferSize =
|
||||||
(sampleCount)mPlaybackSamplesToCopy;
|
(sampleCount)mPlaybackSamplesToCopy;
|
||||||
|
|
||||||
// In the extraordinarily rare case that we can't even afford 100 samples, just give up.
|
|
||||||
if(playbackBufferSize < 100 || playbackMixBufferSize < 100)
|
|
||||||
{
|
|
||||||
StartStreamCleanup();
|
|
||||||
wxMessageBox(_("Out of memory!"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPlaybackBuffers = new RingBuffer* [mPlaybackTracks->size()];
|
mPlaybackBuffers = new RingBuffer* [mPlaybackTracks->size()];
|
||||||
mPlaybackMixers = new Mixer* [mPlaybackTracks->size()];
|
mPlaybackMixers = new Mixer* [mPlaybackTracks->size()];
|
||||||
|
|
||||||
@ -1786,7 +1796,19 @@ int AudioIO::StartStream(const WaveTrackArray &playbackTracks,
|
|||||||
mCaptureRingBufferSecs *= 0.5;
|
mCaptureRingBufferSecs *= 0.5;
|
||||||
mMinCaptureSecsToCopy *= 0.5;
|
mMinCaptureSecsToCopy *= 0.5;
|
||||||
bDone = false;
|
bDone = false;
|
||||||
}
|
|
||||||
|
// In the extraordinarily rare case that we can't even afford 100 samples, just give up.
|
||||||
|
sampleCount playbackBufferSize =
|
||||||
|
(sampleCount)lrint(mRate * mPlaybackRingBufferSecs);
|
||||||
|
sampleCount playbackMixBufferSize =
|
||||||
|
(sampleCount)mPlaybackSamplesToCopy;
|
||||||
|
if(playbackBufferSize < 100 || playbackMixBufferSize < 100)
|
||||||
|
{
|
||||||
|
StartStreamCleanup();
|
||||||
|
wxMessageBox(_("Out of memory!"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
} while(!bDone);
|
} while(!bDone);
|
||||||
|
|
||||||
if (mNumPlaybackChannels > 0)
|
if (mNumPlaybackChannels > 0)
|
||||||
@ -2166,6 +2188,8 @@ void AudioIO::StopStream()
|
|||||||
//
|
//
|
||||||
|
|
||||||
mAudioThreadFillBuffersLoopRunning = false;
|
mAudioThreadFillBuffersLoopRunning = false;
|
||||||
|
if (mScrubQueue)
|
||||||
|
mScrubQueue->PoisonPill();
|
||||||
|
|
||||||
// Audacity can deadlock if it tries to update meters while
|
// Audacity can deadlock if it tries to update meters while
|
||||||
// we're stopping PortAudio (because the meter updating code
|
// we're stopping PortAudio (because the meter updating code
|
||||||
@ -2821,7 +2845,16 @@ AudioThread::ExitCode AudioThread::Entry()
|
|||||||
}
|
}
|
||||||
gAudioIO->mAudioThreadFillBuffersLoopActive = false;
|
gAudioIO->mAudioThreadFillBuffersLoopActive = false;
|
||||||
|
|
||||||
Sleep(10);
|
if (gAudioIO->mPlayMode == AudioIO::PLAY_SCRUB) {
|
||||||
|
// Rely on the Wait() in ScrubQueue::Transformer()
|
||||||
|
// This allows the scrubbing update interval to be made very short without
|
||||||
|
// playback becoming intermittent.
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Perhaps this too could use a condition variable, for available space in the
|
||||||
|
// ring buffer, instead of a polling loop? But no harm in doing it this way.
|
||||||
|
Sleep(10);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -650,7 +650,7 @@ private:
|
|||||||
bool mInputMixerWorks;
|
bool mInputMixerWorks;
|
||||||
float mMixerOutputVol;
|
float mMixerOutputVol;
|
||||||
|
|
||||||
enum {
|
volatile enum {
|
||||||
PLAY_STRAIGHT,
|
PLAY_STRAIGHT,
|
||||||
PLAY_LOOPED,
|
PLAY_LOOPED,
|
||||||
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||||
|
45
src/Mix.cpp
45
src/Mix.cpp
@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "Audacity.h"
|
#include "Audacity.h"
|
||||||
|
|
||||||
#include "Mix.h"
|
#include "Mix.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
@ -251,13 +250,13 @@ Mixer::Mixer(const WaveTrackConstArray &inputTracks,
|
|||||||
const auto numInputTracks = inputTracks.size();
|
const auto numInputTracks = inputTracks.size();
|
||||||
mHighQuality = highQuality;
|
mHighQuality = highQuality;
|
||||||
mNumInputTracks = numInputTracks;
|
mNumInputTracks = numInputTracks;
|
||||||
mInputTrack = new const WaveTrack*[mNumInputTracks];
|
mInputTrack = new WaveTrackCache[mNumInputTracks];
|
||||||
|
|
||||||
// mSamplePos holds for each track the next sample position not
|
// mSamplePos holds for each track the next sample position not
|
||||||
// yet processed.
|
// yet processed.
|
||||||
mSamplePos = new sampleCount[mNumInputTracks];
|
mSamplePos = new sampleCount[mNumInputTracks];
|
||||||
for(i=0; i<mNumInputTracks; i++) {
|
for(i=0; i<mNumInputTracks; i++) {
|
||||||
mInputTrack[i] = inputTracks[i];
|
mInputTrack[i].SetTrack(inputTracks[i]);
|
||||||
mSamplePos[i] = inputTracks[i]->TimeToLongSamples(startTime);
|
mSamplePos[i] = inputTracks[i]->TimeToLongSamples(startTime);
|
||||||
}
|
}
|
||||||
mTimeTrack = warpOptions.timeTrack;
|
mTimeTrack = warpOptions.timeTrack;
|
||||||
@ -313,7 +312,7 @@ Mixer::Mixer(const WaveTrackConstArray &inputTracks,
|
|||||||
mSampleQueue = new float *[mNumInputTracks];
|
mSampleQueue = new float *[mNumInputTracks];
|
||||||
mResample = new Resample*[mNumInputTracks];
|
mResample = new Resample*[mNumInputTracks];
|
||||||
for(i=0; i<mNumInputTracks; i++) {
|
for(i=0; i<mNumInputTracks; i++) {
|
||||||
double factor = (mRate / mInputTrack[i]->GetRate());
|
double factor = (mRate / mInputTrack[i].GetTrack()->GetRate());
|
||||||
double minFactor, maxFactor;
|
double minFactor, maxFactor;
|
||||||
if (mTimeTrack) {
|
if (mTimeTrack) {
|
||||||
// variable rate resampling
|
// variable rate resampling
|
||||||
@ -408,11 +407,12 @@ void MixBuffers(int numChannels, int *channelFlags, float *gains,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleCount Mixer::MixVariableRates(int *channelFlags, const WaveTrack *track,
|
sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrackCache &cache,
|
||||||
sampleCount *pos, float *queue,
|
sampleCount *pos, float *queue,
|
||||||
int *queueStart, int *queueLen,
|
int *queueStart, int *queueLen,
|
||||||
Resample * pResample)
|
Resample * pResample)
|
||||||
{
|
{
|
||||||
|
const WaveTrack *const track = cache.GetTrack();
|
||||||
const double trackRate = track->GetRate();
|
const double trackRate = track->GetRate();
|
||||||
const double initialWarp = mRate / mSpeed / trackRate;
|
const double initialWarp = mRate / mSpeed / trackRate;
|
||||||
const double tstep = 1.0 / trackRate;
|
const double tstep = 1.0 / trackRate;
|
||||||
@ -455,10 +455,8 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, const WaveTrack *track,
|
|||||||
// Nothing to do if past end of play interval
|
// Nothing to do if past end of play interval
|
||||||
if (getLen > 0) {
|
if (getLen > 0) {
|
||||||
if (backwards) {
|
if (backwards) {
|
||||||
track->Get((samplePtr)&queue[*queueLen],
|
auto results = cache.Get(floatSample, *pos - (getLen - 1), getLen);
|
||||||
floatSample,
|
memcpy(&queue[*queueLen], results, sizeof(float) * getLen);
|
||||||
*pos - (getLen - 1),
|
|
||||||
getLen);
|
|
||||||
|
|
||||||
track->GetEnvelopeValues(mEnvValues,
|
track->GetEnvelopeValues(mEnvValues,
|
||||||
getLen,
|
getLen,
|
||||||
@ -468,10 +466,8 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, const WaveTrack *track,
|
|||||||
*pos -= getLen;
|
*pos -= getLen;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
track->Get((samplePtr)&queue[*queueLen],
|
auto results = cache.Get(floatSample, *pos, getLen);
|
||||||
floatSample,
|
memcpy(&queue[*queueLen], results, sizeof(float) * getLen);
|
||||||
*pos,
|
|
||||||
getLen);
|
|
||||||
|
|
||||||
track->GetEnvelopeValues(mEnvValues,
|
track->GetEnvelopeValues(mEnvValues,
|
||||||
getLen,
|
getLen,
|
||||||
@ -558,9 +554,10 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, const WaveTrack *track,
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleCount Mixer::MixSameRate(int *channelFlags, const WaveTrack *track,
|
sampleCount Mixer::MixSameRate(int *channelFlags, WaveTrackCache &cache,
|
||||||
sampleCount *pos)
|
sampleCount *pos)
|
||||||
{
|
{
|
||||||
|
const WaveTrack *const track = cache.GetTrack();
|
||||||
int slen = mMaxOut;
|
int slen = mMaxOut;
|
||||||
int c;
|
int c;
|
||||||
const double t = *pos / track->GetRate();
|
const double t = *pos / track->GetRate();
|
||||||
@ -588,7 +585,8 @@ sampleCount Mixer::MixSameRate(int *channelFlags, const WaveTrack *track,
|
|||||||
slen = mMaxOut;
|
slen = mMaxOut;
|
||||||
|
|
||||||
if (backwards) {
|
if (backwards) {
|
||||||
track->Get((samplePtr)mFloatBuffer, floatSample, *pos - (slen - 1), slen);
|
auto results = cache.Get(floatSample, *pos - (slen - 1), slen);
|
||||||
|
memcpy(mFloatBuffer, results, sizeof(float) * slen);
|
||||||
track->GetEnvelopeValues(mEnvValues, slen, t - (slen - 1) / mRate, 1.0 / mRate);
|
track->GetEnvelopeValues(mEnvValues, slen, t - (slen - 1) / mRate, 1.0 / mRate);
|
||||||
for(int i=0; i<slen; i++)
|
for(int i=0; i<slen; i++)
|
||||||
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
|
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
|
||||||
@ -597,7 +595,8 @@ sampleCount Mixer::MixSameRate(int *channelFlags, const WaveTrack *track,
|
|||||||
*pos -= slen;
|
*pos -= slen;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
track->Get((samplePtr)mFloatBuffer, floatSample, *pos, slen);
|
auto results = cache.Get(floatSample, *pos, slen);
|
||||||
|
memcpy(mFloatBuffer, results, sizeof(float) * slen);
|
||||||
track->GetEnvelopeValues(mEnvValues, slen, t, 1.0 / mRate);
|
track->GetEnvelopeValues(mEnvValues, slen, t, 1.0 / mRate);
|
||||||
for(int i=0; i<slen; i++)
|
for(int i=0; i<slen; i++)
|
||||||
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
|
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
|
||||||
@ -632,7 +631,7 @@ sampleCount Mixer::Process(sampleCount maxToProcess)
|
|||||||
|
|
||||||
Clear();
|
Clear();
|
||||||
for(i=0; i<mNumInputTracks; i++) {
|
for(i=0; i<mNumInputTracks; i++) {
|
||||||
const WaveTrack *track = mInputTrack[i];
|
const WaveTrack *const track = mInputTrack[i].GetTrack();
|
||||||
for(j=0; j<mNumChannels; j++)
|
for(j=0; j<mNumChannels; j++)
|
||||||
channelFlags[j] = 0;
|
channelFlags[j] = 0;
|
||||||
|
|
||||||
@ -661,12 +660,12 @@ sampleCount Mixer::Process(sampleCount maxToProcess)
|
|||||||
}
|
}
|
||||||
if (mbVariableRates || track->GetRate() != mRate)
|
if (mbVariableRates || track->GetRate() != mRate)
|
||||||
maxOut = std::max(maxOut,
|
maxOut = std::max(maxOut,
|
||||||
MixVariableRates(channelFlags, track,
|
MixVariableRates(channelFlags, mInputTrack[i],
|
||||||
&mSamplePos[i], mSampleQueue[i],
|
&mSamplePos[i], mSampleQueue[i],
|
||||||
&mQueueStart[i], &mQueueLen[i], mResample[i]));
|
&mQueueStart[i], &mQueueLen[i], mResample[i]));
|
||||||
else
|
else
|
||||||
maxOut = std::max(maxOut,
|
maxOut = std::max(maxOut,
|
||||||
MixSameRate(channelFlags, track, &mSamplePos[i]));
|
MixSameRate(channelFlags, mInputTrack[i], &mSamplePos[i]));
|
||||||
|
|
||||||
double t = (double)mSamplePos[i] / (double)track->GetRate();
|
double t = (double)mSamplePos[i] / (double)track->GetRate();
|
||||||
if (mT0 > mT1)
|
if (mT0 > mT1)
|
||||||
@ -728,7 +727,7 @@ void Mixer::Restart()
|
|||||||
mTime = mT0;
|
mTime = mT0;
|
||||||
|
|
||||||
for(i=0; i<mNumInputTracks; i++)
|
for(i=0; i<mNumInputTracks; i++)
|
||||||
mSamplePos[i] = mInputTrack[i]->TimeToLongSamples(mT0);
|
mSamplePos[i] = mInputTrack[i].GetTrack()->TimeToLongSamples(mT0);
|
||||||
|
|
||||||
for(i=0; i<mNumInputTracks; i++) {
|
for(i=0; i<mNumInputTracks; i++) {
|
||||||
mQueueStart[i] = 0;
|
mQueueStart[i] = 0;
|
||||||
@ -748,7 +747,7 @@ void Mixer::Reposition(double t)
|
|||||||
mTime = std::max(mT0, (std::min(mT1, mTime)));
|
mTime = std::max(mT0, (std::min(mT1, mTime)));
|
||||||
|
|
||||||
for(i=0; i<mNumInputTracks; i++) {
|
for(i=0; i<mNumInputTracks; i++) {
|
||||||
mSamplePos[i] = mInputTrack[i]->TimeToLongSamples(mTime);
|
mSamplePos[i] = mInputTrack[i].GetTrack()->TimeToLongSamples(mTime);
|
||||||
mQueueStart[i] = 0;
|
mQueueStart[i] = 0;
|
||||||
mQueueLen[i] = 0;
|
mQueueLen[i] = 0;
|
||||||
}
|
}
|
||||||
|
11
src/Mix.h
11
src/Mix.h
@ -14,16 +14,16 @@
|
|||||||
|
|
||||||
#include "MemoryX.h"
|
#include "MemoryX.h"
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
|
|
||||||
#include "SampleFormat.h"
|
#include "SampleFormat.h"
|
||||||
#include "Resample.h"
|
|
||||||
|
|
||||||
|
class Resample;
|
||||||
class DirManager;
|
class DirManager;
|
||||||
class TimeTrack;
|
class TimeTrack;
|
||||||
class TrackFactory;
|
class TrackFactory;
|
||||||
class TrackList;
|
class TrackList;
|
||||||
class WaveTrack;
|
class WaveTrack;
|
||||||
class WaveTrackConstArray;
|
class WaveTrackConstArray;
|
||||||
|
class WaveTrackCache;
|
||||||
|
|
||||||
/** @brief Mixes together all input tracks, applying any envelopes, amplitude
|
/** @brief Mixes together all input tracks, applying any envelopes, amplitude
|
||||||
* gain, panning, and real-time effects in the process.
|
* gain, panning, and real-time effects in the process.
|
||||||
@ -141,10 +141,10 @@ class AUDACITY_DLL_API Mixer {
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
sampleCount MixSameRate(int *channelFlags, const WaveTrack *src,
|
sampleCount MixSameRate(int *channelFlags, WaveTrackCache &cache,
|
||||||
sampleCount *pos);
|
sampleCount *pos);
|
||||||
|
|
||||||
sampleCount MixVariableRates(int *channelFlags, const WaveTrack *track,
|
sampleCount MixVariableRates(int *channelFlags, WaveTrackCache &cache,
|
||||||
sampleCount *pos, float *queue,
|
sampleCount *pos, float *queue,
|
||||||
int *queueStart, int *queueLen,
|
int *queueStart, int *queueLen,
|
||||||
Resample * pResample);
|
Resample * pResample);
|
||||||
@ -152,7 +152,8 @@ class AUDACITY_DLL_API Mixer {
|
|||||||
private:
|
private:
|
||||||
// Input
|
// Input
|
||||||
int mNumInputTracks;
|
int mNumInputTracks;
|
||||||
const WaveTrack **mInputTrack;
|
WaveTrackCache *mInputTrack;
|
||||||
|
|
||||||
bool mbVariableRates;
|
bool mbVariableRates;
|
||||||
const TimeTrack *mTimeTrack;
|
const TimeTrack *mTimeTrack;
|
||||||
sampleCount *mSamplePos;
|
sampleCount *mSamplePos;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user