1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-02 16:49:41 +02:00

A not-harmful change to MIDI timing for Windows and Mac...

... and hoping it is positively helpful for Linux.

In AudioIO::MidiTime(), compute one of the terms by different means.

Use PaStreamInfo::outputLatency.

Do not use the difference of PaStreamCallbackTimeInfo::outputBufferDacTime
and PaStreamCallbackTimeInfo::currentTime.

Which debugging shows is very nearly the same value for Windows and Mac.

But we suspect the PaStreamCallbackTimeInfo fields are not correctly reported
on Linux.
This commit is contained in:
Paul Licameli 2017-08-27 03:26:44 -04:00
parent faa7d1828a
commit 582e574ab8
2 changed files with 45 additions and 14 deletions

View File

@ -315,6 +315,13 @@ wxArrayLong AudioIO::mCachedSampleRates;
double AudioIO::mCachedBestRateIn = 0.0;
double AudioIO::mCachedBestRateOut;
enum {
// This is the least positive latency we can
// specify to Pm_OpenOutput, 1 ms, which prevents immediate
// scheduling of events:
MIDI_MINIMAL_LATENCY_MS = 1
};
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
#include "tracks/ui/Scrubbing.h"
@ -1525,6 +1532,7 @@ bool AudioIO::StartPortAudioStream(double sampleRate,
int userData = 24;
int* lpUserData = (captureFormat_saved == int24Sample) ? &userData : NULL;
mMidiTimeCorrection = 0;
mLastPaError = Pa_OpenStream( &mPortStreamV19,
useCapture ? &captureParameters : NULL,
usePlayback ? &playbackParameters : NULL,
@ -1538,6 +1546,10 @@ bool AudioIO::StartPortAudioStream(double sampleRate,
Px_SetInputVolume(mPortMixer, oldRecordVolume);
#endif
if (mPortStreamV19 != NULL && mLastPaError == paNoError) {
auto info = Pa_GetStreamInfo(mPortStreamV19);
if (info)
mMidiTimeCorrection =
info->outputLatency - (MIDI_MINIMAL_LATENCY_MS / 1000.0);
#ifdef __WXMAC__
if (mPortMixer) {
if (Px_SupportsPlaythrough(mPortMixer)) {
@ -2110,7 +2122,6 @@ bool AudioIO::StartPortMidiStream()
if (nTracks == 0)
return false;
mMidiLatency = 1; // arbitrary, but small
//printf("StartPortMidiStream: mT0 %g mTime %g\n",
// gAudioIO->mT0, gAudioIO->mTime);
@ -2141,7 +2152,7 @@ bool AudioIO::StartPortMidiStream()
0,
&::MidiTime,
NULL,
mMidiLatency);
MIDI_MINIMAL_LATENCY_MS);
if (mLastPmError == pmNoError) {
mMidiStreamActive = true;
mMidiPaused = false;
@ -3829,7 +3840,7 @@ void AudioIO::OutputEvent()
// 0.0005 is for rounding
double time = eventTime + 0.0005 -
((mMidiLatency + mSynthLatency) * 0.001);
(mSynthLatency * 0.001);
time += 1; // MidiTime() has a 1s offset
// state changes have to go out without delay because the
@ -4034,17 +4045,25 @@ PmTimestamp AudioIO::MidiTime()
// note: the extra 0.0005 is for rounding. Round down by casting to
// unsigned long, then convert to PmTimeStamp (currently signed)
// PRL: Bug1714 happened because PaUtil_GetTime() and
// mAudioCallbackOutputDacTime could be very widely different, causing
// integer overlows.
// Portaudio documentation says the origin for
// the times passed to audacityAudioCallback is unspecified.
// See long comments at the top of the file for the explanation of this
// calculation; we must use PaUtil_GetTime() here and also in the audio
// callback, to change the origin of times from portaudio, so the diffence of
// now and then is small, as the long comment assumes.
// PRL: the time correction is really Midi latency achieved by different
// means than specifiying it to Pm_OpenStream. The use of the accumulated
// sample count generated by the audio callback (in AudioTime()) might also
// have the virtue of keeping the Midi output synched with audio, even though
// pmlinuxalsa.c does not implement any synchronization of its own.
// We are now using mMidiTimeCorrection, computed once after opening the
// portaudio stream, rather than the difference of dac and current
// times reported to the audio callback; because the answer is very nearly
// the same on Windows and macOs, doing no harm, whereas on Linux with ALSA,
// the dac and current times were not reliable, and that caused irregular
// timing of Midi playback because latency was effectively zero.
auto clockChange = PaUtil_GetTime() - mAudioCallbackClockTime;
auto offset = mAudioCallbackOutputDacTime - mAudioCallbackOutputCurrentTime;
// auto offset = mAudioCallbackOutputDacTime - mAudioCallbackOutputCurrentTime;
auto offset = mMidiTimeCorrection;
return (PmTimestamp) ((unsigned long) (1000 * (
AudioTime() + 1.0005 -
mAudioFramesPerBuffer / mRate +
@ -4283,8 +4302,13 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
#ifdef EXPERIMENTAL_MIDI_OUT
/* GSW: Save timeInfo in case MidiPlayback needs it */
gAudioIO->mAudioCallbackClockTime = PaUtil_GetTime();
// PRL: We no longer trust these numbers on Linux / ALSA:
/*
gAudioIO->mAudioCallbackOutputDacTime = timeInfo->outputBufferDacTime;
gAudioIO->mAudioCallbackOutputCurrentTime = timeInfo->currentTime;
*/
// printf("in callback, mAudioCallbackOutputDacTime %g\n", gAudioIO->mAudioCallbackOutputDacTime); //DBG
gAudioIO->mAudioFramesPerBuffer = framesPerBuffer;
if(gAudioIO->IsPaused())

View File

@ -528,20 +528,27 @@ private:
// MIDI_PLAYBACK:
PmStream *mMidiStream;
PmError mLastPmError;
/// Latency value for PortMidi
long mMidiLatency;
PaTime mMidiTimeCorrection; // seconds
/// Latency of MIDI synthesizer
long mSynthLatency;
long mSynthLatency; // ms
// These fields are used to synchronize MIDI with audio:
/// PortAudio's clock time
volatile double mAudioCallbackClockTime;
/// PortAudio's currentTime -- its origin is unspecified! So that's why
/// we also record the above
/// PRL: no longer using the next two because they are not reliably
/// reported to the audio callback on Linux.
/// Compute the approximate difference of them by other means now:
/// use PaStreamInfo::outputLatency.
/*
/// PortAudio's currentTime -- its origin is unspecified!
volatile double mAudioCallbackOutputCurrentTime;
/// PortAudio's outTime
volatile double mAudioCallbackOutputDacTime;
*/
/// Number of frames output, including pauses
volatile long mNumFrames;
/// How many frames of zeros were output due to pauses?