1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-07 07:39:29 +02:00

To synch better, just don't use a separate thread for MIDI! ...

... I am not sure that this will solve all problems of jittery play, but I do
think this will solve the problem of a rush of slightly late notes at start of
play.
This commit is contained in:
Paul Licameli 2017-08-27 10:53:05 -04:00
parent 582e574ab8
commit 75bdde3003
2 changed files with 73 additions and 43 deletions

View File

@ -904,7 +904,9 @@ void InitAudioIO()
gAudioIO = ugAudioIO.get();
gAudioIO->mThread->Run();
#ifdef EXPERIMENTAL_MIDI_OUT
#ifdef USE_MIDI_THREAD
gAudioIO->mMidiThread->Run();
#endif
#endif
// Make sure device prefs are initialized
@ -1033,8 +1035,12 @@ AudioIO::AudioIO()
// Same logic for PortMidi as described above for PortAudio
}
#ifdef USE_MIDI_THREAD
mMidiThread = std::make_unique<MidiThread>();
mMidiThread->Create();
#endif
#endif
// Start thread
@ -1083,8 +1089,11 @@ AudioIO::~AudioIO()
/* Delete is a "graceful" way to stop the thread.
(Kill is the not-graceful way.) */
#ifdef USE_MIDI_THREAD
mMidiThread->Delete();
mMidiThread.reset();
#endif
#endif
/* Delete is a "graceful" way to stop the thread.
@ -2323,13 +2332,19 @@ void AudioIO::StopStream()
#ifdef EXPERIMENTAL_MIDI_OUT
/* Stop Midi playback */
if ( mMidiStream ) {
mMidiStreamActive = false;
#ifdef USE_MIDI_THREAD
mMidiThreadFillBuffersLoopRunning = false; // stop output to stream
// but output is in another thread. Wait for output to stop...
while (mMidiThreadFillBuffersLoopActive) {
wxMilliSleep(1);
}
#endif
mMidiOutputComplete = true;
// now we can assume "ownership" of the mMidiStream
// if output in progress, send all off, etc.
AllNotesOff();
@ -2969,49 +2984,7 @@ MidiThread::ExitCode MidiThread::Entry()
// mNumFrames signals at least one callback, needed for MidiTime()
gAudioIO->mNumFrames > 0)
{
// Keep track of time paused. If not paused, fill buffers.
if (gAudioIO->IsPaused()) {
if (!gAudioIO->mMidiPaused) {
gAudioIO->mMidiPaused = true;
gAudioIO->AllNotesOff(); // to avoid hanging notes during pause
}
} else {
if (gAudioIO->mMidiPaused) {
gAudioIO->mMidiPaused = false;
}
gAudioIO->FillMidiBuffers();
// test for end
double realTime = gAudioIO->MidiTime() * 0.001 -
gAudioIO->PauseTime();
realTime -= 1; // MidiTime() runs ahead 1s
// XXX Is this still true now? It seems to break looping --Poke
//
// The TrackPanel::OnTimer() method updates the time position
// indicator every 200ms, so it tends to not advance the
// indicator to the end of the selection (mT1) but instead stop
// up to 200ms before the end. At this point, output is shut
// down and the indicator is removed, but for a brief time, the
// indicator is clearly stopped before reaching mT1. To avoid
// this, we do not set mMidiOutputComplete until we are actually
// 0.22s beyond mT1 (even though we stop playing at mT1). This
// gives OnTimer() time to wake up and draw the final time
// position at mT1 before shutting down the stream.
const double loopDelay = 0.220;
double timeAtSpeed;
if (gAudioIO->mTimeTrack)
timeAtSpeed = gAudioIO->mTimeTrack->SolveWarpedLength(gAudioIO->mT0, realTime);
else
timeAtSpeed = realTime;
gAudioIO->mMidiOutputComplete =
(gAudioIO->mPlayMode == gAudioIO->PLAY_STRAIGHT && // PRL: what if scrubbing?
timeAtSpeed >= gAudioIO->mT1 + loopDelay);
// !gAudioIO->mNextEvent);
}
gAudioIO->FillMidiBuffers();
}
gAudioIO->mMidiThreadFillBuffersLoopActive = false;
Sleep(MIDI_SLEEP);
@ -4009,6 +3982,19 @@ bool AudioIO::SetHasSolo(bool hasSolo)
void AudioIO::FillMidiBuffers()
{
// Keep track of time paused. If not paused, fill buffers.
if (gAudioIO->IsPaused()) {
if (!gAudioIO->mMidiPaused) {
gAudioIO->mMidiPaused = true;
gAudioIO->AllNotesOff(); // to avoid hanging notes during pause
}
return;
}
if (gAudioIO->mMidiPaused) {
gAudioIO->mMidiPaused = false;
}
bool hasSolo = false;
auto numPlaybackTracks = gAudioIO->mPlaybackTracks.size();
for(unsigned t = 0; t < numPlaybackTracks; t++ )
@ -4030,6 +4016,36 @@ void AudioIO::FillMidiBuffers()
OutputEvent();
GetNextEvent();
}
// test for end
double realTime = gAudioIO->MidiTime() * 0.001 -
gAudioIO->PauseTime();
realTime -= 1; // MidiTime() runs ahead 1s
// XXX Is this still true now? It seems to break looping --Poke
//
// The TrackPanel::OnTimer() method updates the time position
// indicator every 200ms, so it tends to not advance the
// indicator to the end of the selection (mT1) but instead stop
// up to 200ms before the end. At this point, output is shut
// down and the indicator is removed, but for a brief time, the
// indicator is clearly stopped before reaching mT1. To avoid
// this, we do not set mMidiOutputComplete until we are actually
// 0.22s beyond mT1 (even though we stop playing at mT1). This
// gives OnTimer() time to wake up and draw the final time
// position at mT1 before shutting down the stream.
const double loopDelay = 0.220;
double timeAtSpeed;
if (gAudioIO->mTimeTrack)
timeAtSpeed = gAudioIO->mTimeTrack->SolveWarpedLength(gAudioIO->mT0, realTime);
else
timeAtSpeed = realTime;
gAudioIO->mMidiOutputComplete =
(gAudioIO->mPlayMode == gAudioIO->PLAY_STRAIGHT && // PRL: what if scrubbing?
timeAtSpeed >= gAudioIO->mT1 + loopDelay);
// !gAudioIO->mNextEvent);
}
double AudioIO::PauseTime()
@ -4314,6 +4330,12 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
if(gAudioIO->IsPaused())
gAudioIO->mNumPauseFrames += framesPerBuffer;
gAudioIO->mNumFrames += framesPerBuffer;
#ifndef USE_MIDI_THREAD
if (gAudioIO->mMidiStream)
gAudioIO->FillMidiBuffers();
#endif
#endif
unsigned int i;

View File

@ -93,6 +93,12 @@ DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_AUDIOIO_PLAYBACK, -1);
DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_AUDIOIO_CAPTURE, -1);
DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_AUDIOIO_MONITOR, -1);
// If we always run a portaudio output stream (even just to produce silence)
// whenever we play Midi, then we can use just one thread for both, which
// simplifies synchronization problems and avoids the rush of notes at start of
// play. PRL.
#undef USE_MIDI_THREAD
struct ScrubbingOptions;
// To avoid growing the argument list of StartStream, add fields here
@ -607,7 +613,9 @@ private:
std::unique_ptr<AudioThread> mThread;
#ifdef EXPERIMENTAL_MIDI_OUT
#ifdef USE_MIDI_THREAD
std::unique_ptr<AudioThread> mMidiThread;
#endif
#endif
ArrayOf<std::unique_ptr<Resample>> mResample;
ArrayOf<std::unique_ptr<RingBuffer>> mCaptureBuffers;