How to Integrate Ableton Link with AudioKit using Swift or C++

ableton

Ableton Link is a great technology for syncing iOS audio apps with each other as well as Ableton Live. A link session tracks the beat, bar, and phrase according to tempo so that apps can play in time with a shared tempo.

LinkKit is a C++ library with functions for creating and joining a Link session, getting the beat for the current time, updating the tempo etc. from both the audio thread and UI thread. The best practice is creating it once when you initialize your app with your audio engine and tracking the beat or requesting session updates in the audio render loop. Alternatively, you can setup your custom timer to act like an audio render loop. But, it does not guarantee precise accuracy.

I recommend reading the Link documentation for an in-depth overview before we start.

Getting LinkKit

You need to request LinkKit from Ableton and they will give you access to the private repo of LinkKit where you can download the final release with UI guidelines and a well-built example app called LinkHut. You can study the example app. If you have no experience with AudioUnits, Audio DSP programming, mixing Objective-C with C++ (Objective-C++), this example will help. Don’t worry if you are a Swift programmer, it works perfectly well with Swift apps. Also, you can test your implementation with the LinkHut to check if your app is in sync with other Link apps.

AudioKit Integration

Ableton Link works great with AudioKit. If you have an Audio Unit where you can subscribe its render loop (the audio thread), you are basically ready to go. Also, it doesn’t hurt to add one if you don’t currently have one. Otherwise, you can setup your custom loop with the NSTimer, for example, if you are using AudioKit for just the AKSequencer.

Implementation

There are two main data structures you will work with. An engine data, where the audio engine related data goes, like, output latency, bpm, quantum etc.

And Link data, the data you need in audio thread. It will have the ABLLinkRef, the Link itself and two EngineData references one is the audio thread only data and other is shared between main and audio thread so changes on EngineData from different threads don’t block each other. Also, it has time-related data for calculating the beats accurate as possible.

In audio render loop, we sync sharedEngineData to localEngineData with the static void pullEngineData(LinkData* linkData, EngineData* output) function. So, tempo or play/stop state changes applied on audio thread.

And in the last part, in our DSP code, or audio render loop, we pull the Link data, capture the current session, check the tempo, play/stop changes and commit any requested changes by our client to the session.

When we setup our audio render loop function to audio unit, we should send our LinkData. So, we can pull it from void *inRefCon parameter in our audioCallback function.

Passing custom data

You can pass your custom data type as well, if you want to do something else. For example, you can setup a custom callback function like:

And setup a property for implementing it in another class.

In your audio engine’s header file. Also, you should setup a custom struct for passing it to your audio callback function.

Create a private reference for it in your implementation file.

And pass it to the audio callback method.

Then, in your audio callback function, pull it from void *inRefCon.

You can pass the current beat to your custom callback function now.

You can find the full example in this repo.

Integrating with Swift projects

For Swift projects, you need a bridging header file and include the LinkKit.

Also, you need to link libc++.tbd to your linked frameworks and libraries section in the General tab in your project settings.

There is a Swift port of EngineData and LinkData as Swift structs that you can grab from here.

In this example, we will setup a custom timer and use it as our audio render loop. So, it will cover the projects with no audio units. The update function in the Swift port code is the same code in the audio render loop above. So, our custom timer should call it.

If you subscribe its listeners, you are going the react tempo/start/stop changes. And of course, you can request the change them as well, by simply setting its properties.

For more information on Ableton link, please visit their website.
(Blog photo courtesy of Ableton Link website)

Related Posts

Comments (1)

I’m trying to install the full example, but getting this error:

Undefined symbols for architecture x86_64:
“_ABLLinkSetIsPlaying”, referenced from:
audioCallback(void*, unsigned int*, AudioTimeStamp const*, unsigned int, unsigned int, AudioBufferList*) in AudioEngine.o
“_OBJC_CLASS_$_ABLLinkSettingsViewController”, referenced from:
objc-class-ref in AudioEngineManager.o
“_ABLLinkRequestBeatAtStartPlayingTime”, referenced from:
audioCallback(void*, unsigned int*, AudioTimeStamp const*, unsigned int, unsigned int, AudioBufferList*) in AudioEngine.o
“_ABLLinkCommitAudioSessionState”, referenced from:
audioCallback(void*, unsigned int*, AudioTimeStamp const*, unsigned int, unsigned int, AudioBufferList*) in AudioEngine.o
“_ABLLinkSetSessionTempoCallback”, referenced from:
-[AudioEngine initLinkData:] in AudioEngine.o
“_ABLLinkCaptureAudioSessionState”, referenced from:
audioCallback(void*, unsigned int*, AudioTimeStamp const*, unsigned int, unsigned int, AudioBufferList*) in AudioEngine.o
“_ABLLinkIsPlaying”, referenced from:
-[AudioEngine isPlaying] in AudioEngine.o
audioCallback(void*, unsigned int*, AudioTimeStamp const*, unsigned int, unsigned int, AudioBufferList*) in AudioEngine.o
“_ABLLinkGetTempo”, referenced from:
-[AudioEngine bpm] in AudioEngine.o

Leave a Reply