我设法在我的C ++ DSP中实现了Ableton Link。它在按下开始键时开始计数,并且几乎没有延迟地同步播放。但是,第一次启动和停止之后的任何时间都不再同步。引入了延迟,有时更多,有时更少。我不明白为什么会这样。
此代码正在启动Ableton Link:
void abletonSetup(){
struct mach_timebase_info timeInfo = mach_timebase_info_data_t();
mach_timebase_info(&timeInfo);
ABLLinkRef linkRef = ABLLinkNew(bpm);
EngineData sharedEngineData = EngineData();
EngineData localEngineData = EngineData();
_linkData = LinkData();
_linkData.ablLink = linkRef;
_linkData.sampleRate = sampleRate;
_linkData.secondsToHostTime = (1.0e9 * Float64(timeInfo.denom)) / Float64(timeInfo.numer);
_linkData.sharedEngineData = sharedEngineData;
_linkData.localEngineData = localEngineData;
_linkData.timeAtLastClick = 0;
_linkData.isPlaying = false;
// add listeners
ABLLinkSetIsEnabledCallback(_linkData.ablLink, &AKClockDSPKernel::abletonEnabledListener, this);
ABLLinkSetIsConnectedCallback(_linkData.ablLink, &AKClockDSPKernel::abletonConnectedListener, this);
ABLLinkSetIsStartStopSyncEnabledCallback(_linkData.ablLink, &AKClockDSPKernel::abletonStartStopListener, this);
ABLLinkSetSessionTempoCallback(_linkData.ablLink, &AKClockDSPKernel::abletonBPMListener, this);
// manually update vars:
abletonEnabled = ABLLinkIsEnabled(_linkData.ablLink);
abletonConnected = ABLLinkIsConnected(_linkData.ablLink);
abletonStartStop = ABLLinkIsStartStopSyncEnabled(_linkData.ablLink);
setAbletonActive();
}
此函数计算延迟(从外部C ++调用):
void setAbletonLatency(double value, int bufferSize){
const Float64 hostTicksPerSample = _linkData.secondsToHostTime / sampleRate;
const UInt64 deviceLatency = UInt64(_linkData.secondsToHostTime * value);
const UInt64 bufferInTicks = UInt64( hostTicksPerSample * bufferSize);
// latency introduced by my app:
const UInt64 latencyInTicks = UInt64( hostTicksPerSample * getZtxLatency());
UInt64 outputLatency = deviceLatency + bufferInTicks + latencyInTicks;
os_unfair_lock_lock(&lock);
_linkData.sharedEngineData.outputLatency = outputLatency;
os_unfair_lock_unlock(&lock);
}
在主音频线程的开头调用以下函数:
void abletonUpdate() {
LinkData linkData = _linkData;
const ABLLinkSessionStateRef sessionState = ABLLinkCaptureAudioSessionState(linkData.ablLink);
EngineData engineData;
pullEngineData(&linkData, &engineData);
// The mHostTime member of the timestamp represents the time at
// which the buffer is delivered to the audio hardware. The output
// latency is the time from when the buffer is delivered to the
// audio hardware to when the beginning of the buffer starts
// reaching the output. We add those values to get the host time
// at which the first sample of this buffer will reach the output.
//const UInt64 hostTimeAtBufferBegin = inTimeStamp->mHostTime + engineData.outputLatency;
UInt64 hostTimeAtBufferBegin = mach_absolute_time() + engineData.outputLatency;
if(abletonStartStop == true){
if (engineData.requestStart && !ABLLinkIsPlaying(sessionState)) {
// Request starting playback at the beginning of this buffer.
ABLLinkSetIsPlaying(sessionState, YES, hostTimeAtBufferBegin);
}
if (engineData.requestStop && ABLLinkIsPlaying(sessionState)) {
// Request stopping playback at the beginning of this buffer.
ABLLinkSetIsPlaying(sessionState, NO, hostTimeAtBufferBegin);
}
if (!linkData.isPlaying && ABLLinkIsPlaying(sessionState)) {
// Reset the session state's beat timeline so that the requested
// beat time corresponds to the time the transport will start playing.
// The returned beat time is the actual beat time mapped to the time
// playback will start, which therefore may be less than the requested
// beat time by up to a quantum.
ABLLinkRequestBeatAtStartPlayingTime(sessionState, 0., engineData.quantum);
linkData.isPlaying = YES;
abletonLastCount = 0;
showAbletonCounting();
}else if(linkData.isPlaying && !ABLLinkIsPlaying(sessionState)) {
linkData.isPlaying = NO;
}
}
// Handle a tempo proposal
if (engineData.proposeBpm != INVALID_BPM) {
// Propose that the new tempo takes effect at the beginning of this buffer.
ABLLinkSetTempo(sessionState, engineData.proposeBpm, hostTimeAtBufferBegin);
}
ABLLinkCommitAudioSessionState(linkData.ablLink, sessionState);
_linkData = linkData;
// If Ableton start / stop sync is activated:
if(abletonStartStop == true){
if (linkData.isPlaying) {
float beatTime = ABLLinkBeatAtTime(ABLLinkCaptureAppSessionState(linkData.ablLink), hostTimeAtBufferBegin, engineData.quantum);
float beatQuantum = fmod(engineData.quantum + beatTime, engineData.quantum);
if(started == false && beatQuantum < 0.1){
started = true;
}else if(started == false && beatQuantum >= 0.1){
// code for showing count-in via GUI:
if(beatQuantum >= abletonLastCount + 1){
abletonLastCount = beatQuantum;
linkDidChange(floor(beatQuantum));
}
}
}else{
if(started == true){
stop();
}
}
}
}
任何帮助都受到赞赏。
编辑:
我发现问题出在我的应用程序中。我不得不 每次停止触发时复位内部时钟。所以代码 以上应该是正确的。我会保留原样,也许是这样 会帮助别人的。