mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-16 08:34:10 +02:00
Fix MIDI timestamp calculations when you loop AND have time track...
... At least it fixes the gross problem; but there seems to be a small accumulation of error still each time around the loop, that I don't understand yet.
This commit is contained in:
parent
85b2f80545
commit
4a0a7efd84
@ -214,7 +214,8 @@
|
||||
special "event" of sending all notes off. After that, we destroy
|
||||
the iterator and use PrepareMidiIterator() to set up a NEW one.
|
||||
At each iteration, time must advance by (mT1 - mT0), so the
|
||||
accumulated time is held in mMidiLoopOffset.
|
||||
accumulated complete loop time (in "unwarped," track time) is computed
|
||||
by MidiLoopOffset().
|
||||
|
||||
\todo run through all functions called from audio and portaudio threads
|
||||
to verify they are thread-safe. Note that synchronization of the style:
|
||||
@ -2144,7 +2145,7 @@ bool AudioIO::StartPortMidiStream()
|
||||
if (mLastPmError == pmNoError) {
|
||||
mMidiStreamActive = true;
|
||||
mMidiPaused = false;
|
||||
mMidiLoopOffset = 0;
|
||||
mMidiLoopPasses = 0;
|
||||
mMidiOutputComplete = false;
|
||||
PrepareMidiIterator();
|
||||
|
||||
@ -3804,6 +3805,19 @@ void AudioIO::SetListener(AudioIOListener* listener)
|
||||
static Alg_update gAllNotesOff; // special event for loop ending
|
||||
// the fields of this event are never used, only the address is important
|
||||
|
||||
double AudioIO::UncorrectedMidiEventTime()
|
||||
{
|
||||
double time;
|
||||
if (mTimeTrack)
|
||||
time =
|
||||
mTimeTrack->ComputeWarpedLength(mT0, mNextEventTime - MidiLoopOffset())
|
||||
+ mT0 + (mMidiLoopPasses * mWarpedLength);
|
||||
else
|
||||
time = mNextEventTime;
|
||||
|
||||
return time + PauseTime();
|
||||
}
|
||||
|
||||
void AudioIO::OutputEvent()
|
||||
{
|
||||
int channel = (mNextEvent->chan) & 0xF; // must be in [0..15]
|
||||
@ -3811,13 +3825,10 @@ void AudioIO::OutputEvent()
|
||||
int data1 = -1;
|
||||
int data2 = -1;
|
||||
|
||||
double eventTime;
|
||||
if (mTimeTrack)
|
||||
eventTime = mTimeTrack->ComputeWarpedLength(mT0, mNextEventTime) + mT0;
|
||||
else
|
||||
eventTime = mNextEventTime;
|
||||
double eventTime = UncorrectedMidiEventTime();
|
||||
|
||||
// 0.0005 is for rounding
|
||||
double time = eventTime + PauseTime() + 0.0005 -
|
||||
double time = eventTime + 0.0005 -
|
||||
((mMidiLatency + mSynthLatency) * 0.001);
|
||||
|
||||
time += 1; // MidiTime() has a 1s offset
|
||||
@ -3833,8 +3844,8 @@ void AudioIO::OutputEvent()
|
||||
AllNotesOff();
|
||||
if (mPlayMode == gAudioIO->PLAY_LOOPED) {
|
||||
// jump back to beginning of loop
|
||||
mMidiLoopOffset += (mT1 - mT0);
|
||||
PrepareMidiIterator(false, mMidiLoopOffset);
|
||||
++mMidiLoopPasses;
|
||||
PrepareMidiIterator(false, MidiLoopOffset());
|
||||
} else {
|
||||
mNextEvent = NULL;
|
||||
}
|
||||
@ -3958,18 +3969,19 @@ void AudioIO::GetNextEvent()
|
||||
mNextEvent = NULL;
|
||||
return;
|
||||
}
|
||||
auto midiLoopOffset = MidiLoopOffset();
|
||||
mNextEvent = mIterator->next(&mNextIsNoteOn,
|
||||
(void **) &mNextEventTrack,
|
||||
&nextOffset, mT1 + mMidiLoopOffset);
|
||||
&nextOffset, mT1 + midiLoopOffset);
|
||||
|
||||
mNextEventTime = mT1 + mMidiLoopOffset + 1;
|
||||
mNextEventTime = mT1 + midiLoopOffset + 1;
|
||||
if (mNextEvent) {
|
||||
mNextEventTime = (mNextIsNoteOn ? mNextEvent->time :
|
||||
mNextEvent->get_end_time()) + nextOffset;;
|
||||
}
|
||||
if (mNextEventTime > (mT1 + mMidiLoopOffset)){ // terminate playback at mT1
|
||||
if (mNextEventTime > (mT1 + midiLoopOffset)){ // terminate playback at mT1
|
||||
mNextEvent = &gAllNotesOff;
|
||||
mNextEventTime = mT1 + mMidiLoopOffset - ALG_EPS;
|
||||
mNextEventTime = mT1 + midiLoopOffset - ALG_EPS;
|
||||
mNextIsNoteOn = true; // do not look at duration
|
||||
mIterator->end();
|
||||
mIterator.reset(); // debugging aid
|
||||
@ -4000,12 +4012,10 @@ void AudioIO::FillMidiBuffers()
|
||||
break;
|
||||
}
|
||||
SetHasSolo(hasSolo);
|
||||
// Compute the current track time differently depending upon
|
||||
// whether audio playback is in effect:
|
||||
double time = AudioTime() - PauseTime();
|
||||
double time = AudioTime();
|
||||
while (mNextEvent &&
|
||||
(mTimeTrack ? (mTimeTrack->ComputeWarpedLength(mT0, mNextEventTime) + mT0) : mNextEventTime)
|
||||
< time + ((MIDI_SLEEP + mSynthLatency) * 0.001)) {
|
||||
UncorrectedMidiEventTime() <
|
||||
time + ((MIDI_SLEEP + mSynthLatency) * 0.001)) {
|
||||
OutputEvent();
|
||||
GetNextEvent();
|
||||
}
|
||||
|
@ -437,6 +437,11 @@ private:
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
void PrepareMidiIterator(bool send = true, double offset = 0);
|
||||
bool StartPortMidiStream();
|
||||
|
||||
// Compute nondecreasing time stamps, accounting for pauses, but not the
|
||||
// synth latency.
|
||||
double UncorrectedMidiEventTime();
|
||||
|
||||
void OutputEvent();
|
||||
void FillMidiBuffers();
|
||||
void GetNextEvent();
|
||||
@ -542,7 +547,9 @@ private:
|
||||
/// How many frames of zeros were output due to pauses?
|
||||
volatile long mNumPauseFrames;
|
||||
/// total of backward jumps
|
||||
volatile double mMidiLoopOffset;
|
||||
volatile int mMidiLoopPasses;
|
||||
inline double MidiLoopOffset() { return mMidiLoopPasses * (mT1 - mT0); }
|
||||
|
||||
volatile long mAudioFramesPerBuffer;
|
||||
/// Used by Midi process to record that pause has begun,
|
||||
/// so that AllNotesOff() is only delivered once
|
||||
@ -613,12 +620,17 @@ private:
|
||||
double mT1;
|
||||
/// Current time position during playback, in seconds. Between mT0 and mT1.
|
||||
double mTime;
|
||||
/// Current time after warping, starting at zero (unlike mTime).
|
||||
/// Length in real seconds between mT0 and mTime.
|
||||
|
||||
/// Accumulated real time (not track position), starting at zero (unlike
|
||||
/// mTime), and wrapping back to zero each time around looping play.
|
||||
/// Thus, it is the length in real seconds between mT0 and mTime.
|
||||
double mWarpedTime;
|
||||
/// Total length after warping via a time track.
|
||||
|
||||
/// Real length to be played (if looping, for each pass) after warping via a
|
||||
/// time track, computed just once when starting the stream.
|
||||
/// Length in real seconds between mT0 and mT1. Always positive.
|
||||
double mWarpedLength;
|
||||
|
||||
double mSeek;
|
||||
double mPlaybackRingBufferSecs;
|
||||
double mCaptureRingBufferSecs;
|
||||
|
Loading…
x
Reference in New Issue
Block a user