Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ableton link clock module #1032

Merged
merged 19 commits into from Mar 25, 2020
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -1,3 +1,9 @@
[submodule "third-party/link-c"]
path = third-party/link-c
url = https://github.com/artfwo/link-c.git
[submodule "third-party/link"]
path = third-party/link
url = https://github.com/Ableton/link.git
[submodule "crone/softcut"]
path = crone/softcut
url = https://github.com/monome/softcut-lib.git
@@ -85,6 +85,7 @@ end

clock.INTERNAL = 0
clock.MIDI = 1
clock.LINK = 2

--- select the sync source, currently clock.INTERNAL and clock.MIDI.
-- @tparam integer source : clock.INTERNAL (0) or clock.MIDI (1)
@@ -96,4 +97,9 @@ clock.get_time_beats = function()
return _norns.clock_get_time_beats()
end

clock.get_tempo = function()
return _norns.clock_get_tempo()
end


return clock
@@ -113,6 +113,16 @@ double clock_gettime_beats() {
return this_beat;
}

double clock_get_tempo() {
pthread_mutex_lock(&reference.lock);

double tempo = 60.0 / reference.beat_duration;

pthread_mutex_unlock(&reference.lock);

return tempo;
}

bool clock_schedule_resume_sync(int coro_id, double beats) {
double zero_beat_time;
double this_beat;
@@ -5,6 +5,7 @@
typedef enum {
CLOCK_SOURCE_INTERNAL = 0,
CLOCK_SOURCE_MIDI = 1,
CLOCK_SOURCE_LINK = 2,
} clock_source_t;

void clock_init();
@@ -17,6 +18,7 @@ void clock_cancel_all();

double clock_gettime_beats();
double clock_gettime_secondsf();
double clock_get_tempo();

void clock_cancel(int);
void clock_cancel_coro(int);
@@ -0,0 +1,77 @@
#include <stdlib.h>
#include <stdbool.h>
#include <pthread.h>
#include <stdint.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>

#include <ableton_link.h>

#include "clock.h"

static pthread_t clock_link_thread;
static struct clock_link_shared_data_t {
double quantum;
double requested_tempo;
pthread_mutex_t lock;
} clock_link_shared_data;

static void *clock_link_run(void *p) {
(void) p;

AbletonLink *link;
AbletonLinkClock *clock;
AbletonLinkSessionState *state;

link = ableton_link_new(120);
clock = ableton_link_clock(link);
ableton_link_enable(link, true);

while (true) {
if (pthread_mutex_trylock(&clock_link_shared_data.lock) == 0) {
state = ableton_link_capture_audio_session_state(link);

double link_tempo = ableton_link_session_state_tempo(state);
uint64_t micros = ableton_link_clock_micros(clock);
double link_beat = ableton_link_session_state_beat_at_time(state, micros, clock_link_shared_data.quantum);

clock_update_reference_from(link_beat, 60.0f / link_tempo, CLOCK_SOURCE_LINK);

if (clock_link_shared_data.requested_tempo > 0) {
ableton_link_session_state_set_tempo(state, clock_link_shared_data.requested_tempo, micros);
clock_link_shared_data.requested_tempo = 0;
ableton_link_commit_audio_session_state(link, state);
}

ableton_link_session_state_destroy(state);
pthread_mutex_unlock(&clock_link_shared_data.lock);
}

usleep(1000000 / 100);
}

return NULL;
}

void clock_link_start() {
pthread_attr_t attr;
pthread_attr_init(&attr);

clock_link_shared_data.quantum = 4;
clock_link_shared_data.requested_tempo = 0;

pthread_create(&clock_link_thread, &attr, &clock_link_run, NULL);
}

void clock_link_set_quantum(double quantum) {
pthread_mutex_lock(&clock_link_shared_data.lock);
clock_link_shared_data.quantum = quantum;
pthread_mutex_unlock(&clock_link_shared_data.lock);
}

void clock_link_set_tempo(double tempo) {
pthread_mutex_lock(&clock_link_shared_data.lock);
clock_link_shared_data.requested_tempo = tempo;
pthread_mutex_unlock(&clock_link_shared_data.lock);
}
@@ -0,0 +1,5 @@
#pragma once

void clock_link_start();
void clock_link_set_quantum(double quantum);
void clock_link_set_tempo(double tempo);
@@ -27,6 +27,10 @@
#include "clocks/clock_internal.h"
#include "clocks/clock_midi.h"

#ifdef HAVE_ABLETON_LINK
#include "clocks/clock_link.h"
#endif

#include "oracle.h"
#include "weaver.h"

@@ -71,6 +75,10 @@ int main(int argc, char **argv) {
clock_init();
clock_internal_start();
clock_midi_init();
#ifdef HAVE_ABLETON_LINK
clock_link_start();
#endif

watch_init();

o_init(); // oracle (audio)
@@ -39,6 +39,7 @@
#include "system_cmd.h"
#include "clock.h"
#include "clocks/clock_internal.h"
#include "clocks/clock_link.h"


// registered lua functions require the LVM state as a parameter.
@@ -221,8 +222,11 @@ static int _clock_schedule_sleep(lua_State *l);
static int _clock_schedule_sync(lua_State *l);
static int _clock_cancel(lua_State *l);
static int _clock_internal_set_tempo(lua_State *l);
static int _clock_link_set_tempo(lua_State *l);
static int _clock_link_set_quantum(lua_State *l);
static int _clock_set_source(lua_State *l);
static int _clock_get_time_beats(lua_State *l);
static int _clock_get_tempo(lua_State *l);

// boilerplate: push a function to the stack, from field in global 'norns'
static inline void
@@ -417,8 +421,11 @@ void w_init(void) {
lua_register_norns("clock_schedule_sync", &_clock_schedule_sync);
lua_register_norns("clock_cancel", &_clock_cancel);
lua_register_norns("clock_internal_set_tempo", &_clock_internal_set_tempo);
lua_register_norns("clock_link_set_tempo", &_clock_link_set_tempo);
lua_register_norns("clock_link_set_quantum", &_clock_link_set_quantum);
lua_register_norns("clock_set_source", &_clock_set_source);
lua_register_norns("clock_get_time_beats", &_clock_get_time_beats);
lua_register_norns("clock_get_tempo", &_clock_get_tempo);

// name global extern table
lua_setglobal(lvm, "_norns");
@@ -1414,6 +1421,20 @@ int _clock_internal_set_tempo(lua_State *l) {
return 0;
}

int _clock_link_set_tempo(lua_State *l) {
lua_check_num_args(1);
double bpm = luaL_checknumber(l, 1);
clock_link_set_tempo(bpm);
return 0;
}

int _clock_link_set_quantum(lua_State *l) {
lua_check_num_args(1);
double quantum = luaL_checknumber(l, 1);
clock_link_set_quantum(quantum);
return 0;
}

int _clock_set_source(lua_State *l) {
lua_check_num_args(1);
int source = (int) luaL_checkinteger(l, 1);
@@ -1426,6 +1447,11 @@ int _clock_get_time_beats(lua_State *l) {
return 1;
}

int _clock_get_tempo(lua_State *l) {
lua_pushnumber(l, clock_get_tempo());
return 1;
}

void w_handle_monome_add(void *mdev) {
struct dev_monome *md = (struct dev_monome *)mdev;
int id = md->dev.id;
@@ -29,28 +29,44 @@ def build(bld):
'src/system_cmd.c',
'src/clock.c',
'src/clocks/clock_internal.c',
'src/clocks/clock_midi.c'
'src/clocks/clock_midi.c',
]

matron_includes = [
'src',
'src/device',
'src/hardware',
'lua',
]

matron_libs = [
'pthread',
'm',
]

matron_use = [
'ALSA',
'LIBUDEV',
'LIBEVDEV',
'CAIRO',
'CAIRO-FT',
'LUA53',
'LIBLO',
'LIBMONOME',
'SNDFILE',
'AVAHI-COMPAT-LIBDNS_SD',
]

if bld.env.ENABLE_ABLETON_LINK:
matron_sources += ['src/clocks/clock_link.c']
matron_includes += ['../third-party/link-c']
matron_libs += ['stdc++']
matron_use += ['LIBLINK_C']

bld.program(features='c cprogram',
source=matron_sources,
target='matron',
includes=[
'src',
'src/device',
'src/hardware',
'lua',
], use=[
'ALSA',
'LIBUDEV',
'LIBEVDEV',
'CAIRO',
'CAIRO-FT',
'LUA53',
'LIBLO',
'LIBMONOME',
'SNDFILE',
'AVAHI-COMPAT-LIBDNS_SD',
], lib=[
'pthread', 'm'
], cflags=['-O3', '-Wall'])
includes=matron_includes,
use=matron_use,
lib=matron_libs,
cflags=['-O3', '-Wall'])
Submodule link added at 0b77cc
Submodule link-c added at aebe47
@@ -0,0 +1,25 @@
top = '..'

def build_link(bld):
bld.stlib(features='c cxx cxxstlib',
source=[
'link-c/ableton_link.cpp',
],
target='link-c',
includes=[
'link/include',
'link/modules/asio-standalone/asio/include',
],
cxxflags=[
'-O3',
'-Wall',
'-Wno-multichar',
],
defines=[
'LINK_PLATFORM_LINUX'
],
name='LIBLINK_C')

def build(bld):
if bld.env.ENABLE_ABLETON_LINK:
build_link(bld)
10 wscript
@@ -14,6 +14,7 @@ def options(opt):
opt.load('compiler_c compiler_cxx boost')
opt.add_option('--desktop', action='store_true', default=False)
opt.add_option('--supercollider-prefix', action='store', default='/usr')
opt.add_option('--enable-ableton-link', action='store_true', default=False)

def configure(conf):
conf.load('compiler_c compiler_cxx boost')
@@ -37,7 +38,7 @@ def configure(conf):
conf.check_cfg(package='lua53', args=['--cflags', '--libs'])
conf.check_cfg(package='nanomsg', args=['--cflags', '--libs'])
conf.check_cfg(package='avahi-compat-libdns_sd', args=['--cflags', '--libs'])
conf.check_cfg(package='sndfile', args=['--cflags', '--libs'])
conf.check_cfg(package='sndfile', args=['--cflags', '--libs'])

conf.check_cc(msg='Checking for libmonome',
define_name='HAVE_LIBMONOME',
@@ -54,19 +55,24 @@ def configure(conf):
includes=[
'{}/include/SuperCollider/plugin_interface'.format(conf.env.SC_PREFIX),
'{}/include/SuperCollider/common'.format(conf.env.SC_PREFIX),
'{}/sc/external_libraries/nova-simd'.format(top)
'{}/sc/external_libraries/nova-simd'.format(top)
],
header_name='SC_PlugIn.h',
uselib_store='SUPERCOLLIDER')

conf.check_boost()

if conf.options.desktop:
conf.check_cfg(package='sdl2', args=['--cflags', '--libs'])
conf.define('NORNS_DESKTOP', True)

conf.env.ENABLE_ABLETON_LINK = conf.options.enable_ableton_link
conf.define('HAVE_ABLETON_LINK', conf.options.enable_ableton_link)

def build(bld):
bld.recurse('matron')
bld.recurse('maiden-repl')
bld.recurse('ws-wrapper')
bld.recurse('sc')
bld.recurse('crone')
bld.recurse('third-party')
ProTip! Use n and p to navigate between commits in a pull request.