(***************************************************************) (* 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