|
| 1 | +namespace Midinette.Platform.RtMidi |
| 2 | + |
| 3 | +open System.Reflection |
| 4 | +open Midinette.Platform |
| 5 | + |
| 6 | +module Impl = |
| 7 | + let getRawDataReceivedEventFromInput port = |
| 8 | + let f = port.GetType().GetEvent "Message"; |
| 9 | + let dataReceived = new Event<byte array>() |
| 10 | + let handler = System.EventHandler<byte array>(fun _ -> dataReceived.Trigger) |
| 11 | + f.AddEventHandler(port, handler) |
| 12 | + dataReceived |
| 13 | + |
| 14 | + let toBytes (message: Midi.MidiMessage) = [|message.Status;message.Data1;message.Data2|] |
| 15 | + let rtMidiOut (bytes: byte array) port = |
| 16 | + let f = port.GetType().GetField("_outputDevice", BindingFlags.NonPublic ||| BindingFlags.Instance) |
| 17 | + let rtoutport = f.GetValue port |
| 18 | + let sendmethod = rtoutport.GetType().GetMethod "SendMessage" |
| 19 | + let paramters = box bytes |
| 20 | + let result = sendmethod.Invoke(rtoutport, Array.singleton paramters) :?> bool |
| 21 | + () |
| 22 | + |
| 23 | +open Impl |
| 24 | +open Midi |
| 25 | +open System.Diagnostics |
| 26 | + |
| 27 | +type InPortEvents<'timestamp> = { |
| 28 | + error : Event<string> |
| 29 | + channelMessage : Event<Midi.MidiEvent<'timestamp>> |
| 30 | + systemMessage: Event<Midi.MidiEvent<'timestamp>> |
| 31 | + sysex: Event<byte array> |
| 32 | + realtimeMessage: Event<Midi.MidiEvent<'timestamp>> |
| 33 | +} |
| 34 | + |
| 35 | +type RtMidiInfo = { info: RtMidi.Core.Devices.Infos.IMidiDeviceInfo } |
| 36 | +with |
| 37 | + interface IDeviceInfo with |
| 38 | + member d.Name = d.info.Name |
| 39 | + |
| 40 | +type RtMidiIn = { inPort: RtMidi.Core.Devices.IMidiInputDevice; info: RtMidiInfo; inEvents: InPortEvents<int64> } |
| 41 | +with |
| 42 | + interface IMidiInput<Midi.MidiEvent<int64>> with |
| 43 | + |
| 44 | + member x.Close () = x.inPort.Close() |
| 45 | + member x.Open bufferSize = x.inPort.Open() |> ignore |
| 46 | + member x.DeviceInfo = x.info :> _ |
| 47 | + [<CLIEvent>] member x.Error = x.inEvents.error.Publish |
| 48 | + [<CLIEvent>] member x.ChannelMessageReceived = x.inEvents.channelMessage.Publish |
| 49 | + [<CLIEvent>] member x.SystemMessageReceived = x.inEvents.systemMessage.Publish |
| 50 | + [<CLIEvent>] member x.SysexReceived = x.inEvents.sysex.Publish |
| 51 | + [<CLIEvent>] member x.RealtimeMessageReceived = x.inEvents.realtimeMessage.Publish |
| 52 | + |
| 53 | +type RtMidiOut<'timestamp> = { outPort: RtMidi.Core.Devices.IMidiOutputDevice; info: RtMidiInfo } |
| 54 | +with |
| 55 | + interface IMidiOutput<'timestamp,Midi.MidiMessage> with |
| 56 | + member x.DeviceInfo = x.info :> _ |
| 57 | + member x.Open bufferSize latency = x.outPort.Open() |> ignore |
| 58 | + member x.Close () = x.outPort.Close() |
| 59 | + member x.WriteSysex timestamp data = rtMidiOut data x.outPort |
| 60 | + member x.WriteMessage timestamp message = rtMidiOut (toBytes message) x.outPort |
| 61 | + member x.WriteMessages timestamp messages = |
| 62 | + let bytes = |
| 63 | + Array.init |
| 64 | + (messages.Length * 3) |
| 65 | + (fun i -> |
| 66 | + let message = messages.[i / 3] |
| 67 | + match i % 3 with |
| 68 | + | 0 -> message.Status |
| 69 | + | 1 -> message.Data1 |
| 70 | + | 2 -> message.Data2 |
| 71 | + | _ -> failwithf "index %i not expected???" i |
| 72 | + ) |
| 73 | + rtMidiOut bytes x.outPort |
| 74 | + |
| 75 | +type RtMidiMidinettePlatformImpl() = |
| 76 | + let rtmidi = RtMidi.Core.MidiDeviceManager.Default |
| 77 | + let watch = Stopwatch.StartNew() |
| 78 | + let platformEvents = MidiPlatformTrigger() |
| 79 | + |
| 80 | + interface IMidiPlatform<IDeviceInfo, Midi.MidiEvent<int64>, int64, Midi.MidiMessage> with |
| 81 | + member x.InputDevices = |
| 82 | + RtMidi.Core.MidiDeviceManager.Default.InputDevices |
| 83 | + |> Seq.map (fun d -> { info = d} :> IDeviceInfo) |
| 84 | + |> Seq.toArray |
| 85 | + member x.OutputDevices = |
| 86 | + RtMidi.Core.MidiDeviceManager.Default.InputDevices |
| 87 | + |> Seq.map (fun d -> { info = d} :> IDeviceInfo) |
| 88 | + |> Seq.toArray |
| 89 | + |
| 90 | + member x.GetMidiInput deviceInfo = |
| 91 | + match deviceInfo with |
| 92 | + | :? RtMidiInfo as rtDevice -> |
| 93 | + match rtDevice.info with |
| 94 | + | :? RtMidi.Core.Devices.Infos.IMidiInputDeviceInfo as device -> |
| 95 | + // todo: keep that in store instead of creating / subscribing again |
| 96 | + let port = device.CreateDevice() |
| 97 | + let dataReceived = getRawDataReceivedEventFromInput port |
| 98 | + let error = new Event<_>() |
| 99 | + let channelMessage = new Event<_>() |
| 100 | + let sysex = new Event<_>() |
| 101 | + let systemMessage = new Event<_>() |
| 102 | + let realtimeMessage = new Event<_>() |
| 103 | + dataReceived.Publish.Add(fun bytes -> |
| 104 | + let status : MidiMessageType = LanguagePrimitives.EnumOfValue (bytes.[0]) |
| 105 | + let timestamp = watch.ElapsedTicks |
| 106 | + let inline triggerMessageEvent (event1: Event<_>) platformNoticer = |
| 107 | + let message = MidiMessage.Encode bytes.[0] bytes.[1] bytes.[2] |
| 108 | + let event = Midi.MidiEvent(message, timestamp) |
| 109 | + event1.Trigger event |
| 110 | + platformNoticer (rtDevice,event) |
| 111 | + if MidiMessageTypeIdentifaction.isChannelMessage status then |
| 112 | + triggerMessageEvent channelMessage platformEvents.NoticeChannelMessage |
| 113 | + elif MidiMessageTypeIdentifaction.isRealtimeMessage status then |
| 114 | + triggerMessageEvent realtimeMessage platformEvents.NoticeRealtimeMessage |
| 115 | + elif MidiMessageTypeIdentifaction.isSystemMessage status then |
| 116 | + triggerMessageEvent systemMessage platformEvents.NoticeSystemMessage |
| 117 | + elif MidiMessageTypeIdentifaction.isSysexBeginOrEnd status then |
| 118 | + sysex.Trigger bytes |
| 119 | + platformEvents.NoticeSysex(rtDevice, bytes) |
| 120 | + else |
| 121 | + #if DEBUG |
| 122 | + failwithf "unable to parse what I received received %A" bytes |
| 123 | + #endif |
| 124 | + ) |
| 125 | + let inEvents = {error = error; channelMessage = channelMessage; sysex = sysex; systemMessage = systemMessage; realtimeMessage = realtimeMessage } |
| 126 | + Some ({ inPort = port; info = rtDevice; inEvents = inEvents } :> _) |
| 127 | + | _ -> None |
| 128 | + | _ -> None |
| 129 | + member x.GetMidiOutput deviceInfo = |
| 130 | + match deviceInfo with |
| 131 | + | :? RtMidiInfo as rtDevice -> |
| 132 | + match rtDevice.info with |
| 133 | + | :? RtMidi.Core.Devices.Infos.IMidiOutputDeviceInfo as device -> |
| 134 | + Some ({ outPort = device.CreateDevice() ; info = rtDevice} :> IMidiOutput<_,_>) |
| 135 | + | _ -> None |
| 136 | + | _ -> None |
| 137 | + |
0 commit comments