Go bindings for Ableton Link, a technology that synchronizes musical tempo and timing across multiple applications running on one or more devices.
- Complete Go API coverage of Ableton Link functionality
- Link Audio support for sharing audio channels across the network
- Automatic build system with included C library as git submodule
- Thread-safe operations with separate audio/application thread contexts
- Callback support for tempo, peer count, and transport state changes
- Cross-platform support (macOS, Linux, Windows)
- Static linking support for deployment
- Go 1.19 or later
- CMake 3.15 or later
- C++14 compatible compiler (GCC, Clang, or MSVC)
- Git (for submodules)
git clone --recursive https://github.com/DatanoiseTV/abletonlink-go.git
cd abletonlink-gogit clone https://github.com/DatanoiseTV/abletonlink-go.git
cd abletonlink-go
git submodule update --init --recursivego get github.com/DatanoiseTV/abletonlink-gomake # Build everything
make build # Build Go module only
make examples # Build examples./build.sh # Build C library
go generate ./... # Generate bindings
go build ./... # Build Go moduleFor deployments requiring static linking (Linux/Windows):
make build-static
# or
go build -tags static ./...Note: Static linking is not supported on macOS due to system limitations.
package main
import (
"fmt"
"time"
"github.com/DatanoiseTV/abletonlink-go"
)
func main() {
// Create Link instance with 120 BPM
link := abletonlink.NewLink(120.0)
defer link.Destroy()
// Enable Link networking
link.Enable(true)
defer link.Enable(false)
// Set up callbacks
link.SetNumPeersCallback(func(numPeers uint64) {
fmt.Printf("Connected peers: %d\n", numPeers)
})
link.SetTempoCallback(func(tempo float64) {
fmt.Printf("Tempo changed: %.2f BPM\n", tempo)
})
// Create session state for timeline manipulation
state := abletonlink.NewSessionState()
defer state.Destroy()
// Main loop
for {
link.CaptureAppSessionState(state)
currentTime := link.ClockMicros()
beat := state.BeatAtTime(currentTime, 4.0)
tempo := state.Tempo()
fmt.Printf("Beat: %.2f, Tempo: %.2f BPM\n", beat, tempo)
time.Sleep(1 * time.Second)
}
}// Create and destroy Link instances
link := abletonlink.NewLink(bpm float64) *Link
link.Destroy()
// Network control
link.Enable(enable bool)
link.IsEnabled() bool
// Peer information
link.NumPeers() uint64
// Clock access
link.ClockMicros() int64// Create session state
state := abletonlink.NewSessionState() *SessionState
state.Destroy()
// Capture/commit from different thread contexts
link.CaptureAudioSessionState(state) // Audio thread only
link.CommitAudioSessionState(state) // Audio thread only
link.CaptureAppSessionState(state) // Any thread
link.CommitAppSessionState(state) // Any thread// Tempo operations
state.Tempo() float64
state.SetTempo(bpm float64, atTime int64)
// Beat/time conversions
state.BeatAtTime(time int64, quantum float64) float64
state.PhaseAtTime(time int64, quantum float64) float64
state.TimeAtBeat(beat float64, quantum float64) int64
// Beat mapping
state.RequestBeatAtTime(beat float64, time int64, quantum float64)
state.ForceBeatAtTime(beat float64, time uint64, quantum float64)// Transport state
state.IsPlaying() bool
state.SetIsPlaying(isPlaying bool, time uint64)
state.TimeForIsPlaying() uint64
// Convenience functions
state.RequestBeatAtStartPlayingTime(beat float64, quantum float64)
state.SetIsPlayingAndRequestBeatAtTime(isPlaying bool, time uint64, beat float64, quantum float64)// Set callback functions
link.SetNumPeersCallback(func(uint64)) // Peer count changes
link.SetTempoCallback(func(float64)) // Tempo changes
link.SetStartStopCallback(func(bool)) // Transport state changes
link.SetChannelsChangedCallback(func()) // Audio channels updated// Enable audio sharing
link.EnableAudio(true)
link.IsAudioEnabled() bool
// Channel discovery
link.Channels() []Channel
// Creating a Sink (sending audio)
sink := link.NewSink(name string, maxSamples uint64)
buffer := sink.RetainBuffer()
// ... fill buffer.Samples ...
sink.Commit(buffer, state, beats, quantum, frames, channels, rate)
// Creating a Source (receiving audio)
source := link.NewSource(channelID, func(samples []int16, info SourceBufferInfo) {
// Process incoming samples
})Ableton Link provides separate methods for different thread contexts:
-
Audio Thread: Use
CaptureAudioSessionStateandCommitAudioSessionState- Realtime-safe, non-blocking
- Should only be called from audio processing thread
-
Application Thread: Use
CaptureAppSessionStateandCommitAppSessionState- Thread-safe but may block
- Safe to call from any non-audio thread
Do not mix audio and application thread methods concurrently.
Complete examples are available in the examples/ directory:
cd examples
go run basic_link.goA comprehensive example that creates a bidirectional bridge between MIDI clock and Ableton Link:
cd examples
go run midi_bridge.goFeatures:
- Creates virtual MIDI ports ("Link-MIDI Bridge In/Out")
- Bidirectional tempo synchronization (MIDI ↔ Link)
- Transport sync (start/stop/continue) with bar quantization
- Real-time MIDI clock generation based on Link tempo
- Compatible with DAWs, hardware sequencers, and MIDI applications
Streams Link audio channels to an Icecast2 server as MP3:
cd examples/icecast_stream
./build.sh
./icecast_streamFeatures:
- Auto-discovery of Link audio channels
- Real-time MP3 encoding (128kbps)
- Live level meter and buffer status
- Dynamic Icecast metadata updates (BPM and station info)
- Built-in resampler for any input rate (44.1kHz -> 48kHz)
See examples/README.md for detailed usage instructions.
The build system automatically handles the Ableton Link C library:
- Downloads and builds Ableton Link as a git submodule
- Configures CGO to link against the built library
- Generates Go bindings automatically
make deps # Build C dependencies only
make generate # Run go generate
make build # Build Go module
make build-static # Build with static linking (Linux/Windows)
make examples # Build examples
make test # Run tests
make clean # Clean build artifacts
make install # Full build and install- macOS: Full support with dynamic linking
- Linux: Full support with dynamic and static linking
- Windows: Full support with dynamic and static linking
This project follows the same licensing terms as Ableton Link (GPL v2+).
Contributions are welcome. Please ensure all tests pass and follow Go conventions.
For issues specific to these Go bindings, please file an issue on GitHub. For Ableton Link questions, refer to the official Link repository.