-
Notifications
You must be signed in to change notification settings - Fork 3
/
portmidi.ml
231 lines (203 loc) · 6.63 KB
/
portmidi.ml
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
open! Core_kernel
open! No_polymorphic_compare [@@warning "-66"]
module Device_info = struct
type t =
{ interface : string option;
name : string option;
input : bool;
output : bool;
struct_version_internal : int;
opened_internal : bool
}
[@@deriving sexp, fields]
end
module Portmidi_error = struct
type t =
[ `Got_data
| `Host_error
| `Invalid_device_id
| `Insufficient_memory
| `Buffer_too_small
| `Bad_ptr
| `Bad_data
| `Internal_error
| `Buffer_max_size
]
[@@deriving sexp, variants]
end
let message_status msg = Int32.bit_and msg 0xFFl
let message_data1 msg = Int32.bit_and (Int32.( lsr ) msg 8) 0xFFl
let message_data2 msg = Int32.bit_and (Int32.( lsr ) msg 16) 0xFFl
module Portmidi_event = struct
type t =
{ message : Int32.t;
timestamp : Int32.t
}
[@@deriving sexp, fields]
let create ~status ~data1 ~data2 ~timestamp =
let message =
let status = Char.to_int status |> Int32.of_int_exn in
let data1 = Char.to_int data1 |> Int32.of_int_exn in
let data2 = Char.to_int data2 |> Int32.of_int_exn in
let data1_masked = Int32.( lsl ) data1 8 in
let data2_masked = Int32.( lsl ) data2 16 in
Int32.bit_or status data1_masked |> Int32.bit_or data2_masked
in
{ message; timestamp }
end
module Input_stream = struct
type t = unit Ctypes_static.ptr
end
module Output_stream = struct
type t = unit Ctypes_static.ptr
end
module Data = struct
open C.Types
let result_of_pm_error i : (unit, Portmidi_error.t) result =
let open Pm_error in
if Int.( = ) i no_error
then Ok ()
else if Int.( = ) i no_data
then Ok ()
else if Int.( = ) i got_data
then Error `Got_data
else if Int.( = ) i host_error
then Error `Host_error
else if Int.( = ) i invalid_device_id
then Error `Invalid_device_id
else if Int.( = ) i insufficient_memory
then Error `Insufficient_memory
else if Int.( = ) i buffer_too_small
then Error `Buffer_too_small
else if Int.( = ) i bad_ptr
then Error `Bad_ptr
else if Int.( = ) i bad_data
then Error `Bad_data
else if Int.( = ) i internal_error
then Error `Internal_error
else if Int.( = ) i buffer_max_size
then Error `Buffer_max_size
else failwithf "unknown PmError code: %d" i ()
let pm_error_int i =
let open Pm_error in
match i with
| `Got_data -> got_data
| `Host_error -> host_error
| `Invalid_device_id -> invalid_device_id
| `Insufficient_memory -> insufficient_memory
| `Buffer_too_small -> buffer_too_small
| `Bad_ptr -> bad_ptr
| `Bad_data -> bad_data
| `Internal_error -> internal_error
| `Buffer_max_size -> buffer_max_size
let device_info_of_pdi pdi =
let module PDI = PmDeviceInfo in
let get x f = Ctypes.getf x f in
{ Device_info.struct_version_internal = get pdi PDI.struct_version;
interface = get pdi PDI.interf;
name = get pdi PDI.name;
input = Int.( = ) (get pdi PDI.input) 1;
output = Int.( = ) (get pdi PDI.output) 1;
opened_internal = Int.( = ) (get pdi PDI.opened) 1
}
let default_sysex_buffer_size = default_sysex_buffer_size
end
let default_sysex_buffer_size = Data.default_sysex_buffer_size
module Functions = struct
(*open Ctypes*)
open C.Functions
let initialize () = Data.result_of_pm_error (pm_initialize ())
let terminate () = pm_terminate ()
let count_devices () = pm_count_devices ()
let get_device_info index =
let di = pm_get_device_info index in
if Ctypes.is_null di then None else Some (Data.device_info_of_pdi (Ctypes.( !@ ) di))
let get_error_text err = pm_get_error_text (Data.pm_error_int err)
let close stream = Data.result_of_pm_error (pm_close stream)
let abort stream = Data.result_of_pm_error (pm_abort stream)
let open_input ~device_id ~buffer_size =
let open Ctypes in
let stream = allocate (ptr void) null in
let res = pm_open_input stream device_id null buffer_size null null in
match Data.result_of_pm_error res with
| Ok () -> Ok !@stream
| Error err -> Error err
let poll_input stream =
match pm_poll stream with
| 0 -> Ok false
| 1 -> Ok true
| x ->
(match Data.result_of_pm_error x with
| Ok () -> failwithf "poll_input: expected error here" ()
| Error _ as e -> e)
let read_input ~length stream =
let open Ctypes in
let buffer = allocate_n C.Types.PmEvent.t ~count:length in
let retval = pm_read stream buffer (Int32.of_int_exn length) in
if Int.( >= ) retval 0
then
let module PME = C.Types.PmEvent in
let get x f = Ctypes.getf x f in
let lst =
let a = CArray.from_ptr buffer retval in
List.map (CArray.to_list a) ~f:(fun pme ->
{ Portmidi_event.message = get pme PME.message; timestamp = get pme PME.timestamp })
in
Ok lst
else (
match Data.result_of_pm_error retval with
| Ok () -> failwithf "read_input: expected error here" ()
| Error _ as e -> e)
let abort_input = abort
let close_input = close
let open_output ~device_id ~buffer_size ~latency =
let open Ctypes in
let stream = allocate (ptr void) null in
let res = pm_open_output stream device_id null buffer_size null null latency in
match Data.result_of_pm_error res with
| Ok () -> Ok !@stream
| Error _ as e -> e
let write_output stream lst =
let open Ctypes in
let length = List.length lst in
let a =
let lst =
let module PME = C.Types.PmEvent in
List.map lst ~f:(fun portmidi_event ->
let pme = make PME.t in
setf pme PME.message portmidi_event.Portmidi_event.message;
setf pme PME.timestamp portmidi_event.Portmidi_event.timestamp;
pme)
in
let a = CArray.of_list C.Types.PmEvent.t lst in
CArray.start a
in
let retval = pm_write stream a (Int32.of_int_exn length) in
if Int.( = ) retval 0
then Ok ()
else (
match Data.result_of_pm_error retval with
| Ok () -> failwithf "write_output: expected error here" ()
| Error _ as e -> e)
let write_output_sysex ~when_ ~msg stream =
let open Ctypes in
let msg =
let len = Array.length msg in
let b = CArray.make char ~initial:'\x00' len in
for i = 0 to pred len do
CArray.set b i (Array.get msg i)
done;
CArray.start b
in
let res = pm_write_sysex stream (Int32.of_int_exn when_) msg in
match Data.result_of_pm_error res with
| Ok () -> Ok ()
| Error _ as e -> e
let abort_output = abort
let close_output = close
end
module Porttime = struct
open C.Functions
let time () = pt_time ()
end
include Functions