/* Copyright 2016, Ableton AG, Berlin. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * If you would like to incorporate Link into a proprietary software application, * please contact . */ #include "AudioEngine.hpp" namespace ableton { namespace link { AudioEngine::AudioEngine(Link& link) : mLink(link) , mSharedEngineData({0., false, false, 4., false}) , mLockfreeEngineData(mSharedEngineData) , mIsPlaying(false) { } void AudioEngine::startPlaying() { std::lock_guard lock(mEngineDataGuard); mSharedEngineData.requestStart = true; } void AudioEngine::stopPlaying() { std::lock_guard lock(mEngineDataGuard); mSharedEngineData.requestStop = true; } bool AudioEngine::isPlaying() const { return mLink.captureAppSessionState().isPlaying(); } double AudioEngine::beatTime() const { const auto sessionState = mLink.captureAppSessionState(); return sessionState.beatAtTime(mLink.clock().micros(), mSharedEngineData.quantum); } void AudioEngine::setTempo(double tempo) { std::lock_guard lock(mEngineDataGuard); mSharedEngineData.requestedTempo = tempo; } double AudioEngine::quantum() const { return mSharedEngineData.quantum; } void AudioEngine::setQuantum(double quantum) { std::lock_guard lock(mEngineDataGuard); mSharedEngineData.quantum = quantum; } bool AudioEngine::isStartStopSyncEnabled() const { return mLink.isStartStopSyncEnabled(); } void AudioEngine::setStartStopSyncEnabled(const bool enabled) { mLink.enableStartStopSync(enabled); } AudioEngine::EngineData AudioEngine::pullEngineData() { auto engineData = EngineData{}; if (mEngineDataGuard.try_lock()) { engineData.requestedTempo = mSharedEngineData.requestedTempo; mSharedEngineData.requestedTempo = 0; engineData.requestStart = mSharedEngineData.requestStart; mSharedEngineData.requestStart = false; engineData.requestStop = mSharedEngineData.requestStop; mSharedEngineData.requestStop = false; mLockfreeEngineData.quantum = mSharedEngineData.quantum; mLockfreeEngineData.startStopSyncOn = mSharedEngineData.startStopSyncOn; mEngineDataGuard.unlock(); } engineData.quantum = mLockfreeEngineData.quantum; return engineData; } void AudioEngine::timelineCallback(const std::chrono::microseconds hostTime, LinkTimeInfo* const info) { const auto engineData = pullEngineData(); auto sessionState = mLink.captureAudioSessionState(); if (engineData.requestStart) { sessionState.setIsPlaying(true, hostTime); } if (engineData.requestStop) { sessionState.setIsPlaying(false, hostTime); } if (!mIsPlaying && sessionState.isPlaying()) { // Reset the timeline so that beat 0 corresponds to the time when transport starts sessionState.requestBeatAtStartPlayingTime(0, engineData.quantum); mIsPlaying = true; } else if (mIsPlaying && !sessionState.isPlaying()) { mIsPlaying = false; } if (engineData.requestedTempo > 0) { // Set the newly requested tempo from the beginning of this buffer sessionState.setTempo(engineData.requestedTempo, hostTime); } // Timeline modifications are complete, commit the results mLink.commitAudioSessionState(sessionState); // Save session state info->beatsPerBar = engineData.quantum; info->beatsPerMinute = sessionState.tempo(); info->beat = sessionState.beatAtTime(hostTime, engineData.quantum); info->phase = sessionState.phaseAtTime(hostTime, engineData.quantum); info->playing = mIsPlaying; } } // namespace link } // namespace ableton