@@ -31,6 +31,8 @@ module MachineSpecs =
31
31
let patterns = [| 0 uy.. 127 uy|]
32
32
let kits = [| 0 uy.. 63 uy|]
33
33
let songs = [| 0 uy.. 31 uy|]
34
+
35
+
34
36
35
37
[<RequireQualifiedAccess>]
36
38
type Track =
681
683
682
684
type TriggerType =
683
685
| TriggerChannel of Track
684
- | TriggerPattern of PatternBank * patternNumber : byte
686
+ | TriggerPattern of PatternLocator
685
687
| UnknownTrigger of value : byte
686
688
687
689
type NoteTriggerType = NoteTriggerType of note : byte * TriggerType
@@ -708,14 +710,9 @@ type KeyMapStructure(bytes: byte array) =
708
710
| v -> failwithf " bank %i " v
709
711
let getTriggerType ( value : byte ) =
710
712
if value < 0x10 uy then TriggerChannel ( Track.trackForValue value)
711
- elif value < 0x8f uy then TriggerPattern ( bankForValue value, value &&& 0xf uy)
713
+ elif value < 0x8f uy then TriggerPattern ( PatternLocator.PatternLocator ( bankForValue value, value &&& 0xf uy) )
712
714
else UnknownTrigger ( value)
713
- (*
714
- let getTrigger (note: byte) (value: byte)=
715
- if value < 0x10uy then TriggerChannel (note, Track.trackForValue value)
716
- elif value < 0x8fuy then TriggerPattern (note, bankForValue value, value &&& 0xfuy)
717
- else Unknown (note, value)
718
- *)
715
+
719
716
let triggers =
720
717
bytes
721
718
|> Array.indexed
@@ -790,6 +787,8 @@ type GlobalSettings = {
790
787
// Trig mode for keymap
791
788
}
792
789
with
790
+ static member ToSysex globals =
791
+ [||]
793
792
static member FromSysex ( bytes : byte array ) =
794
793
let originalPosition = bytes.[ 0x09 ]
795
794
let drumRoutingTable = bytes |> getSlice 0x0a 16 |> Array.map Output.FromByte
@@ -831,6 +830,7 @@ type MachineDrumSysexResponses =
831
830
| PatternResponse of MDPattern
832
831
| SongResponse of MDSong
833
832
| StatusResponse of MachineDrumStatusType * byte
833
+ //| UnknownSysexResponse of byte array
834
834
with
835
835
member x.MessageId =
836
836
match x with
@@ -839,14 +839,18 @@ with
839
839
| PatternResponse _ -> 0x67 uy
840
840
| SongResponse _ -> 0x69 uy
841
841
| StatusResponse _ -> 0x72 uy
842
- static member BuildResponse ( bytes : byte array ) =
843
- match bytes.[ 6 ] with
844
- | 0x50 uy -> GlobalSettingsResponse ( GlobalSettings.FromSysex bytes)
845
- | 0x52 uy -> KitResponse ( MDKit bytes)
846
- | 0x67 uy -> PatternResponse ( MDPattern bytes)
847
- | 0x69 uy -> SongResponse ( MDSong bytes)
848
- | 0x72 uy -> StatusResponse ( MachineDrumStatusType.FromByte bytes.[ 7 ], bytes.[ 8 ])
849
- | _ -> failwithf " h:%x response not understood" bytes.[ 6 ]
842
+ static member BuildResponse ( sysex : byte array ) =
843
+ match sysex.[ 6 ] with
844
+ | 0x50 uy -> Some ( GlobalSettingsResponse ( GlobalSettings.FromSysex sysex) )
845
+ | 0x52 uy -> Some ( KitResponse ( MDKit sysex) )
846
+ | 0x67 uy -> Some ( PatternResponse ( MDPattern sysex) )
847
+ | 0x69 uy -> Some ( SongResponse ( MDSong sysex) )
848
+ | 0x72 uy -> Some ( StatusResponse ( MachineDrumStatusType.FromByte sysex.[ 7 ], sysex.[ 8 ]))
849
+ | _ ->
850
+ // failwithf "h:%x response not understood" sysex.[6]
851
+ None
852
+ //Some (UnknownSysexResponse sysex)
853
+
850
854
851
855
type AssignMachineMode =
852
856
| InitSynthesis
@@ -920,6 +924,40 @@ with
920
924
| SetReverbParameter( parameter, value) -> [| ReverbParameter.ToByte parameter; value|]
921
925
Elektron.Platform.SysexHelper.makeMachineDrumSysexMessage ( Array.concat ([| x.MessageId |> Array.singleton; data|]))
922
926
927
+ type LFOEvent =
928
+ | AssignTrack of Track
929
+ | AssignDestination of MDTrackParameter
930
+ | AssignShape1 of LFOShape
931
+ | AssignShape2 of LFOShape
932
+ | AssignType of LFOType
933
+
934
+ type MachineDrumEvent =
935
+ | TrackLevel of Track * value : byte
936
+ | TrackParameter of Track * MDTrackParameter * value : byte
937
+ | TrackTrigger of Track * velocity : byte
938
+ | TrackRelease of Track
939
+ | PatternChanged of byte
940
+ | LFOSetting of Track * LFOEvent
941
+ | DelaySetting of DelayParameter * value : byte
942
+ | ReverbSetting of ReverbParameter * value : byte
943
+ | EqualizerSetting of EqualizerParameter * value : byte
944
+ | CompressorSetting of CompressorParameter * value : byte
945
+ | Unknown of MidiMessage
946
+ | MachineDrumSysex of MachineDrumSysexResponses
947
+ | Sysex of byte array
948
+ | KitChanged of byte
949
+ with
950
+ member x.Track =
951
+ match x with
952
+ | TrackLevel ( track, _)
953
+ | TrackParameter( track,_,_)
954
+ | TrackTrigger( track, _)
955
+ | TrackRelease track
956
+ | LFOSetting( track, _)
957
+ -> Some track
958
+ | _ -> None
959
+
960
+
923
961
module Sysex =
924
962
let mdHeader = [|
925
963
0xf0 uy
@@ -930,12 +968,17 @@ module Sysex =
930
968
0x00 uy
931
969
|]
932
970
971
+ [<RequireQualifiedAccess>]
972
+ type MidiOutputData =
973
+ | Message of MidiMessage
974
+ | Sysex of bytes : byte array
975
+
933
976
type MachineDrum ( inPort : IMidiInput < int >, outPort : IMidiOutput < int >, getSysexNowTimestamp : unit -> int ) =
934
977
let helpGetMDSysex maxMessage ( timeout : TimeSpan ) ( request : MachineDrumSysexRequests ) inPort : Async < MachineDrumSysexResponses option > =
935
978
#if FABLE_ COMPILER
936
979
failwithf " TODO FABLE"
937
980
#else
938
- Midi.Sysex.helpGetSysex maxMessage timeout ( fun sysex -> sysex.[ 0 .. 5 ] = Sysex.mdHeader && sysex.[ 6 ] = request.ResponseMessageId) request.BuildResponse inPort
981
+ Midi.Sysex.helpGetSysex maxMessage timeout ( fun sysex -> sysex.[ 0 .. 5 ] = Sysex.mdHeader && sysex.[ 6 ] = request.ResponseMessageId) ( request.BuildResponse >> Option.get ) inPort
939
982
#endif
940
983
941
984
let performSysExRequest ( requestMessage : MachineDrumSysexRequests ) =
@@ -952,13 +995,65 @@ type MachineDrum(inPort: IMidiInput<int>, outPort: IMidiOutput<int>, getSysexNow
952
995
#endif
953
996
else
954
997
None
998
+
999
+
955
1000
member x.MidiOutPort = inPort
956
- member x.MidiInPort = outPort
957
- member x.QueryStatus statusType =
958
- performSysExRequest ( QueryStatus statusType)
1001
+ member x.MidiInPort = outPort
1002
+
1003
+ member x.EventToMidiMessages ( mdEvent : MachineDrumEvent ) globals =
1004
+ let channel = globals.MidiBaseChannel
1005
+ //globals.KeymapStructure.GetTriggerNotesForBank
1006
+ let track = mdEvent.Track
1007
+ let note =
1008
+ match track with
1009
+ | Some track -> globals.KeymapStructure.GetTriggerNoteForChannel track
1010
+ | None -> None
1011
+
1012
+ let makeNote note velocity isOn =
1013
+ match note with
1014
+ | None -> None
1015
+ | Some note ->
1016
+ if isOn then
1017
+ Some ( MidiOutputData.Message ( MidiMessage.NoteOn channel note velocity))
1018
+ else
1019
+ Some ( MidiOutputData.Message ( MidiMessage.NoteOff channel note velocity))
1020
+
1021
+ let makeProgramChange program =
1022
+ MidiMessage.ProgramChange channel program
1023
+ |> MidiOutputData.Message
1024
+
1025
+ let none = Array.empty
1026
+ let some = Array.singleton
1027
+ match mdEvent with
1028
+ | TrackTrigger( track, velocity) -> makeNote note velocity true |> Option.get |> some
1029
+ | TrackRelease( track) -> makeNote note 0 uy false |> Option.get |> some
1030
+ | TrackLevel( track, level) -> none
1031
+ | TrackParameter( track, parameter, value) -> none
1032
+ | PatternChanged pattern -> none
1033
+ | Unknown message -> message |> MidiOutputData.Message |> some
1034
+ | Sysex data -> data |> MidiOutputData.Sysex |> some
1035
+ | MachineDrumSysex sysex -> none
1036
+ | KitChanged kit -> makeProgramChange kit |> some
1037
+ | _ -> none
1038
+
959
1039
960
- member x.Dump dumpRequest =
961
- performSysExRequest dumpRequest
1040
+
1041
+ member x.SendEvents mdGlobals mdEvents getNow =
1042
+ match mdGlobals with
1043
+ | Some globals ->
1044
+ let now = getNow ()
1045
+ for timestamp, mdEvent in mdEvents do
1046
+ x.EventToMidiMessages mdEvent globals
1047
+ |> Array.iter (
1048
+ function
1049
+ | MidiOutputData.Message message -> outPort.WriteMessage ( timestamp + now) message
1050
+ | MidiOutputData.Sysex sysex -> outPort.WriteSysex now sysex
1051
+ )
1052
+ | None -> ()
1053
+
1054
+ member x.QueryStatus statusType = performSysExRequest ( QueryStatus statusType)
1055
+
1056
+ member x.Dump dumpRequest = performSysExRequest dumpRequest
962
1057
member x.CurrentGlobalSettingsSlot =
963
1058
match x.Dump ( QueryStatus( MachineDrumStatusType.GlobalSlot)) with
964
1059
| Some ( MachineDrumSysexResponses.StatusResponse( GlobalSlot, slot)) -> Some slot
@@ -989,7 +1084,11 @@ type MachineDrum(inPort: IMidiInput<int>, outPort: IMidiOutput<int>, getSysexNow
989
1084
( AssignMachine ( track, machine, mode)) .Sysex
990
1085
|> outPort.WriteSysex ( getSysexNowTimestamp())
991
1086
992
- (*
1087
+ member x.ChangeTrackParameter globals track parameter value =
1088
+ let cc = MDTrackParameter.GetCCForTrack parameter track
1089
+ MidiMessage.CC ( globals.MidiBaseChannel) cc value
1090
+ |> outPort.WriteMessage ( getSysexNowTimestamp())
1091
+ (*
993
1092
member x.DumpKit kit =
994
1093
performSysExRequest (DumpKit kit)
995
1094
@@ -1005,35 +1104,18 @@ module MachineDrumEventParser =
1005
1104
message.MessageType = MidiMessageType.ControllerChange
1006
1105
&& isMachineDrumChannel midiBaseChannel message.Channel.Value
1007
1106
&& ( message.Data1 >= 16 uy && message.Data1 <= 119 uy)
1008
-
1009
- type LFOEvent =
1010
- | AssignTrack of Track
1011
- | AssignDestination of MDTrackParameter
1012
- | AssignShape1 of LFOShape
1013
- | AssignShape2 of LFOShape
1014
- | AssignType of LFOType
1015
-
1016
- type MachineDrumEvent =
1017
- | TrackLevel of Track * value : byte
1018
- | TrackParameter of Track * MDTrackParameter * value : byte
1019
- | TrackTrigger of Track * velocity : byte
1020
- | TrackRelease of Track
1021
- | PatternChanged of byte
1022
- | LFOSetting of Track * LFOEvent
1023
- | DelaySetting of DelayParameter * value : byte
1024
- | ReverbSetting of ReverbParameter * value : byte
1025
- | EqualizerSetting of EqualizerParameter * value : byte
1026
- | CompressorSetting of CompressorParameter * value : byte
1027
- | Unknown of MidiMessage
1028
- | MachineDrumSysex of MachineDrumSysexResponses
1029
- | Sysex of byte array
1107
+ let trackOffset cc =
1108
+ if cc <= 39 uy then 0 uy
1109
+ elif cc <= 63 uy then 1 uy
1110
+ elif cc <= 95 uy then 2 uy
1111
+ else 3 uy
1030
1112
1031
1113
type TimestampedMessage < 't > = {
1032
1114
Timestamp : int
1033
1115
Message: 't
1034
1116
}
1035
1117
1036
- type MachineDrumEventListener ( md : MachineDrum , getTimestamp : unit -> int ) =
1118
+ type MachineDrumEventListener ( md : MachineDrum , getNow : unit -> int ) =
1037
1119
let mutable mdGlobalSettings = md.CurrentGlobalSettings
1038
1120
let midiIn = md.MidiOutPort
1039
1121
//let mutable lastKit = {Timestamp = 0; Message = None }
@@ -1050,11 +1132,8 @@ type MachineDrumEventListener(md: MachineDrum, getTimestamp : unit -> int) =
1050
1132
elif MachineDrumEventParser.isMachineDrumControlChange midiBaseChannel message then
1051
1133
let channel = message.Channel.Value
1052
1134
let cc = message.Data1
1053
- let trackOffset =
1054
- if cc <= 39 uy then 0 uy
1055
- elif cc <= 63 uy then 1 uy
1056
- elif cc <= 95 uy then 2 uy
1057
- else 3 uy
1135
+ let trackOffset = MachineDrumEventParser.trackOffset cc
1136
+
1058
1137
let midiChannelOffset = channel - midiBaseChannel
1059
1138
let track = Track.trackForValue (( midiChannelOffset * 4 uy) + trackOffset)
1060
1139
let parameter = MDTrackParameter.GetParameterForCC cc
@@ -1124,7 +1203,7 @@ type MachineDrumEventListener(md: MachineDrum, getTimestamp : unit -> int) =
1124
1203
| _ -> Sysex sysex
1125
1204
| _ ->
1126
1205
if sysex.[ 0 .. 5 ] = Sysex.mdHeader then
1127
- MachineDrumSysex ( MachineDrumSysexResponses.BuildResponse sysex)
1206
+ MachineDrumSysex ( MachineDrumSysexResponses.BuildResponse sysex) .Value
1128
1207
else
1129
1208
Sysex sysex
1130
1209
@@ -1134,7 +1213,7 @@ type MachineDrumEventListener(md: MachineDrum, getTimestamp : unit -> int) =
1134
1213
)
1135
1214
let sysexListener = midiIn.SysexReceived.Subscribe( fun m ->
1136
1215
// TODO TODO
1137
- let timestamp = getTimestamp ()
1216
+ let timestamp = getNow ()
1138
1217
let message = onSysexMessage m
1139
1218
{ Timestamp = timestamp; Message = message } |> event.Trigger
1140
1219
)
@@ -1145,6 +1224,29 @@ type MachineDrumEventListener(md: MachineDrum, getTimestamp : unit -> int) =
1145
1224
1146
1225
[<CLIEvent>] member x.Event = event.Publish
1147
1226
1227
+ let mdDetection getTimestamp inputs outputs onSysex withMachineDrum =
1228
+ let queryMessage = QueryStatus( GlobalSlot)
1229
+ let onSysex =
1230
+ match onSysex with
1231
+ | Some onSysex -> onSysex
1232
+ | _ ->
1233
+ ( fun sysex ->
1234
+ match MachineDrumSysexResponses.BuildResponse sysex with
1235
+ | Some( MachineDrumSysexResponses.GlobalSettingsResponse globals) -> true
1236
+ | _ -> false
1237
+ )
1238
+
1239
+ Sysex.deviceInquiry inputs outputs
1240
+ onSysex
1241
+ ( fun midiOut ->
1242
+ midiOut.WriteSysex 0 ( QueryStatus( GlobalSlot) .Sysex)
1243
+ )
1244
+ ( fun midiIn midiOut ->
1245
+ let md = MachineDrum( midiIn, midiOut, getTimestamp)
1246
+ withMachineDrum md
1247
+ { new System.IDisposable with member x.Dispose () = () }
1248
+ )
1249
+
1148
1250
1149
1251
1150
1252
let effectsParameters =
0 commit comments