1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-28 14:18:41 +02:00

Time track properly warps the MIDI event times (but not pitches)

mMidiPlaySpeed doesn't handle all of the options provided by time tracks,
and in fact doesn't even use time tracks.  Using the time track
instead allows note tracks to remain synchronized with the rest of the audio.

mMidiPlaySpeed _did_ work where it was used in the transcription tool
bar, but that already updates a time track so no special handling is
needed.
This commit is contained in:
Pokechu22 2017-02-22 12:39:12 -08:00 committed by Paul Licameli
parent 8b37ac5c39
commit c50567e1db
3 changed files with 25 additions and 20 deletions

View File

@ -997,7 +997,6 @@ AudioIO::AudioIO()
mMidiStreamActive = false; mMidiStreamActive = false;
mSendMidiState = false; mSendMidiState = false;
mIterator = NULL; mIterator = NULL;
mMidiPlaySpeed = 1.0;
mNumFrames = 0; mNumFrames = 0;
mNumPauseFrames = 0; mNumPauseFrames = 0;
@ -2388,7 +2387,6 @@ void AudioIO::StopStream()
} }
mIterator.reset(); // just in case someone tries to reference it mIterator.reset(); // just in case someone tries to reference it
mMidiPlaySpeed = 1.0;
} }
#endif #endif
@ -3021,6 +3019,9 @@ MidiThread::ExitCode MidiThread::Entry()
if (gAudioIO->mNumPlaybackChannels != 0) { if (gAudioIO->mNumPlaybackChannels != 0) {
realTime -= 1; // with audio, MidiTime() runs ahead 1s realTime -= 1; // with audio, MidiTime() runs ahead 1s
} }
// XXX Is this still true now? It seems to break looping --Poke
//
// The TrackPanel::OnTimer() method updates the time position // The TrackPanel::OnTimer() method updates the time position
// indicator every 200ms, so it tends to not advance the // indicator every 200ms, so it tends to not advance the
// indicator to the end of the selection (mT1) but instead stop // indicator to the end of the selection (mT1) but instead stop
@ -3028,15 +3029,20 @@ MidiThread::ExitCode MidiThread::Entry()
// down and the indicator is removed, but for a brief time, the // down and the indicator is removed, but for a brief time, the
// indicator is clearly stopped before reaching mT1. To avoid // indicator is clearly stopped before reaching mT1. To avoid
// this, we do not set mMidiOutputComplete until we are actually // this, we do not set mMidiOutputComplete until we are actually
// 0.22s beyond mT1 (even though we stop playing at mT1. This // 0.22s beyond mT1 (even though we stop playing at mT1). This
// gives OnTimer() time to wake up and draw the final time // gives OnTimer() time to wake up and draw the final time
// position at mT1 before shutting down the stream. // position at mT1 before shutting down the stream.
double timeAtSpeed = (realTime - gAudioIO->mT0) * const double loopDelay = 0.220;
gAudioIO->mMidiPlaySpeed + gAudioIO->mT0;
double timeAtSpeed;
if (gAudioIO->mTimeTrack)
timeAtSpeed = gAudioIO->mTimeTrack->SolveWarpedLength(gAudioIO->mT0, realTime);
else
timeAtSpeed = realTime;
gAudioIO->mMidiOutputComplete = gAudioIO->mMidiOutputComplete =
(gAudioIO->mPlayMode == gAudioIO->PLAY_STRAIGHT && // PRL: what if scrubbing? (gAudioIO->mPlayMode == gAudioIO->PLAY_STRAIGHT && // PRL: what if scrubbing?
timeAtSpeed >= gAudioIO->mT1 + 0.220); timeAtSpeed >= gAudioIO->mT1 + loopDelay);
// !gAudioIO->mNextEvent); // !gAudioIO->mNextEvent);
} }
} }
@ -3849,8 +3855,13 @@ void AudioIO::OutputEvent()
int command = -1; int command = -1;
int data1 = -1; int data1 = -1;
int data2 = -1; int data2 = -1;
double eventTime;
if (mTimeTrack)
eventTime = mTimeTrack->ComputeWarpedLength(mT0, mNextEventTime) + mT0;
else
eventTime = mNextEventTime;
// 0.0005 is for rounding // 0.0005 is for rounding
double eventTime = (mNextEventTime - mT0) / mMidiPlaySpeed + mT0;
double time = eventTime + PauseTime() + 0.0005 - double time = eventTime + PauseTime() + 0.0005 -
((mMidiLatency + mSynthLatency) * 0.001); ((mMidiLatency + mSynthLatency) * 0.001);
@ -4028,7 +4039,11 @@ void AudioIO::FillMidiBuffers()
time = AudioTime() - PauseTime(); time = AudioTime() - PauseTime();
} else { } else {
time = mT0 + Pt_Time() * 0.001 - PauseTime(); time = mT0 + Pt_Time() * 0.001 - PauseTime();
double timeAtSpeed = (time - mT0) * mMidiPlaySpeed + mT0; double timeAtSpeed;
if (mTimeTrack)
timeAtSpeed = mTimeTrack->SolveWarpedLength(mT0, time);
else
timeAtSpeed = time;
if (mNumCaptureChannels <= 0) { if (mNumCaptureChannels <= 0) {
// no audio callback, so move the time cursor here: // no audio callback, so move the time cursor here:
double trackTime = timeAtSpeed - mMidiLoopOffset; double trackTime = timeAtSpeed - mMidiLoopOffset;
@ -4054,8 +4069,8 @@ void AudioIO::FillMidiBuffers()
time += MIDI_SLEEP * 0.001; time += MIDI_SLEEP * 0.001;
} }
while (mNextEvent && while (mNextEvent &&
(mNextEventTime - mT0) / mMidiPlaySpeed + mT0 < time + (mTimeTrack ? (mTimeTrack->ComputeWarpedLength(mT0, mNextEventTime) + mT0) : mNextEventTime)
((MIDI_SLEEP + mSynthLatency) * 0.001)) { < time + ((MIDI_SLEEP + mSynthLatency) * 0.001)) {
OutputEvent(); OutputEvent();
GetNextEvent(); GetNextEvent();
} }

View File

@ -56,7 +56,6 @@ class TimeTrack;
class AudioThread; class AudioThread;
class Meter; class Meter;
class SelectedRegion; class SelectedRegion;
class TimeTrack;
class AudacityProject; class AudacityProject;
@ -227,9 +226,6 @@ class AUDACITY_DLL_API AudioIO final {
public: public:
bool SetHasSolo(bool hasSolo); bool SetHasSolo(bool hasSolo);
bool GetHasSolo() { return mHasSolo; } bool GetHasSolo() { return mHasSolo; }
/// Sets the speed for midi playback, based off of the transcription speed.
/// This takes a percentage, so passing 100 will play at normal speed.
void SetMidiPlaySpeed(double s) { mMidiPlaySpeed = s * 0.01; }
#endif #endif
/** \brief Returns true if the stream is active, or even if audio I/O is /** \brief Returns true if the stream is active, or even if audio I/O is
@ -527,9 +523,6 @@ private:
long mMidiLatency; long mMidiLatency;
/// Latency of MIDI synthesizer /// Latency of MIDI synthesizer
long mSynthLatency; long mSynthLatency;
/// A copy of TranscriptionToolBar::mPlaySpeed - a linear speed offset.
/// Should be replaced with use of mTimeTrack
double mMidiPlaySpeed;
// These fields are used to synchronize MIDI with audio: // These fields are used to synchronize MIDI with audio:

View File

@ -475,9 +475,6 @@ void TranscriptionToolBar::PlayAtSpeed(bool looped, bool cutPreview)
// Start playing // Start playing
if (playRegionStart >= 0) { if (playRegionStart >= 0) {
// playRegionEnd = playRegionStart + (playRegionEnd-playRegionStart)* 100.0/mPlaySpeed; // playRegionEnd = playRegionStart + (playRegionEnd-playRegionStart)* 100.0/mPlaySpeed;
#ifdef EXPERIMENTAL_MIDI_OUT
gAudioIO->SetMidiPlaySpeed(mPlaySpeed);
#endif
AudioIOStartStreamOptions options(p->GetDefaultPlayOptions()); AudioIOStartStreamOptions options(p->GetDefaultPlayOptions());
options.playLooped = looped; options.playLooped = looped;
options.timeTrack = mTimeTrack.get(); options.timeTrack = mTimeTrack.get();