-
Notifications
You must be signed in to change notification settings - Fork 0
/
tutor_eng.sml
283 lines (212 loc) · 7.52 KB
/
tutor_eng.sml
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
(*
DOC : read portmidi.h from the original src for the complete view of portmidi
or at
http://portmedia.sourceforge.net/portmidi/doxygen/
HERE it is a quick getting started by experiment with portTime.
STEP BY STEP : copy/paste the following lines which endswith ';' at PolyML prompt
*)
use "./portmidi.sml";
open Portmidi;
(* first *)
initialize();
(*
time is music => familiarizing with PortTime
*)
(* verify portTime *)
ptStarted();
ptStop ();
(* set clock tick 1 ms *)
ptStart 1;
(* show clock *)
ptTime();
(* pause 2000 ms *)
ptSleep 2000;
(* new on linux virtual port return the new id of virtual device *)
val jojo = createVirtualOutput "jojo";
val jiji = createVirtualInput "jiji";
(* on windows you get an error code *)
getErrorText jojo;
(* return => val it = "PortMidi: Function is not implemented": string
and yes portmidi can't create virtual port on windows *)
deleteVirtualDevice jojo;
deleteVirtualDevice jiji;
(* now look at the devices *)
showDevices();
(*
here on Mac :
----
Gestionnaire IAC Bus 1 id=0 input=true output=false opened=false interf=CoreMIDI
Gestionnaire IAC Bus 2 id=1 input=true output=false opened=false interf=CoreMIDI
ATM SQ id=2 input=true output=false opened=false interf=CoreMIDI
ATM SQ Control id=3 input=true output=false opened=false interf=CoreMIDI
Kontakt Virtual Output id=4 input=true output=false opened=false interf=CoreMIDI
Gestionnaire IAC Bus 1 id=5 input=false output=true opened=false interf=CoreMIDI
Gestionnaire IAC Bus 2 id=6 input=false output=true opened=false interf=CoreMIDI
ATM SQ id=7 input=false output=true opened=false interf=CoreMIDI
ATM SQ Control id=8 input=false output=true opened=false interf=CoreMIDI
Kontakt Virtual Input id=9 input=false output=true opened=false interf=CoreMIDI
----
here on Linux :
______
Midi Through Port-0 id=0 input=false output=true opened=true interf=ALSA
Midi Through Port-0 id=1 input=true output=false opened=false interf=ALSA
ATM SQ ATM SQ id=2 input=false output=true opened=false interf=ALSA
ATM SQ ATM SQ id=3 input=true output=false opened=false interf=ALSA
ATM SQ ATM SQ Control id=4 input=false output=true opened=false interf=ALSA
ATM SQ ATM SQ Control id=5 input=true output=false opened=false interf=ALSA
val it = (): unit
I choose id=0 'Midi Through Port-0' output=true => I can output midi messages
and set out_id according
previously I have connected this port to Ardour instrument vst3 >
Surge (fantastic synth with microtonal possibilities)
https://surge-synthesizer.github.io
*)
(* from de devices list get the correct number for output device
here I put 0 *)
val out_id =0;
(*
Implementation :
openOutput and openInput save pointer in pointers array PM_STREAMS which is indexed on device id
and after we can get this pointer with : (getStream out_id) see "portmidi.sml"
Note : pointers are transparents for using this interface with that,
as we can only use device Id
- usage : openOutput devId buffer_size latency
*)
(* open out Kontakt
err = 0
=> success *)
openOutput out_id 100 2;
(* first note *)
val c4 = message (144,60,100); (* note on *)
val c4' = message (0x80,60,0); (* note off *)
writeShort out_id 0 c4;
writeShort out_id 0 c4';
(* we need array buffer for writing block of messages to device *)
(* filling output buffer *)
val notes_o = [(message(0x90,60,100),0),
(message(0x80,60,0),900),
(message(0x90,64,100),1000),
(message(0x90,67,100),1000),
(message(0x80,64,0),2000),
(message(0x80,67,0),2000)
];
(* to array *)
val notes_o' = Array.fromList notes_o;
(* latency > 0 here : 2 *)
openOutput out_id 100 2;
(* write buffer *)
write out_id notes_o' 6;
(*
don't ear expecting notes because 0 and 1000 ms timestamps for note on
are in the past vs portTime
*)
(* look at clock *)
ptTime();
(* => val it = 84314: int
1000 is legacy
I have to reset the clock to 0 before playing and put a small latency to be in time.
=> write a small function
*)
fun playo n = ( ptStop();ptStart 1; openOutput out_id 100 2; write out_id notes_o' n);
(* play 1 note = 2 events 1st on and 2nd off *)
playo 2;
(* then play 6 events *)
playo 6;
(* another solution for being in time is to add ptTime() to each timestamp of notes list
without touching clock or already opened device
=>
write these another small functions should help *)
fun addPortTime_o port_time event_array =
Array.modify (fn (msg,ts) => (msg,ts + port_time)) event_array;
fun playList_o notes_list clock = let
val notes_array = Array.fromList notes_list
val modified = addPortTime_o clock notes_array
in
write out_id notes_array 6
end;
playList_o notes_o (ptTime());
(* my addon to portmidi playing with bigbuffer JH *)
(* one tuple for message + timestamp
0x90 = note on et 0x80 note off (channel 1)
60 = C4
100 = velocity
0 for data4 (* this can be used for tagging and mandatory for sysex *)
0 = timestamp
here I put 2 notes at the same time 1000 => chord
the two lists are here for experiment portmidi timing with timestamp
*)
(* bad timestamp ordered list - 2000 is before 0 - read portmidi doc -
but play well on Suse *)
val notes = [
(0x90,67,100,0,1000),
(0x80,67,0,0,2000),
(0x90,64,100,0,1000),
(0x80,64,0,0,2000),
(0x90,60,100,0,0),
(0x80,60,0,0,980)
];
(* good *)
val notes2 = [(0x90,60,100,0,0),
(0x80,60,0,0,980),
(0x90,67,100,0,1000),
(0x90,64,100,0,1000),
(0x80,67,0,0,2000),
(0x80,64,0,0,2000)
];
(* bigWrite need Array buffer *)
val notes'= Array.fromList notes;
val notes2'= Array.fromList notes2;
(* latency > 0 for use timestamp *)
openOutput out_id 100 2;
(* set time 0 before playing
I have six messages but I can play only 4
*)
fun play msg_array n = ( ptStop();ptStart 1; openOutput out_id 100 5;
bigWrite out_id msg_array n);
(* don't play msg in time because the initial list is bad formed :
all timestamp should be ordered
but it's ok with Open Suse *)
play notes' 4;
play notes' 6;
play notes2' 6; (* notes2 is ordered and play well on all tested platforms *)
(* second solution for good timing => add port_time to timestamp *)
fun addPortTime port_time event_array =
Array.modify (fn (stat,dat1,dat2,dat3,ts) => (stat,dat1,dat2,dat3,ts + port_time)) event_array;
(*
playing size messages from list
*)
fun playList notes_list size = let
val notes_array = Array.fromList notes_list
val pt_time = ptTime()
val modified = addPortTime pt_time notes_array
in
bigWrite out_id notes_array size
end;
(* reinitialize out device for all notes off *)
fun gout () = openOutput out_id 100 2;
(* try *)
playList notes 6;
playList notes2 6;
(*
join all with #ptSleep for serial cacophony
*)
fun play3 () = (playList notes2 2 ;ptSleep 1000;playList notes2 6 ;ptSleep 1500;playList notes2 6);
play3();
fun playPlus () = (playo 2; ptSleep 1000; playo 2; ptSleep 1000; playo 4 ;ptSleep 2000;playo 6);
playPlus();
(* à vous de jouer ! *)
(* ERRORS *)
(* if you get error use - getErrorText errnum *)
openOutput 0 100 2;
(* => it = ~9999: int *)
getErrorText it;
(*
val it = "PortMidi: `Invalid device ID'": string
surely because output=false
Gestionnaire IAC Bus 1 id=0 input=true output=false opened=false interf=CoreMIDI
*)
terminate();
print ("args : " ^ (String.concatWith ", " (CommandLine.arguments() ) ));
print "\n";
print ("cmd : " ^ (CommandLine.name()) );
print "\n";