Skip to content

Commit

Permalink
basic ableton link support (BespokeSynth#702)
Browse files Browse the repository at this point in the history
  • Loading branch information
awwbees authored Dec 22, 2021
1 parent d4f641a commit 60cbdb7
Show file tree
Hide file tree
Showing 13 changed files with 241 additions and 16 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
[submodule "libs/json/jsoncpp"]
path = libs/json/jsoncpp
url = https://github.com/open-source-parsers/jsoncpp.git
[submodule "libs/link"]
path = libs/link
url = https://github.com/Ableton/link.git
147 changes: 147 additions & 0 deletions Source/AbletonLink.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
bespoke synth, a software modular synthesizer
Copyright (C) 2021 Ryan Challinor (contact: [email protected])
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 3 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 <http://www.gnu.org/licenses/>.
**/
//
// AbletonLink.cpp
//
// Created by Ryan Challinor on 12/9/21.
//
//

#include "AbletonLink.h"
#include "OpenFrameworksPort.h"
#include "Transport.h"
#include "ModularSynth.h"
#include "UIControlMacros.h"

#include "juce_audio_devices/juce_audio_devices.h"

#include "ableton/Link.hpp"
#include "ableton/link/Timeline.hpp"
#include "ableton/link/HostTimeFilter.hpp"

static ableton::link::HostTimeFilter<ableton::link::platform::Clock> sHostTimeFilter;

AbletonLink::AbletonLink()
{
sHostTimeFilter.reset();

mNumPeers = 0;

mTempo = TheTransport->GetTempo();

mLink = std::make_unique<ableton::Link>(mTempo);

mLink->enable(true);

mLink->setNumPeersCallback([this](std::size_t p)
{
mNumPeers = p;
});

mLink->setTempoCallback([this](const double bpm)
{
TheTransport->SetTempo(bpm);
mTempo = bpm;
});
}

void AbletonLink::Init()
{
IDrawableModule::Init();

TheTransport->AddAudioPoller(this);
}

AbletonLink::~AbletonLink()
{
TheTransport->RemoveAudioPoller(this);
}

void AbletonLink::CreateUIControls()
{
IDrawableModule::CreateUIControls();
UIBLOCK0();
FLOATSLIDER(mOffsetMsSlider, "offset ms", &mOffsetMs, -1000, 1000);
ENDUIBLOCK(mWidth, mHeight);

mHeight = 80;
}

void AbletonLink::Poll()
{
}

void AbletonLink::OnTransportAdvanced(float amount)
{
if (mEnabled && mLink.get() != nullptr)
{
if (mTempo != TheTransport->GetTempo())
{
mTempo = TheTransport->GetTempo();
auto sessionState = mLink->captureAudioSessionState();
sessionState.setTempo(mTempo, mLink->clock().micros());
mLink->commitAudioSessionState(sessionState);
}

auto& deviceManager = TheSynth->GetAudioDeviceManager();
if (deviceManager.getCurrentAudioDevice() != nullptr)
{
auto sampleRate = deviceManager.getCurrentAudioDevice()->getCurrentSampleRate();
auto outputLatencySamples = deviceManager.getCurrentAudioDevice()->getOutputLatencyInSamples();
auto bufferSize = deviceManager.getCurrentAudioDevice()->getCurrentBufferSizeSamples();

const auto hostTimeUs = sHostTimeFilter.sampleTimeToHostTime(mSampleTime);
const auto outputLatencyUs = std::chrono::microseconds{ std::llround(1.0e6 * bufferSize / sampleRate) };
auto offsetUs = std::chrono::microseconds{ llround(mOffsetMs * 1000) };

auto adjustedTimeUs = hostTimeUs + outputLatencyUs + offsetUs;

auto sessionState = mLink->captureAudioSessionState();

double quantum = TheTransport->GetTimeSigTop();
mLastReceivedBeat = sessionState.beatAtTime(adjustedTimeUs, quantum);

TheTransport->SetMeasureTime(mLastReceivedBeat / quantum);


// Timeline modifications are complete, commit the results
//mLink->commitAudioSessionState(sessionState);

mSampleTime += gBufferSize;
}
}
}

void AbletonLink::DrawModule()
{
if (Minimized() || IsVisible() == false)
return;

mOffsetMsSlider->Draw();

DrawTextNormal("peers: " + ofToString(mNumPeers) + "\ntempo: " + ofToString(mTempo) + "\nbeat: " + ofToString(mLastReceivedBeat), 3, 40);
}

void AbletonLink::CheckboxUpdated(Checkbox *checkbox)
{
}

void AbletonLink::FloatSliderUpdated(FloatSlider* slider, float oldVal)
{
}

76 changes: 76 additions & 0 deletions Source/AbletonLink.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
bespoke synth, a software modular synthesizer
Copyright (C) 2021 Ryan Challinor (contact: [email protected])
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 3 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 <http://www.gnu.org/licenses/>.
**/
//
// AbletonLink.h
//
// Created by Ryan Challinor on 12/9/21.
//
//

#pragma once

#include <iostream>

#include "IDrawableModule.h"
#include "Checkbox.h"
#include "IAudioPoller.h"
#include "Slider.h"

namespace ableton
{
class Link;
}

class AbletonLink : public IDrawableModule, public IAudioPoller, public IFloatSliderListener
{
public:
AbletonLink();
virtual ~AbletonLink();
static IDrawableModule* Create() { return new AbletonLink(); }

void Init() override;
void CreateUIControls() override;
void Poll() override;

void OnTransportAdvanced(float amount) override;

void SetEnabled(bool enabled) override { mEnabled = enabled; }

void CheckboxUpdated(Checkbox* checkbox) override;
void FloatSliderUpdated(FloatSlider* slider, float oldVal) override;

private:
//IDrawableModule
void DrawModule() override;
void GetModuleDimensions(float& width, float& height) override { width = mWidth; height = mHeight; }
bool Enabled() const override { return mEnabled; }

float mWidth;
float mHeight;

float mOffsetMs{ 0 };
FloatSlider* mOffsetMsSlider;

std::unique_ptr<ableton::Link> mLink;
double mTempo = 120.0;
std::size_t mNumPeers = 0;
double mLastReceivedBeat{ 0 };
double mSampleTime{ 0 };
};


3 changes: 1 addition & 2 deletions Source/BeatBloks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -967,8 +967,7 @@ void BeatBloks::CheckboxUpdated(Checkbox *checkbox)
{
if (mSample)
mSample->Reset();
TheTransport->SetMeasure(0);
TheTransport->SetMeasurePos(0);
TheTransport->SetMeasureTime(0);
}
if (checkbox == mPlayRemixCheckbox)
{
Expand Down
3 changes: 3 additions & 0 deletions Source/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
include(cmake/lib.cmake)
include(cmake/versiontools.cmake)
include(../libs/link/AbletonLinkConfig.cmake)

juce_add_gui_app(BespokeSynth
PRODUCT_NAME BespokeSynth
Expand All @@ -16,6 +17,7 @@ juce_add_gui_app(BespokeSynth
bespoke_buildtime_version_info(BespokeSynth)

target_sources(BespokeSynth PRIVATE
AbletonLink.cpp
ADSR.cpp
ADSRDisplay.cpp
Amplifier.cpp
Expand Down Expand Up @@ -467,6 +469,7 @@ target_link_libraries(BespokeSynth PRIVATE

$<$<STREQUAL:${BESPOKE_SYSTEM_TUNING_LIBRARY},OFF>:tuning-library>
oddsound-mts
Ableton::Link

juce::juce_audio_basics
juce::juce_audio_devices
Expand Down
3 changes: 1 addition & 2 deletions Source/ControllingSong.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,7 @@ void ControllingSong::Process(double time)
int measure;
float measurePos;
mMidiReader.GetMeasurePos(ms, measure, measurePos);
TheTransport->SetMeasure(measure);
TheTransport->SetMeasurePos(measurePos);
TheTransport->SetMeasureTime(measure + measurePos);
}

gWorkChannelBuffer.SetNumActiveChannels(1);
Expand Down
9 changes: 3 additions & 6 deletions Source/LooperRecorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -634,8 +634,7 @@ void LooperRecorder::ButtonClicked(ClickButton* button)
pos *= 2;
count += int(pos);
pos -= int(pos);
TheTransport->SetMeasurePos(pos);
TheTransport->SetMeasure(count);
TheTransport->SetMeasureTime(count + pos);
for (int i = 0; i < mLoopers.size(); ++i)
{
if (mLoopers[i])
Expand All @@ -657,8 +656,7 @@ void LooperRecorder::ButtonClicked(ClickButton* button)
pos /= 2;
count += int(pos);
pos -= int(pos);
TheTransport->SetMeasurePos(pos);
TheTransport->SetMeasure(count);
TheTransport->SetMeasureTime(count + pos);
for (int i = 0; i < mLoopers.size(); ++i)
{
if (mLoopers[i])
Expand Down Expand Up @@ -689,10 +687,9 @@ void LooperRecorder::ButtonClicked(ClickButton* button)
int newMeasure = int(TheTransport->GetMeasure(gTime)+TheTransport->GetMeasurePos(gTime)-.5f);
if (newMeasure < 0)
newMeasure = 7;
TheTransport->SetMeasure(newMeasure);
float newMeasurePos = TheTransport->GetMeasurePos(gTime) - .5f;
FloatWrap(newMeasurePos,1);
TheTransport->SetMeasurePos(newMeasurePos);
TheTransport->SetMeasureTime(newMeasure + newMeasurePos);
}

if (button == mShiftDownbeatButton)
Expand Down
2 changes: 2 additions & 0 deletions Source/ModuleFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@
#include "NoteExpressionRouter.h"
#include "NoteToggle.h"
#include "NoteTable.h"
#include "AbletonLink.h"

#include <juce_core/juce_core.h>

Expand Down Expand Up @@ -442,6 +443,7 @@ ModuleFactory::ModuleFactory()
REGISTER(FloatSliderLFOControl, lfo, kModuleType_Other);
REGISTER(NoteToggle, notetoggle, kModuleType_Other);
REGISTER(NoteTable, notetable, kModuleType_Note);
REGISTER(AbletonLink, abletonlink, kModuleType_Other);

//REGISTER_EXPERIMENTAL(MidiPlayer, midiplayer, kModuleType_Instrument);
REGISTER_HIDDEN(Autotalent, autotalent, kModuleType_Audio);
Expand Down
1 change: 1 addition & 0 deletions Source/RollingBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ void RollingBuffer::LoadState(FileStreamIn& in)
for (int i=0; i<channels; ++i)
{
in >> mOffsetToNow[i];
mOffsetToNow[i] %= Size();
if (savedSize <= Size())
{
in.Read(mBuffer.GetChannel(i), savedSize);
Expand Down
4 changes: 1 addition & 3 deletions Source/SampleCanvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,7 @@ void SampleCanvas::OnClicked(int x, int y, bool right)
if (y >= 0 && y < canvasY)
{
float pos = float(x - canvasX)/mCanvas->GetWidth() * mCanvas->GetNumCols();
int measure = int(pos);
TheTransport->SetMeasure(measure);
TheTransport->SetMeasurePos(pos - measure);
TheTransport->SetMeasureTime(pos);
}*/
}

Expand Down
3 changes: 1 addition & 2 deletions Source/TimelineControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ void TimelineControl::FloatSliderUpdated(FloatSlider* slider, float oldVal)
{
if (slider == mTimeSlider)
{
TheTransport->SetMeasure(int(mTime));
TheTransport->SetMeasurePos(mTime - int(mTime));
TheTransport->SetMeasureTime(mTime);
}
}

Expand Down
2 changes: 1 addition & 1 deletion Source/Transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class Transport : public IDrawableModule, public IButtonListener, public IFloatS
double GetDuration(NoteInterval interval);
int GetQuantized(double time, const TransportListenerInfo* listenerInfo, double* remainderMs = nullptr);
double GetMeasurePos(double time) const { return fmod(GetMeasureTime(time), 1); }
void SetMeasurePos(double pos) { mMeasureTime = mMeasureTime - floor(mMeasureTime) + pos; }
void SetMeasureTime(double measureTime) { mMeasureTime = measureTime; }
int GetMeasure(double time) const { return (int)floor(GetMeasureTime(time)); }
double GetMeasureTime(double time) const { return mMeasureTime + (time - gTime) / MsPerBar(); }
void SetMeasure(int count) { mMeasureTime = mMeasureTime - (int)mMeasureTime + count; }
Expand Down
1 change: 1 addition & 0 deletions libs/link
Submodule link added at a4e4c2

0 comments on commit 60cbdb7

Please sign in to comment.