Skip to content

Commit 32beecd

Browse files
committed
Link - further work improving and optimising
* Reduce the amount of communication required between Spider and Tau for timing information (this enables much higher BPMs to work without issue) * Add API plumbing for enabling/disabling link in addition to setting the BPM (this requires an update to sp_midi to work correctly) * Handle changing BPM to and from :link mode with the same logic for both use_bpm and bpm_sync. * Modify tau server API to receive API messages from other Erlang processes in addition to the current route via UDP/OSC (this should help in making it easier to implement a general way to handle scheduled OSC bundles). Note: currently all time checks are disabled - this needs to be addressed and fixed before a release can be made.
1 parent 44b97f2 commit 32beecd

File tree

7 files changed

+224
-187
lines changed

7 files changed

+224
-187
lines changed

app/server/erlang/tau/src/tau_server/tau_server_api.erl

Lines changed: 125 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -106,91 +106,110 @@ init(Parent, CueServer, MIDIServer, LinkServer) ->
106106

107107
loop(State) ->
108108
receive
109+
{timeout, Timer, {call, Server, Msg, Tracker}} ->
110+
Server ! Msg,
111+
tau_server_tracker:forget(Timer, Tracker),
112+
?MODULE:loop(State);
113+
109114
{udp, APISocket, Ip, Port, Bin} ->
110115
debug(3, "api server got UDP on ~p:~p~n", [Ip, Port]),
111-
try osc:decode(Bin) of
112-
{bundle, Time, X} ->
113-
debug("got bundle for time ~f~n", [Time]),
114-
NewState = do_bundle(Time, X, State),
115-
?MODULE:loop(NewState);
116+
case osc:decode(Bin) of
116117
{cmd, ["/ping"]} ->
117118
debug("sending! /pong to ~p ~p ~n", [Ip, Port]),
118119
PongBin = osc:encode(["/pong"]),
119120
ok = gen_udp:send(APISocket, Ip, Port, PongBin),
120121
?MODULE:loop(State);
121-
{cmd, ["/midi", OSC]=Cmd} ->
122-
debug_cmd(Cmd),
123-
MIDIServer = maps:get(midi_server, State),
124-
MIDIServer ! {send, OSC},
125-
?MODULE:loop(State);
126-
{cmd, ["/midi-flush"]=Cmd} ->
127-
debug_cmd(Cmd),
128-
MIDIServer = maps:get(midi_server, State),
129-
MIDIServer ! {flush},
130-
?MODULE:loop(State);
131-
{cmd, ["/flush", Tag]=Cmd} ->
132-
debug_cmd(Cmd),
133-
{Tracker, NewState} = tracker_pid(Tag, State),
134-
tau_server_tracker:flush(all, Tracker),
135-
?MODULE:loop(NewState);
136-
{cmd, ["/internal-cue-port", Flag]=Cmd} ->
137-
debug_cmd(Cmd),
138-
send_to_cue({internal, Flag =:= 1}, State),
139-
?MODULE:loop(State);
140-
{cmd, ["/stop-start-cue-server", Flag]=Cmd} ->
141-
debug_cmd(Cmd),
142-
send_to_cue({enabled, Flag =:= 1}, State),
143-
?MODULE:loop(State);
144-
{cmd, ["/stop-start-midi-cues", Flag]=Cmd} ->
145-
debug_cmd(Cmd),
146-
send_to_cue({midi_enabled, Flag =:= 1}, State),
147-
?MODULE:loop(State);
148-
{cmd, ["/api-rpc", UUID, "/link-get-current-time"]=Cmd} ->
149-
debug_cmd(Cmd),
150-
send_to_link({link_rpc, UUID, get_current_time}, State),
151-
?MODULE:loop(State);
152-
153-
{cmd, ["/api-rpc", UUID, "/link-get-beat-at-time", Time, Quantum]=Cmd} ->
154-
debug_cmd(Cmd),
155-
send_to_link({link_rpc, UUID, get_beat_at_time, Time, Quantum}, State),
156-
?MODULE:loop(State);
157-
158-
{cmd, ["/api-rpc", UUID, "/link-get-time-at-beat", Beat, Quantum]=Cmd} ->
159-
debug_cmd(Cmd),
160-
send_to_link({link_rpc, UUID, get_time_at_beat, Beat, Quantum}, State),
161-
?MODULE:loop(State);
162-
163-
{cmd, ["/api-rpc", UUID, "/link-get-tempo"]=Cmd} ->
164-
debug_cmd(Cmd),
165-
send_to_link({link_rpc, UUID, get_tempo}, State),
166-
?MODULE:loop(State);
167-
168-
{cmd, ["/api-rpc", UUID, "/link-get-num-peers"]=Cmd} ->
169-
debug_cmd(Cmd),
170-
send_to_link({link_rpc, UUID, get_num_peers}, State),
171-
?MODULE:loop(State);
172-
173-
174-
{cmd, ["/link-disable"]=Cmd} ->
175-
debug_cmd(Cmd),
176-
send_to_link({link_disable}, State),
177-
?MODULE:loop(State);
178-
179-
{cmd, ["/link-enable"]=Cmd} ->
180-
debug_cmd(Cmd),
181-
send_to_link({link_enable}, State),
182-
?MODULE:loop(State);
183-
122+
Any -> self() ! Any
123+
end,
124+
?MODULE:loop(State);
125+
126+
{bundle, Time, X} ->
127+
debug("got bundle for time ~f~n", [Time]),
128+
NewState = do_bundle(Time, X, State),
129+
?MODULE:loop(NewState);
130+
131+
{cmd, ["/midi", OSC]=Cmd} ->
132+
debug_cmd(Cmd),
133+
MIDIServer = maps:get(midi_server, State),
134+
MIDIServer ! {send, OSC},
135+
?MODULE:loop(State);
136+
137+
{cmd, ["/midi-flush"]=Cmd} ->
138+
debug_cmd(Cmd),
139+
MIDIServer = maps:get(midi_server, State),
140+
MIDIServer ! {flush},
141+
?MODULE:loop(State);
142+
143+
{cmd, ["/flush", Tag]=Cmd} ->
144+
debug_cmd(Cmd),
145+
{Tracker, NewState} = tracker_pid(Tag, State),
146+
tau_server_tracker:flush(all, Tracker),
147+
?MODULE:loop(NewState);
148+
149+
{cmd, ["/internal-cue-port", Flag]=Cmd} ->
150+
debug_cmd(Cmd),
151+
send_to_cue({internal, Flag =:= 1}, State),
152+
?MODULE:loop(State);
153+
154+
{cmd, ["/stop-start-cue-server", Flag]=Cmd} ->
155+
debug_cmd(Cmd),
156+
send_to_cue({enabled, Flag =:= 1}, State),
157+
?MODULE:loop(State);
158+
159+
{cmd, ["/stop-start-midi-cues", Flag]=Cmd} ->
160+
debug_cmd(Cmd),
161+
send_to_cue({midi_enabled, Flag =:= 1}, State),
162+
?MODULE:loop(State);
163+
164+
{cmd, ["/api-rpc", UUID, "/link-get-current-time"]=Cmd} ->
165+
debug_cmd(Cmd),
166+
send_to_link({link_rpc, UUID, get_current_time}, State),
167+
?MODULE:loop(State);
168+
169+
{cmd, ["/api-rpc", UUID, "/link-get-beat-at-time", Time, Quantum]=Cmd} ->
170+
debug_cmd(Cmd),
171+
send_to_link({link_rpc, UUID, get_beat_at_time, Time, Quantum}, State),
172+
?MODULE:loop(State);
173+
174+
{cmd, ["/api-rpc", UUID, "/link-get-time-at-beat", Beat, Quantum]=Cmd} ->
175+
debug_cmd(Cmd),
176+
send_to_link({link_rpc, UUID, get_time_at_beat, Beat, Quantum}, State),
177+
?MODULE:loop(State);
178+
179+
{cmd, ["/api-rpc", UUID, "/link-get-tempo"]=Cmd} ->
180+
debug_cmd(Cmd),
181+
send_to_link({link_rpc, UUID, get_tempo}, State),
182+
?MODULE:loop(State);
183+
184+
{cmd, ["/api-rpc", UUID, "/link-get-num-peers"]=Cmd} ->
185+
debug_cmd(Cmd),
186+
send_to_link({link_rpc, UUID, get_num_peers}, State),
187+
?MODULE:loop(State);
188+
189+
{cmd, ["/api-rpc", UUID, "/link-is-enabled"]=Cmd} ->
190+
debug_cmd(Cmd),
191+
send_to_link({link_rpc, UUID, is_enabled}, State),
192+
?MODULE:loop(State);
193+
194+
{cmd, ["/link-reset"]=Cmd} ->
195+
debug_cmd(Cmd),
196+
send_to_link({link_reset}, State),
197+
?MODULE:loop(State);
198+
199+
{cmd, ["/link-disable"]=Cmd} ->
200+
debug_cmd(Cmd),
201+
send_to_link({link_disable}, State),
202+
?MODULE:loop(State);
203+
204+
{cmd, ["/link-enable"]=Cmd} ->
205+
debug_cmd(Cmd),
206+
send_to_link({link_enable}, State),
207+
?MODULE:loop(State);
208+
209+
{cmd, Cmd} ->
210+
log("Unknown OSC command:: ~p~n", [Cmd]),
211+
?MODULE:loop(State);
184212

