mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-07 23:51:14 +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:
parent
582e574ab8
commit
75bdde3003
108
src/AudioIO.cpp
108
src/AudioIO.cpp
@ -904,7 +904,9 @@ void InitAudioIO()
|
|||||||
gAudioIO = ugAudioIO.get();
|
gAudioIO = ugAudioIO.get();
|
||||||
gAudioIO->mThread->Run();
|
gAudioIO->mThread->Run();
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
|
#ifdef USE_MIDI_THREAD
|
||||||
gAudioIO->mMidiThread->Run();
|
gAudioIO->mMidiThread->Run();
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Make sure device prefs are initialized
|
// Make sure device prefs are initialized
|
||||||
@ -1033,8 +1035,12 @@ AudioIO::AudioIO()
|
|||||||
|
|
||||||
// Same logic for PortMidi as described above for PortAudio
|
// Same logic for PortMidi as described above for PortAudio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_MIDI_THREAD
|
||||||
mMidiThread = std::make_unique<MidiThread>();
|
mMidiThread = std::make_unique<MidiThread>();
|
||||||
mMidiThread->Create();
|
mMidiThread->Create();
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Start thread
|
// Start thread
|
||||||
@ -1083,8 +1089,11 @@ AudioIO::~AudioIO()
|
|||||||
/* Delete is a "graceful" way to stop the thread.
|
/* Delete is a "graceful" way to stop the thread.
|
||||||
(Kill is the not-graceful way.) */
|
(Kill is the not-graceful way.) */
|
||||||
|
|
||||||
|
#ifdef USE_MIDI_THREAD
|
||||||
mMidiThread->Delete();
|
mMidiThread->Delete();
|
||||||
mMidiThread.reset();
|
mMidiThread.reset();
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Delete is a "graceful" way to stop the thread.
|
/* Delete is a "graceful" way to stop the thread.
|
||||||
@ -2323,13 +2332,19 @@ void AudioIO::StopStream()
|
|||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
/* Stop Midi playback */
|
/* Stop Midi playback */
|
||||||
if ( mMidiStream ) {
|
if ( mMidiStream ) {
|
||||||
|
|
||||||
mMidiStreamActive = false;
|
mMidiStreamActive = false;
|
||||||
|
|
||||||
|
#ifdef USE_MIDI_THREAD
|
||||||
mMidiThreadFillBuffersLoopRunning = false; // stop output to stream
|
mMidiThreadFillBuffersLoopRunning = false; // stop output to stream
|
||||||
// but output is in another thread. Wait for output to stop...
|
// but output is in another thread. Wait for output to stop...
|
||||||
while (mMidiThreadFillBuffersLoopActive) {
|
while (mMidiThreadFillBuffersLoopActive) {
|
||||||
wxMilliSleep(1);
|
wxMilliSleep(1);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
mMidiOutputComplete = true;
|
mMidiOutputComplete = true;
|
||||||
|
|
||||||
// now we can assume "ownership" of the mMidiStream
|
// now we can assume "ownership" of the mMidiStream
|
||||||
// if output in progress, send all off, etc.
|
// if output in progress, send all off, etc.
|
||||||
AllNotesOff();
|
AllNotesOff();
|
||||||
@ -2969,49 +2984,7 @@ MidiThread::ExitCode MidiThread::Entry()
|
|||||||
// mNumFrames signals at least one callback, needed for MidiTime()
|
// mNumFrames signals at least one callback, needed for MidiTime()
|
||||||
gAudioIO->mNumFrames > 0)
|
gAudioIO->mNumFrames > 0)
|
||||||
{
|
{
|
||||||
// Keep track of time paused. If not paused, fill buffers.
|
gAudioIO->FillMidiBuffers();
|
||||||
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->mMidiThreadFillBuffersLoopActive = false;
|
gAudioIO->mMidiThreadFillBuffersLoopActive = false;
|
||||||
Sleep(MIDI_SLEEP);
|
Sleep(MIDI_SLEEP);
|
||||||
@ -4009,6 +3982,19 @@ bool AudioIO::SetHasSolo(bool hasSolo)
|
|||||||
|
|
||||||
void AudioIO::FillMidiBuffers()
|
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;
|
bool hasSolo = false;
|
||||||
auto numPlaybackTracks = gAudioIO->mPlaybackTracks.size();
|
auto numPlaybackTracks = gAudioIO->mPlaybackTracks.size();
|
||||||
for(unsigned t = 0; t < numPlaybackTracks; t++ )
|
for(unsigned t = 0; t < numPlaybackTracks; t++ )
|
||||||
@ -4030,6 +4016,36 @@ void AudioIO::FillMidiBuffers()
|
|||||||
OutputEvent();
|
OutputEvent();
|
||||||
GetNextEvent();
|
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()
|
double AudioIO::PauseTime()
|
||||||
@ -4314,6 +4330,12 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
|
|||||||
if(gAudioIO->IsPaused())
|
if(gAudioIO->IsPaused())
|
||||||
gAudioIO->mNumPauseFrames += framesPerBuffer;
|
gAudioIO->mNumPauseFrames += framesPerBuffer;
|
||||||
gAudioIO->mNumFrames += framesPerBuffer;
|
gAudioIO->mNumFrames += framesPerBuffer;
|
||||||
|
|
||||||
|
#ifndef USE_MIDI_THREAD
|
||||||
|
if (gAudioIO->mMidiStream)
|
||||||
|
gAudioIO->FillMidiBuffers();
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
@ -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_CAPTURE, -1);
|
||||||
DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_AUDIOIO_MONITOR, -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;
|
struct ScrubbingOptions;
|
||||||
|
|
||||||
// To avoid growing the argument list of StartStream, add fields here
|
// To avoid growing the argument list of StartStream, add fields here
|
||||||
@ -607,7 +613,9 @@ private:
|
|||||||
|
|
||||||
std::unique_ptr<AudioThread> mThread;
|
std::unique_ptr<AudioThread> mThread;
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
|
#ifdef USE_MIDI_THREAD
|
||||||
std::unique_ptr<AudioThread> mMidiThread;
|
std::unique_ptr<AudioThread> mMidiThread;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
ArrayOf<std::unique_ptr<Resample>> mResample;
|
ArrayOf<std::unique_ptr<Resample>> mResample;
|
||||||
ArrayOf<std::unique_ptr<RingBuffer>> mCaptureBuffers;
|
ArrayOf<std::unique_ptr<RingBuffer>> mCaptureBuffers;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user