(***************************************************************)
(* Reactive Asco *)
(* http://reactiveml.org/reactive_asco *)
(* *)
(* *)
(* Authors: Guillaume Baudart (guillaume.baudart@ens.fr) *)
(* Louis Mandel (louis.mandel@lri.fr) *)
(* *)
(***************************************************************)
(** This is the interpreter core. It contains the execution process,
handle synchronization and performance errors. *)
open Types
open Reactive_map
open Utils
(** Create a process player that takes as argument an electronic score
[score] and executes it. The player depends on the instrumental
score [instr_score]. It follows the position in the instrumental
score using the process [wait_event] and the tempo with the process
[wait]. The resulting performance is sent on the signal [perf].
*)
let make_player instr_score wait wait_event perf =
let rec process exec_seq generic delta seq =
match seq with
| [] -> (* rule (Empty Sequence) *) ()
| (dae, ae)::s ->
(* rule (Exec Sequence) *)
run (generic (delta +. dae) ae) ||
run (exec_seq generic (delta +. dae) s) in
let rec process exec score =
match score with
| [] -> (* rule (Empty Score) *) ()
| se::sc ->
(* rule (Exec Score) *)
run (exec_score_event se) ||
run (exec sc)
and process exec_score_event se =
let status = run (wait_event se.event) in
match status with
| Detected ->
(* rule (Detect) *)
run (exec_seq (detected se.event) 0.0 se.seq)
| Missed(j) ->
(* rule (Missed) *)
run (exec_seq (missed se.event j) 0.0 se.seq)
and process detected i delta ae =
match ae with
| Action(a) ->
(* rule (Detected Action) *)
run (wait delta);
emit perf (i,delta,a);
| Group(g) ->
begin match g.group_synchro with
| Loose ->
(* rule (Detected Loose Group) *)
run (exec_seq (detected i) delta g.group_seq)
| Tight ->
(* rule (Detected Tight Group) *)
let gs = Groups.slice instr_score i delta g in
run (exec gs)
end
| Until(u) ->
(* Preemption Construct *)
signal kill in
do
run (exec_seq (detected i) delta u.until_seq); emit kill
||
let _ = run (wait_event u.until_event) in emit kill
until
kill done
and process missed i j delta ae =
let dj = instr_score.find j in
let di = instr_score.find i in
match ae with
| Action(a) ->
(* rule (Missed Action) *)
let d = (max 0.0 (delta +. di -. dj)) in
run (wait d);
emit perf (j,d,a);
| Group(g) ->
begin match g.group_error with
| Local -> (* rule (Missed Local Group) *) ()
| Global ->
(* rule (Missed Global Group) *)
run (detected j 0.0 ae)
| Partial ->
(* rule (Missed Partial Group) *)
let past, future = Groups.split instr_score i j delta g in
let gpast = Groups.extract_group past in
let gfuture =
Group({group_synchro = g.group_synchro;
group_error = g.group_error;
group_seq = future;})
in
(run (exec_seq (missed i j) delta gpast) ||
run (detected j 0.0 gfuture))
| Causal ->
(* rule (Missed Causal Group) *)
let past, future = Groups.split instr_score i j delta g in
let gfuture =
Group({group_synchro = g.group_synchro;
group_error = g.group_error;
group_seq = future;})
in
(run (exec_seq (missed i j) delta past) ||
run (detected j 0.0 gfuture))
end
| Until(u) ->
signal kill in
do
run (exec_seq (missed i j) delta u.until_seq); emit kill
||
let _ = run (wait_event u.until_event) in emit kill
until kill done
in
(* Launch the already past part of a dynamically loaded score *)
let rec process exec_past score j =
match score with
| [] -> ()
| se::sc ->
run (exec_seq (missed se.event j) 0.0 se.seq) ||
run (exec_past sc j)
in
(* Launch a score that could start after the beginning of the performance *)
let process play_score score i =
let past, future = List.partition (fun se -> se.event < i) score in
run (exec future) ||
run (exec_past past i)
in
(* Return the process which play an electronic score *)
play_score