185-
{cmd, Cmd} ->
186-
log("Unknown command:: ~p~n", [Cmd]),
187-
?MODULE:loop(State)
188-
catch
189-
Class:Term:Trace ->
190-
log("Error decoding OSC: ~p~n~p:~p~n~p~n",
191-
[Bin, Class, Term, Trace]),
192-
?MODULE:loop(State)
193-
end;
194213
{system, From, Request} ->
195214
%% handling system messages (like a gen_server does)
196215
sys:handle_system_msg(Request, From,
@@ -218,14 +237,18 @@ debug_cmd([Cmd|Args]) ->
218237
do_bundle(Time, [{_,Bin}|T], State) ->
219238
NewState =
220239
try osc:decode(Bin) of
221-
{cmd, ["/send-after", Host, Port , OSC]} ->
222-
schedule_cmd("default", Time, Host, Port, OSC, State);
240+
{cmd, ["/send-after", Host, Port, OSC]} ->
241+
schedule_cmd(Time, "default", State, {send_osc, Host, Port, OSC});
223242
{cmd, ["/send-after-tagged", Tag, Host, Port, OSC]} ->
224-
schedule_cmd(Tag, Time, Host, Port, OSC, State);
225-
{cmd, ["/midi-at", Cmd]} ->
226-
schedule_midi("default", Time, Cmd, State);
227-
{cmd, ["/midi-at-tagged", Tag, Cmd]} ->
228-
schedule_midi(Tag, Time, Cmd, State);
243+
schedule_cmd(Time, Tag, State, {send_osc, Host, Port, OSC});
244+
{cmd, ["/midi-at", MIDI]} ->
245+
schedule_midi(Time, "default", State, {send_midi, MIDI});
246+
{cmd, ["/midi-at-tagged", Tag, MIDI]} ->
247+
schedule_midi(Time, Tag, State, {send_midi, MIDI});
248+
{cmd, ["/link-set-tempo", Tempo]} ->
249+
schedule_link(Time, "default", State, {link_set_tempo, Tempo});
250+
{cmd, ["/link-set-tempo-tagged", Tag, Tempo]} ->
251+
schedule_link(Time, Tag, State, {link_set_tempo, Tempo});
229252
Other ->
230253
log("Unexpected bundle content:~p~n", [Other]),
231254
State
@@ -239,46 +262,37 @@ do_bundle(Time, [{_,Bin}|T], State) ->
239262
do_bundle(_Time, [], State) ->
240263
State.
241264

242-
schedule_midi(Tag, Time, Data, State) ->
243-
{Tracker, NewState} = tracker_pid(Tag, State),
265+
schedule_internal_call(Time, Tag, State, Server, Msg) ->
244266
Delay = Time - osc:now(),
245267
MsDelay = trunc(Delay*1000+0.5), %% nearest
246-
MIDIServer = maps:get(midi_server, State),
268+
{Tracker, NewState} = tracker_pid(Tag, State),
247269
if MsDelay > ?NODELAY_LIMIT ->
248-
Msg = {send, Time, Data, Tracker},
270+
249271
%% Note: lookup of the registered server name will happen
250272
%% when the timer triggers, and if no such process exists
251273
%% at that time, the message will be quietly dropped
252-
Timer = erlang:start_timer(MsDelay, MIDIServer, Msg),
274+
SchedMsg = {call, Server, Msg, Tracker},
275+
Timer = erlang:start_timer(MsDelay, self(), SchedMsg),
253276
debug(2, "start (MIDI) timer of ~w ms for time ~f~n", [MsDelay, Time]),
254277
tau_server_tracker:track(Timer, Time, Tracker);
255278
true ->
256-
MIDIServer ! {send, Time, Data},
257-
debug(2, "directly forward (MIDI) message for delay ~f~n", [Delay])
279+
Server ! Msg,
280+
debug(2, "Directly sent scheduled call~n", [])
258281
end,
259282
NewState.
260283

261284

262-
%% Schedules a command for forwarding (or forwards immediately)
263-
schedule_cmd(Tag, Time, Host, Port, OSC, State) ->
264-
{Tracker, NewState} = tracker_pid(Tag, State),
265-
Data = {Host, Port, OSC},
266-
Delay = Time - osc:now(),
267-
MsDelay = trunc(Delay*1000+0.5), %% nearest
268-
if MsDelay > ?NODELAY_LIMIT ->
269-
Msg = {forward, Time, Data, Tracker},
270-
%% Note: lookup of the registered server name will happen
271-
%% when the timer triggers, and if no such process exists
272-
%% at that time, the message will be quietly dropped
273-
CueServer = maps:get(cue_server, State),
274-
Timer = erlang:start_timer(MsDelay, CueServer, Msg),
275-
debug(2, "start timer of ~w ms for time ~f~n", [MsDelay, Time]),
276-
tau_server_tracker:track(Timer, Time, Tracker);
277-
true ->
278-
send_to_cue({forward, Time, Data}, NewState),
279-
debug(2, "directly forward message for delay ~f~n", [Delay])
280-
end,
281-
NewState.
285+
schedule_link(Time, Tag, State, Msg) ->
286+
LinkServer = maps:get(link_server, State),
287+
schedule_internal_call(Time, Tag, State, LinkServer, Msg).
288+
289+
schedule_midi(Time, Tag, State, Msg) ->
290+
MIDIServer = maps:get(midi_server, State),
291+
schedule_internal_call(Time, Tag, State, MIDIServer, Msg).
292+
293+
schedule_cmd(Time, Tag, State, Msg) ->
294+
CueServer = maps:get(cue_server, State),
295+
schedule_internal_call(Time, Tag, State, CueServer, Msg).
282296

283297
%% Get the pid for the tag group tracker, creating it if needed
284298
tracker_pid(Tag, State) ->

app/server/erlang/tau/src/tau_server/tau_server_cue.erl

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -236,13 +236,8 @@ loop(State) ->
236236
log("Disabling midi cue forwarding ~n"),
237237
?MODULE:loop(State#{midi_enabled := false});
238238

239-
{timeout, Timer, {forward, Time, Data, Tracker}} ->
240-
send_forward(maps:get(in_socket, State), Time, Data),
241-
tau_server_tracker:forget(Timer, Tracker),
242-
?MODULE:loop(State);
243-
244-
{forward, Time, Data} ->
245-
send_forward(maps:get(in_socket, State), Time, Data),
239+
{send_osc, Host, Port, OSC} ->
240+
send_udp(maps:get(in_socket, State), Host, Port, OSC),
246241
?MODULE:loop(State);
247242

248243
{udp_error, _Port, econnreset} ->
@@ -277,13 +272,6 @@ loop(State) ->
277272
end.
278273

279274

280-
send_forward(Socket, Time, {Host, Port, Bin}) ->
281-
Now = osc:now(),
282-
send_udp(Socket, Host, Port, Bin),
283-
debug(1, Now, "sent message for time ~f with error ~f~n",
284-
[Time, Now-Time]),
285-
ok.
286-
287275
send_udp(Socket, Host, Port, Bin)
288276
when is_port(Socket) ->
289277
%% check to see if host is correct and usable

app/server/erlang/tau/src/tau_server/tau_server_link.erl

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ loop(State) ->
8888
maps:get(cue_server, State) ! {link, stop},
8989
?MODULE:loop(State);
9090

91+
{link_reset} ->
92+
log("Resetting link~n", []),
93+
case sp_link:is_enabled() of
94+
true ->
95+
log("Link is currently enabled, now disabling then re-enabling it...", []),
96+
sp_link:enable(false),
97+
sp_link:enable(true);
98+
_ -> ok
99+
end,
100+
?MODULE:loop(State);
91101

92102
{link_disable} ->
93103
log("Disabling link~n", []),
@@ -99,6 +109,11 @@ loop(State) ->
99109
sp_link:enable(true),
100110
?MODULE:loop(State);
101111

112+
{link_set_tempo, Tempo} ->
113+
TNow = sp_link:get_current_time_microseconds(),
114+
sp_link:set_tempo(Tempo, TNow),
115+
?MODULE:loop(State);
116+
102117
{link_rpc, UUID, get_current_time} ->
103118
Time = sp_link:get_current_time_microseconds(),
104119
log("Received link rpc current_time [~p]~n", [Time]),
@@ -129,15 +144,21 @@ loop(State) ->
129144
maps:get(cue_server, State) ! {api_reply, UUID, [NumPeers]},
130145
?MODULE:loop(State);
131146

147+
{link_rpc, UUID, is_enabled} ->
148+
Enabled = case sp_link:is_enabled() of
149+
true -> 1;
150+
false -> 0
151+
end,
152+
log("Link is enabled: [~p]~n", [Enabled]),
153+
maps:get(cue_server, State) ! {api_reply, UUID, [Enabled]},
154+
?MODULE:loop(State);
155+
132156
Any ->
133157
io:format("Received something unexpected", []),
134158
io:format("Unexpected value: ~p~n", [Any]),
135159
?MODULE:loop(State)
136160
end.
137161

138-
139-
140-
141162
%% sys module callbacks
142163

143164
system_continue(_Parent, _Debug, State) ->

app/server/erlang/tau/src/tau_server/tau_server_midi.erl

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,8 @@ mk_tau_str({tau, Kind, Event, Source, _}) ->
8585

8686
loop(State) ->
8787
receive
88-
{send, Time, Data} ->
89-
midi_send(Time, Data),
90-
?MODULE:loop(State);
91-
{timeout, Timer, {send, Time, Data, Tracker}} ->
92-
midi_send(Time, Data),
93-
tau_server_tracker:forget(Timer, Tracker),
88+
{send_midi, Data} ->
89+
midi_send(Data),
9490
?MODULE:loop(State);
9591
{flush} ->
9692
sp_midi:midi_flush(),
@@ -143,7 +139,7 @@ update_midi_ports(State) ->
143139
midi_outs := NewOuts}
144140
end.
145141

146-
midi_send(_Time, <<Data/binary>>) ->
142+
midi_send(<<Data/binary>>) ->
147143
debug("sending MIDI: ~p~n", [Data]),
148144
case tau_server_midi_out:encode_midi_from_osc(Data) of
149145
{ok, multi_chan, _, PortName, MIDIBinaries} ->

0 commit comments

Comments
 (0)