mirror of
https://github.com/cookiengineer/audacity
synced 2025-09-24 16:01:16 +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:
parent
faa7d1828a
commit
582e574ab8
@ -315,6 +315,13 @@ wxArrayLong AudioIO::mCachedSampleRates;
|
|||||||
double AudioIO::mCachedBestRateIn = 0.0;
|
double AudioIO::mCachedBestRateIn = 0.0;
|
||||||
double AudioIO::mCachedBestRateOut;
|
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
|
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||||
|
|
||||||
#include "tracks/ui/Scrubbing.h"
|
#include "tracks/ui/Scrubbing.h"
|
||||||
@ -1525,6 +1532,7 @@ bool AudioIO::StartPortAudioStream(double sampleRate,
|
|||||||
int userData = 24;
|
int userData = 24;
|
||||||
int* lpUserData = (captureFormat_saved == int24Sample) ? &userData : NULL;
|
int* lpUserData = (captureFormat_saved == int24Sample) ? &userData : NULL;
|
||||||
|
|
||||||
|
mMidiTimeCorrection = 0;
|
||||||
mLastPaError = Pa_OpenStream( &mPortStreamV19,
|
mLastPaError = Pa_OpenStream( &mPortStreamV19,
|
||||||
useCapture ? &captureParameters : NULL,
|
useCapture ? &captureParameters : NULL,
|
||||||
usePlayback ? &playbackParameters : NULL,
|
usePlayback ? &playbackParameters : NULL,
|
||||||
@ -1538,6 +1546,10 @@ bool AudioIO::StartPortAudioStream(double sampleRate,
|
|||||||
Px_SetInputVolume(mPortMixer, oldRecordVolume);
|
Px_SetInputVolume(mPortMixer, oldRecordVolume);
|
||||||
#endif
|
#endif
|
||||||
if (mPortStreamV19 != NULL && mLastPaError == paNoError) {
|
if (mPortStreamV19 != NULL && mLastPaError == paNoError) {
|
||||||
|
auto info = Pa_GetStreamInfo(mPortStreamV19);
|
||||||
|
if (info)
|
||||||
|
mMidiTimeCorrection =
|
||||||
|
info->outputLatency - (MIDI_MINIMAL_LATENCY_MS / 1000.0);
|
||||||
#ifdef __WXMAC__
|
#ifdef __WXMAC__
|
||||||
if (mPortMixer) {
|
if (mPortMixer) {
|
||||||
if (Px_SupportsPlaythrough(mPortMixer)) {
|
if (Px_SupportsPlaythrough(mPortMixer)) {
|
||||||
@ -2110,7 +2122,6 @@ bool AudioIO::StartPortMidiStream()
|
|||||||
if (nTracks == 0)
|
if (nTracks == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mMidiLatency = 1; // arbitrary, but small
|
|
||||||
//printf("StartPortMidiStream: mT0 %g mTime %g\n",
|
//printf("StartPortMidiStream: mT0 %g mTime %g\n",
|
||||||
// gAudioIO->mT0, gAudioIO->mTime);
|
// gAudioIO->mT0, gAudioIO->mTime);
|
||||||
|
|
||||||
@ -2141,7 +2152,7 @@ bool AudioIO::StartPortMidiStream()
|
|||||||
0,
|
0,
|
||||||
&::MidiTime,
|
&::MidiTime,
|
||||||
NULL,
|
NULL,
|
||||||
mMidiLatency);
|
MIDI_MINIMAL_LATENCY_MS);
|
||||||
if (mLastPmError == pmNoError) {
|
if (mLastPmError == pmNoError) {
|
||||||
mMidiStreamActive = true;
|
mMidiStreamActive = true;
|
||||||
mMidiPaused = false;
|
mMidiPaused = false;
|
||||||
@ -3829,7 +3840,7 @@ void AudioIO::OutputEvent()
|
|||||||
|
|
||||||
// 0.0005 is for rounding
|
// 0.0005 is for rounding
|
||||||
double time = eventTime + 0.0005 -
|
double time = eventTime + 0.0005 -
|
||||||
((mMidiLatency + mSynthLatency) * 0.001);
|
(mSynthLatency * 0.001);
|
||||||
|
|
||||||
time += 1; // MidiTime() has a 1s offset
|
time += 1; // MidiTime() has a 1s offset
|
||||||
// state changes have to go out without delay because the
|
// 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
|
// note: the extra 0.0005 is for rounding. Round down by casting to
|
||||||
// unsigned long, then convert to PmTimeStamp (currently signed)
|
// 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
|
// 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
|
// 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
|
// callback, to change the origin of times from portaudio, so the diffence of
|
||||||
// now and then is small, as the long comment assumes.
|
// 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 clockChange = PaUtil_GetTime() - mAudioCallbackClockTime;
|
||||||
auto offset = mAudioCallbackOutputDacTime - mAudioCallbackOutputCurrentTime;
|
// auto offset = mAudioCallbackOutputDacTime - mAudioCallbackOutputCurrentTime;
|
||||||
|
auto offset = mMidiTimeCorrection;
|
||||||
return (PmTimestamp) ((unsigned long) (1000 * (
|
return (PmTimestamp) ((unsigned long) (1000 * (
|
||||||
AudioTime() + 1.0005 -
|
AudioTime() + 1.0005 -
|
||||||
mAudioFramesPerBuffer / mRate +
|
mAudioFramesPerBuffer / mRate +
|
||||||
@ -4283,8 +4302,13 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
|
|||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
/* GSW: Save timeInfo in case MidiPlayback needs it */
|
/* GSW: Save timeInfo in case MidiPlayback needs it */
|
||||||
gAudioIO->mAudioCallbackClockTime = PaUtil_GetTime();
|
gAudioIO->mAudioCallbackClockTime = PaUtil_GetTime();
|
||||||
|
|
||||||
|
// PRL: We no longer trust these numbers on Linux / ALSA:
|
||||||
|
/*
|
||||||
gAudioIO->mAudioCallbackOutputDacTime = timeInfo->outputBufferDacTime;
|
gAudioIO->mAudioCallbackOutputDacTime = timeInfo->outputBufferDacTime;
|
||||||
gAudioIO->mAudioCallbackOutputCurrentTime = timeInfo->currentTime;
|
gAudioIO->mAudioCallbackOutputCurrentTime = timeInfo->currentTime;
|
||||||
|
*/
|
||||||
|
|
||||||
// printf("in callback, mAudioCallbackOutputDacTime %g\n", gAudioIO->mAudioCallbackOutputDacTime); //DBG
|
// printf("in callback, mAudioCallbackOutputDacTime %g\n", gAudioIO->mAudioCallbackOutputDacTime); //DBG
|
||||||
gAudioIO->mAudioFramesPerBuffer = framesPerBuffer;
|
gAudioIO->mAudioFramesPerBuffer = framesPerBuffer;
|
||||||
if(gAudioIO->IsPaused())
|
if(gAudioIO->IsPaused())
|
||||||
|
@ -528,20 +528,27 @@ private:
|
|||||||
// MIDI_PLAYBACK:
|
// MIDI_PLAYBACK:
|
||||||
PmStream *mMidiStream;
|
PmStream *mMidiStream;
|
||||||
PmError mLastPmError;
|
PmError mLastPmError;
|
||||||
/// Latency value for PortMidi
|
PaTime mMidiTimeCorrection; // seconds
|
||||||
long mMidiLatency;
|
|
||||||
/// Latency of MIDI synthesizer
|
/// Latency of MIDI synthesizer
|
||||||
long mSynthLatency;
|
long mSynthLatency; // ms
|
||||||
|
|
||||||
// These fields are used to synchronize MIDI with audio:
|
// These fields are used to synchronize MIDI with audio:
|
||||||
|
|
||||||
/// PortAudio's clock time
|
/// PortAudio's clock time
|
||||||
volatile double mAudioCallbackClockTime;
|
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;
|
volatile double mAudioCallbackOutputCurrentTime;
|
||||||
/// PortAudio's outTime
|
/// PortAudio's outTime
|
||||||
volatile double mAudioCallbackOutputDacTime;
|
volatile double mAudioCallbackOutputDacTime;
|
||||||
|
*/
|
||||||
|
|
||||||
/// Number of frames output, including pauses
|
/// Number of frames output, including pauses
|
||||||
volatile long mNumFrames;
|
volatile long mNumFrames;
|
||||||
/// How many frames of zeros were output due to pauses?
|
/// How many frames of zeros were output due to pauses?
|
||||||
|
Loading…
x
Reference in New Issue
Block a user