1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
let midi_frequency (n : int) : float = 440. *. (2. ** (float_of_int (n - 69) /. 12.))
let midi_number (name : string) : int option =
let letter = function 'C' -> Some 0 | 'D' -> Some 2 | 'E' -> Some 4 | 'F' -> Some 5 | 'G' -> Some 7 | 'A' -> Some 9 | 'B' -> Some 11 | _ -> None in
let n = String.length name in
if n < 2 then None
else
match letter (Char.uppercase_ascii name.[0]) with
| None -> None
| Some semitone -> (
let (accidental, rest) =
match name.[1] with '#' -> (1, String.sub name 2 (n - 2)) | 'b' -> (-1, String.sub name 2 (n - 2)) | _ -> (0, String.sub name 1 (n - 1))
in
match int_of_string_opt rest with
| Some octave when octave >= -1 && octave <= 9 -> Some (((octave + 1) * 12) + semitone + accidental)
| _ -> None)
let frequency (name : string) : float = match midi_number name with Some n -> midi_frequency n | None -> 0.
let instrument ~(voice : int) ~(voices : int) : Oscillator.waveform * float =
if voices > 1 && voice = voices - 1 then (Triangle, 0.5) else if voice = 0 then (Square, 0.3) else (Square, 0.18)
let drum ~(volume : float) (key : int) : Synth.t =
let v source f = Synth.Voice { source; frequency = f; slide = None; seconds = 0.2; volume; fade = false; effects = []; envelope = None } in
match key with
| 35 | 36 -> v (Wave Triangle) 150. |> Synth.sliding 50. |> Synth.lasting 0.15 |> Synth.fading |> Synth.louder 2.
| 38 | 40 -> v Noise 5000. |> Synth.lasting 0.15 |> Synth.fading
| 42 | 44 | 46 -> v Noise 12000. |> Synth.lasting 0.05 |> Synth.fading |> Synth.louder 0.6
| _ -> v Noise 3000. |> Synth.lasting 0.1 |> Synth.fading
let to_sound (tune : Abc.tune) : Synth.t =
let voices = List.length tune.voices in
let silence seconds = Synth.voice (Wave Sine) 0. |> Synth.louder 0. |> Synth.lasting seconds in
Synth.Together
(List.mapi
(fun i events ->
let (waveform, volume) = instrument ~voice:i ~voices in
let is_drums = List.nth_opt tune.drums i = Some true in
Synth.After
(List.map
(fun (e : Abc.event) ->
match e.notes with
| [] -> silence e.length
| notes when is_drums ->
Synth.Together (silence e.length :: List.map (drum ~volume:0.25) notes)
| notes ->
let sounding = e.length *. 0.9 in
Synth.After
[ Synth.Together
(List.map
(fun n -> Synth.voice (Wave waveform) (midi_frequency n) |> Synth.lasting sounding |> Synth.louder (volume /. 0.5))
notes);
silence (e.length -. sounding) ])
events))
tune.voices)
let midi_voice (n : Midi.note) : Synth.t =
let volume = 0.25 *. float_of_int n.velocity /. 127. in
let v source f = Synth.Voice { source; frequency = f; slide = None; seconds = n.length; volume; fade = false; effects = []; envelope = None } in
let f = midi_frequency n.key in
if n.channel = 9 then drum ~volume n.key
else
match n.program / 8 with
| 0 | 1 -> v (Wave Square) f |> Synth.fading |> Synth.louder 0.8
| 3 -> v (Wave Sawtooth) f |> Synth.fading |> Synth.louder 0.7
| 4 -> v (Wave Triangle) f |> Synth.louder 1.5
| 5 | 6 -> v (Wave Sawtooth) f |> Synth.louder 0.5
| 7 -> v (Wave Sawtooth) f |> Synth.louder 0.7
| 11 -> v (Wave Triangle) f
| _ -> v (Wave Square) f |> Synth.louder 0.7
let render_score (score : Midi.score) : Signal.t =
let out = Array.make (Signal.samples (score.duration +. 0.2)) 0. in
List.iter
(fun (n : Midi.note) ->
let at = Signal.samples n.start and s = Synth.render (midi_voice n) in
Array.iteri (fun i x -> if at + i < Array.length out then out.(at + i) <- out.(at + i) +. x) s)
score.notes;
out