1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

AudioPlayHead: Improve granularity of position info

This commit is contained in:
reuk 2022-06-13 19:37:49 +01:00
parent 891daf1332
commit 8fbd99c424
No known key found for this signature in database
GPG key ID: 9ADCD339CFC98A11
27 changed files with 924 additions and 572 deletions

View file

@ -103,7 +103,6 @@ struct AudioProcessorHolder
class JuceAU : public AudioProcessorHolder,
public MusicDeviceBase,
public AudioProcessorListener,
public AudioPlayHead,
public AudioProcessorParameter::Listener
{
public:
@ -140,7 +139,6 @@ public:
totalInChannels = juceFilter->getTotalNumInputChannels();
totalOutChannels = juceFilter->getTotalNumOutputChannels();
juceFilter->setPlayHead (this);
juceFilter->addListener (this);
addParameters();
@ -1089,80 +1087,99 @@ public:
return rate > 0 ? juceFilter->getLatencySamples() / rate : 0;
}
//==============================================================================
bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info) override
class ScopedPlayHead : private AudioPlayHead
{
info.timeSigNumerator = 0;
info.timeSigDenominator = 0;
info.editOriginTime = 0;
info.ppqPositionOfLastBarStart = 0;
info.isRecording = false;
info.frameRate = [this]
public:
explicit ScopedPlayHead (JuceAU& juceAudioUnit)
: audioUnit (juceAudioUnit)
{
switch (lastTimeStamp.mSMPTETime.mType)
audioUnit.juceFilter->setPlayHead (this);
}
~ScopedPlayHead() override
{
audioUnit.juceFilter->setPlayHead (nullptr);
}
private:
Optional<PositionInfo> getPosition() const override
{
PositionInfo info;
info.setFrameRate ([this]() -> Optional<FrameRate>
{
case kSMPTETimeType2398: return FrameRate().withBaseRate (24).withPullDown();
case kSMPTETimeType24: return FrameRate().withBaseRate (24);
case kSMPTETimeType25: return FrameRate().withBaseRate (25);
case kSMPTETimeType30Drop: return FrameRate().withBaseRate (30).withDrop();
case kSMPTETimeType30: return FrameRate().withBaseRate (30);
case kSMPTETimeType2997: return FrameRate().withBaseRate (30).withPullDown();
case kSMPTETimeType2997Drop: return FrameRate().withBaseRate (30).withPullDown().withDrop();
case kSMPTETimeType60: return FrameRate().withBaseRate (60);
case kSMPTETimeType60Drop: return FrameRate().withBaseRate (60).withDrop();
case kSMPTETimeType5994: return FrameRate().withBaseRate (60).withPullDown();
case kSMPTETimeType5994Drop: return FrameRate().withBaseRate (60).withPullDown().withDrop();
case kSMPTETimeType50: return FrameRate().withBaseRate (50);
default: break;
switch (audioUnit.lastTimeStamp.mSMPTETime.mType)
{
case kSMPTETimeType2398: return FrameRate().withBaseRate (24).withPullDown();
case kSMPTETimeType24: return FrameRate().withBaseRate (24);
case kSMPTETimeType25: return FrameRate().withBaseRate (25);
case kSMPTETimeType30Drop: return FrameRate().withBaseRate (30).withDrop();
case kSMPTETimeType30: return FrameRate().withBaseRate (30);
case kSMPTETimeType2997: return FrameRate().withBaseRate (30).withPullDown();
case kSMPTETimeType2997Drop: return FrameRate().withBaseRate (30).withPullDown().withDrop();
case kSMPTETimeType60: return FrameRate().withBaseRate (60);
case kSMPTETimeType60Drop: return FrameRate().withBaseRate (60).withDrop();
case kSMPTETimeType5994: return FrameRate().withBaseRate (60).withPullDown();
case kSMPTETimeType5994Drop: return FrameRate().withBaseRate (60).withPullDown().withDrop();
case kSMPTETimeType50: return FrameRate().withBaseRate (50);
default: break;
}
return {};
}());
double ppqPosition = 0.0;
double bpm = 0.0;
if (audioUnit.CallHostBeatAndTempo (&ppqPosition, &bpm) == noErr)
{
info.setPpqPosition (ppqPosition);
info.setBpm (bpm);
}
return FrameRate();
}();
UInt32 outDeltaSampleOffsetToNextBeat;
double outCurrentMeasureDownBeat;
float num;
UInt32 den;
if (CallHostBeatAndTempo (&info.ppqPosition, &info.bpm) != noErr)
{
info.ppqPosition = 0;
info.bpm = 0;
if (audioUnit.CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat,
&num,
&den,
&outCurrentMeasureDownBeat) == noErr)
{
info.setTimeSignature (TimeSignature { (int) num, (int) den });
info.setPpqPositionOfLastBarStart (outCurrentMeasureDownBeat);
}
double outCurrentSampleInTimeLine = 0, outCycleStartBeat = 0, outCycleEndBeat = 0;
Boolean playing = false, looping = false, playchanged;
if (audioUnit.CallHostTransportState (&playing,
&playchanged,
&outCurrentSampleInTimeLine,
&looping,
&outCycleStartBeat,
&outCycleEndBeat) == noErr)
{
info.setIsPlaying (playing);
info.setTimeInSamples ((int64) (outCurrentSampleInTimeLine + 0.5));
info.setTimeInSeconds (*info.getTimeInSamples() / audioUnit.getSampleRate());
info.setIsLooping (looping);
info.setLoopPoints (LoopPoints { outCycleStartBeat, outCycleEndBeat });
}
else
{
// If the host doesn't support this callback, then use the sample time from lastTimeStamp:
outCurrentSampleInTimeLine = audioUnit.lastTimeStamp.mSampleTime;
}
return info;
}
UInt32 outDeltaSampleOffsetToNextBeat;
double outCurrentMeasureDownBeat;
float num;
UInt32 den;
if (CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat, &num, &den,
&outCurrentMeasureDownBeat) == noErr)
{
info.timeSigNumerator = (int) num;
info.timeSigDenominator = (int) den;
info.ppqPositionOfLastBarStart = outCurrentMeasureDownBeat;
}
double outCurrentSampleInTimeLine, outCycleStartBeat = 0, outCycleEndBeat = 0;
Boolean playing = false, looping = false, playchanged;
if (CallHostTransportState (&playing,
&playchanged,
&outCurrentSampleInTimeLine,
&looping,
&outCycleStartBeat,
&outCycleEndBeat) != noErr)
{
// If the host doesn't support this callback, then use the sample time from lastTimeStamp:
outCurrentSampleInTimeLine = lastTimeStamp.mSampleTime;
}
info.isPlaying = playing;
info.timeInSamples = (int64) (outCurrentSampleInTimeLine + 0.5);
info.timeInSeconds = info.timeInSamples / getSampleRate();
info.isLooping = looping;
info.ppqLoopStart = outCycleStartBeat;
info.ppqLoopEnd = outCycleEndBeat;
return true;
}
JuceAU& audioUnit;
};
//==============================================================================
void sendAUEvent (const AudioUnitEventType type, const int juceParamIndex)
{
if (restoringState)
@ -1309,14 +1326,11 @@ public:
jassert (! juceFilter->getHostTimeNs());
if ((inTimeStamp.mFlags & kAudioTimeStampHostTimeValid) != 0)
{
const auto timestamp = timeConversions.hostTimeToNanos (inTimeStamp.mHostTime);
juceFilter->setHostTimeNanos (&timestamp);
}
juceFilter->setHostTimeNanos (timeConversions.hostTimeToNanos (inTimeStamp.mHostTime));
struct AtEndOfScope
{
~AtEndOfScope() { proc.setHostTimeNanos (nullptr); }
~AtEndOfScope() { proc.setHostTimeNanos (nullopt); }
AudioProcessor& proc;
};
@ -1952,6 +1966,7 @@ private:
void processBlock (juce::AudioBuffer<float>& buffer, MidiBuffer& midiBuffer) noexcept
{
const ScopedLock sl (juceFilter->getCallbackLock());
const ScopedPlayHead playhead { *this };
if (juceFilter->isSuspended())
{

View file

@ -519,6 +519,7 @@ public:
{
midiMessages.clear();
lastTimeStamp.mSampleTime = std::numeric_limits<Float64>::max();
lastTimeStamp.mFlags = 0;
}
//==============================================================================
@ -852,7 +853,6 @@ public:
midiMessages.ensureSize (2048);
midiMessages.clear();
zeromem (&lastAudioHead, sizeof (lastAudioHead));
hostMusicalContextCallback = [getAudioUnit() musicalContextBlock];
hostTransportStateCallback = [getAudioUnit() transportStateBlock];
@ -1004,16 +1004,13 @@ public:
}
//==============================================================================
bool getCurrentPosition (CurrentPositionInfo& info) override
Optional<PositionInfo> getPosition() const override
{
bool musicContextCallSucceeded = false;
bool transportStateCallSucceeded = false;
PositionInfo info;
info.setTimeInSamples ((int64) (lastTimeStamp.mSampleTime + 0.5));
info.setTimeInSeconds (*info.getTimeInSamples() / getAudioProcessor().getSampleRate());
info = lastAudioHead;
info.timeInSamples = (int64) (lastTimeStamp.mSampleTime + 0.5);
info.timeInSeconds = info.timeInSamples / getAudioProcessor().getSampleRate();
info.frameRate = [this]
info.setFrameRate ([this]
{
switch (lastTimeStamp.mSMPTETime.mType)
{
@ -1033,7 +1030,7 @@ public:
}
return FrameRate();
}();
}());
double num;
NSInteger den;
@ -1047,17 +1044,14 @@ public:
if (musicalContextCallback (&bpm, &num, &den, &ppqPosition, &outDeltaSampleOffsetToNextBeat, &outCurrentMeasureDownBeat))
{
musicContextCallSucceeded = true;
info.timeSigNumerator = (int) num;
info.timeSigDenominator = (int) den;
info.ppqPositionOfLastBarStart = outCurrentMeasureDownBeat;
info.bpm = bpm;
info.ppqPosition = ppqPosition;
info.setTimeSignature (TimeSignature { (int) num, (int) den });
info.setPpqPositionOfLastBarStart (outCurrentMeasureDownBeat);
info.setBpm (bpm);
info.setPpqPosition (ppqPosition);
}
}
double outCurrentSampleInTimeLine, outCycleStartBeat = 0, outCycleEndBeat = 0;
double outCurrentSampleInTimeLine = 0, outCycleStartBeat = 0, outCycleEndBeat = 0;
AUHostTransportStateFlags flags;
if (hostTransportStateCallback != nullptr)
@ -1066,22 +1060,16 @@ public:
if (transportStateCallback (&flags, &outCurrentSampleInTimeLine, &outCycleStartBeat, &outCycleEndBeat))
{
transportStateCallSucceeded = true;
info.timeInSamples = (int64) (outCurrentSampleInTimeLine + 0.5);
info.timeInSeconds = info.timeInSamples / getAudioProcessor().getSampleRate();
info.isPlaying = ((flags & AUHostTransportStateMoving) != 0);
info.isLooping = ((flags & AUHostTransportStateCycling) != 0);
info.isRecording = ((flags & AUHostTransportStateRecording) != 0);
info.ppqLoopStart = outCycleStartBeat;
info.ppqLoopEnd = outCycleEndBeat;
info.setTimeInSamples ((int64) (outCurrentSampleInTimeLine + 0.5));
info.setTimeInSeconds (*info.getTimeInSamples() / getAudioProcessor().getSampleRate());
info.setIsPlaying ((flags & AUHostTransportStateMoving) != 0);
info.setIsLooping ((flags & AUHostTransportStateCycling) != 0);
info.setIsRecording ((flags & AUHostTransportStateRecording) != 0);
info.setLoopPoints (LoopPoints { outCycleStartBeat, outCycleEndBeat });
}
}
if (musicContextCallSucceeded && transportStateCallSucceeded)
lastAudioHead = info;
return true;
return info;
}
//==============================================================================
@ -1556,15 +1544,12 @@ private:
if (timestamp != nullptr)
{
if ((timestamp->mFlags & kAudioTimeStampHostTimeValid) != 0)
{
const auto convertedTime = timeConversions.hostTimeToNanos (timestamp->mHostTime);
getAudioProcessor().setHostTimeNanos (&convertedTime);
}
getAudioProcessor().setHostTimeNanos (timeConversions.hostTimeToNanos (timestamp->mHostTime));
}
struct AtEndOfScope
{
~AtEndOfScope() { proc.setHostTimeNanos (nullptr); }
~AtEndOfScope() { proc.setHostTimeNanos (nullopt); }
AudioProcessor& proc;
};
@ -1854,7 +1839,6 @@ private:
ObjCBlock<AUHostTransportStateBlock> hostTransportStateCallback;
AudioTimeStamp lastTimeStamp;
CurrentPositionInfo lastAudioHead;
String contextName;