Skip to content

Commit d0e9fd2

Browse files
monomachine model and midi docs
1 parent ca663df commit d0e9fd2

File tree

3 files changed

+144
-53
lines changed

3 files changed

+144
-53
lines changed

src/Midinette.Elektron/Elektron.MonoMachine.fs

Lines changed: 119 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,23 @@ open System
44
open Midi
55
open Elektron.Platform
66
open Elektron.Platform.SilverMachines
7-
7+
type midi7 = byte
8+
type midi8 = byte
9+
type LFOShape =
10+
| Tri | InvertedTri
11+
| Saw | InvertedSaw
12+
| Square | InvertedSquare
13+
| Exp | InvertedExp
14+
| Ramp | InvertedRamp
15+
| Random
16+
with
17+
static member FromCCValue (value: midi7) =
18+
match value with
19+
| _ -> failwithf "%i not defined" value
20+
21+
22+
type LFOMultiplier = Times1 | Times2 | Times4 | Times8 | Times16 | Times32 | Times64
23+
824
module internal Helpers =
925
let monoMachineHeader = [|
1026
0xf0uy
@@ -21,6 +37,7 @@ module internal Helpers =
2137

2238
open Helpers
2339
open System.Text
40+
open System.Security.Authentication.ExtendedProtection
2441

2542
type MonoMachineStatusType =
2643
| GlobalSlot
@@ -52,9 +69,6 @@ with
5269
| v -> failwithf "unknown status type %i" v
5370

5471

55-
56-
57-
5872
type Track = Track1 | Track2 | Track3 | Track4 | Track5 | Track6
5973
with
6074
static member FromByte b =
@@ -234,7 +248,8 @@ with
234248
baseFrequency = 0 //globalData.[0x104..] |> Elektron.fourBytesToBigEndianInt
235249
}
236250
else
237-
failwithf "can't parse globalsettings from sysex %A" sysexData
251+
printf "can't parse globalsettings from sysex %A" sysexData
252+
None
238253

239254
type MonoMachineSysexResponse =
240255
| GlobalSettings of GlobalSettings
@@ -449,7 +464,23 @@ type MonoMachineParameterPage =
449464
| Filter
450465
| Effects
451466
| LFO of OneToThree
452-
type MonoMachineParameter =
467+
468+
[<RequireQualifiedAccess>]
469+
type LFOTrigMode = Free | Trig | Hold | One | Half
470+
471+
472+
473+
type LFOParameter =
474+
| Page //of MonoMachineParameterPage
475+
| Dest //of MonoMachineParameter
476+
| TrigType //of LFOTrigMode
477+
| Wave //of LFOShape
478+
| Multiplier //of LFOMultiplier
479+
| Speed //of speed: byte
480+
| Interlace //of byte
481+
| Depth //of byte
482+
483+
and MonoMachineParameter =
453484
| SynthParam1
454485
| SynthParam2
455486
| SynthParam3
@@ -482,15 +513,7 @@ type MonoMachineParameter =
482513
| EffectDelayFeedback
483514
| EffectDelayFilterBase
484515
| EffectDelayFilterWidth
485-
| LFOPage of OneToThree
486-
| LFODest of OneToThree
487-
| LFOTrig of OneToThree
488-
| LFOWave of OneToThree
489-
| LFOMultiplier of OneToThree
490-
| LFOSpeed of OneToThree
491-
| LFOInterlace of OneToThree
492-
| LFODepth of OneToThree
493-
516+
| LFOParameter of OneToThree * LFOParameter
494517

495518
module MonoMachineControlChangeLogic =
496519

@@ -508,20 +531,19 @@ module MonoMachineControlChangeLogic =
508531
let page = OneToThree.One
509532
let pageBase = getLFOCCPageBase page
510533
if isCCLFOPageForPage page cc then
534+
511535
match cc - pageBase with
512-
| 0uy -> LFOPage page |> Some
513-
| 1uy -> LFODest page |> Some
514-
| 2uy -> LFOTrig page |> Some
515-
| 3uy -> LFOWave page |> Some
516-
| 4uy -> LFOMultiplier page |> Some
517-
| 5uy -> LFOSpeed page |> Some
518-
| 6uy -> LFOInterlace page |> Some
519-
| 7uy -> LFODepth page |> Some
536+
| 0uy -> (page, Page ) |> LFOParameter |> Some
537+
| 1uy -> (page, Dest ) |> LFOParameter |> Some
538+
| 2uy -> (page, TrigType ) |> LFOParameter |> Some
539+
| 3uy -> (page, Wave ) |> LFOParameter |> Some
540+
| 4uy -> (page, Multiplier) |> LFOParameter |> Some
541+
| 5uy -> (page, Speed ) |> LFOParameter |> Some
542+
| 6uy -> (page, Interlace ) |> LFOParameter |> Some
543+
| 7uy -> (page, Depth ) |> LFOParameter |> Some
520544
| _ -> None
521-
else
522-
None
523-
524-
545+
else
546+
None
525547

526548
let getCC parameter =
527549
match parameter with
@@ -533,14 +555,14 @@ module MonoMachineControlChangeLogic =
533555
| FilterAttack -> 76uy | FilterDecay -> 77uy | FilterBaseOffset -> 78uy | FilterWidthOffset -> 79uy
534556
| EffectEqFrequency -> 80uy | EffectEqGain -> 81uy | EffectSampleRateReduction -> 82uy | EffectDelayTime -> 83uy
535557
| EffectDelaySend -> 84uy | EffectDelayFeedback -> 85uy | EffectDelayFilterBase -> 86uy | EffectDelayFilterWidth -> 87uy
536-
| LFOPage page -> getLFOCCPageBase page + 0uy
537-
| LFODest page -> getLFOCCPageBase page + 1uy
538-
| LFOTrig page -> getLFOCCPageBase page + 2uy
539-
| LFOWave page -> getLFOCCPageBase page + 3uy
540-
| LFOMultiplier page -> getLFOCCPageBase page + 4uy
541-
| LFOSpeed page -> getLFOCCPageBase page + 5uy
542-
| LFOInterlace page -> getLFOCCPageBase page + 6uy
543-
| LFODepth page -> getLFOCCPageBase page + 7uy
558+
| LFOParameter(page, Page ) -> getLFOCCPageBase page + 0uy
559+
| LFOParameter(page, Dest ) -> getLFOCCPageBase page + 1uy
560+
| LFOParameter(page, TrigType ) -> getLFOCCPageBase page + 2uy
561+
| LFOParameter(page, Wave ) -> getLFOCCPageBase page + 3uy
562+
| LFOParameter(page, Multiplier) -> getLFOCCPageBase page + 4uy
563+
| LFOParameter(page, Speed ) -> getLFOCCPageBase page + 5uy
564+
| LFOParameter(page, Interlace ) -> getLFOCCPageBase page + 6uy
565+
| LFOParameter(page, Depth ) -> getLFOCCPageBase page + 7uy
544566

545567
let getFromCC cc =
546568
match cc with
@@ -576,45 +598,67 @@ module MonoMachineControlChangeLogic =
576598
| 85uy -> Some EffectDelayFeedback
577599
| 86uy -> Some EffectDelayFilterBase
578600
| 87uy -> Some EffectDelayFilterWidth
579-
| cc ->
580-
getLfoParameter cc
581-
582-
type LFOEvent =
583-
| AssignDest of Track * MonoMachineParameter
601+
| cc -> getLfoParameter cc
584602

585603

604+
605+
type LFOEvent =
606+
| AssignDest of pickedPage: MonoMachineParameterPage option * pickedDest: byte option * MonoMachineParameter option
607+
| PickShape of LFOShape
608+
| ChangeDepth of depth: byte
609+
| ChangeTime of time: byte * LFOMultiplier
610+
type MonoMachineControlChange =
611+
| JoystickUp
612+
| JoystickDown
613+
| Mute
614+
| MonoMachineParameter of MonoMachineParameter
615+
| AllNotesOff
586616
type MonoMachineEvent =
587617
| TrackLevel of Track * value: byte
588618
| TrackParameter of Track * MonoMachineParameter * value: byte
589619
| TrackTrigger of Track * note: byte * velocity: byte
590620
| TrackRelease of Track * note: byte * velocity: byte
591621
| PatternChanged of PatternLocator
592-
| LFOSetting of Track * LFOEvent
622+
| LFOSetting of Track * LFOParameter * byte
623+
| LFOEvent of Track * OneToThree * LFOEvent
593624
| Unknown of MidiMessage
594625
| MonoMachineSysex of MonoMachineSysexResponse
595626
| Sysex of byte array
596627
| KitChanged of byte
597628
| SequencerStarted
598629
| SequencerStopped
599630
| PatternSelected of PatternLocator
631+
//| ControlChange of MonoMachineControlChange
600632
//| Note of Track * noteNumber: byte * velocity: byte
601633

602-
603-
604-
605634
type TimestampedMessage<'t> = {
606635
Timestamp : int
607636
Message: 't
608637
}
609638

610-
639+
open Midi.MessageMatching
611640
type MonoMachineEventListener(getNow: unit -> int, mm: MonoMachine) =
612641
let settings = mm.CurrentGlobalSettings
613642
//let settings = { GlobalSettings.midiBaseChannel = 0uy }
614643
let midiIn = mm.MidiOutPort
615644
let midiRealtimeState = Midi.Registers.MidiRealtimeState()
616645
let event = new Event<_>()
617-
646+
647+
let midiChannelStates =
648+
let tracks =
649+
[|
650+
Track1
651+
Track2
652+
Track3
653+
Track4
654+
Track5
655+
Track6
656+
|]
657+
658+
tracks
659+
|> Array.map (fun t -> t, Midi.Registers.MidiChannelState<_>())
660+
|> dict
661+
618662
let makeMessage timestamp m = { Timestamp = timestamp; Message = m}
619663

620664
let onChannelMessage (midiEvent: MidiEvent<_>) =
@@ -627,15 +671,37 @@ type MonoMachineEventListener(getNow: unit -> int, mm: MonoMachine) =
627671
if midiChannelIsTrack then
628672
let track = Track.FromByte (byte (messageChannel - settings.midiChannel))
629673
match message with
630-
| Midi.MessageMatching.NoteOn (_, note, velocity) -> TrackTrigger(track, note, velocity)
631-
| Midi.MessageMatching.NoteOff (_, note, velocity) -> TrackRelease(track, note, velocity)
632-
| Midi.MessageMatching.ProgramChange {program = program} ->
633-
674+
| NoteOn (_, note, velocity) -> TrackTrigger(track, note, velocity)
675+
| NoteOff (_, note, velocity) -> TrackRelease(track, note, velocity)
676+
| ProgramChange {program = program} ->
634677
let locator = PatternLocator.FromByte program
635678
PatternSelected(locator)
636-
| _ -> Unknown message
637-
638-
679+
//| CC {control} ->
680+
681+
//Unknown message
682+
| _ ->
683+
Unknown message
684+
685+
(*| MessageMatching.CC {Midi.MessageMatching.ControlChange.channel = channel; control; value} ->
686+
match control with
687+
| 0x01uy -> JoystickUp |> MonoMachineEvent.ControlChange
688+
| 0x02uy -> JoystickDown |> MonoMachineEvent.ControlChange
689+
| 0x03uy -> Mute |> MonoMachineEvent.ControlChange
690+
| _ ->
691+
match getFromCC cc with
692+
| Some mmParam ->
693+
let lfoEvent =
694+
match mmParam with
695+
| LFOParameter(page, LFOParameter.Page) -> AssignDest(Some paramPage, None, None) |> Some
696+
| LFOParameter(page, LFOParameter.Dest) -> AssignDest(None, Some value, None) |> Some
697+
| LFOParameter(page, LFOParameter.Wave) -> LFOShape.FromCCValue value |> PickShape |> Some
698+
| _ -> None
699+
match lfoEvent with
700+
| Some lfoEvent ->
701+
LFOEvent(track, page, lfoEvent)
702+
| _ -> Unknown message
703+
| _ -> Unknown message
704+
| _ -> Unknown message*)
639705
else
640706
Unknown message
641707

src/Midinette.Elektron/Midinette.Elektron.fsproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
<TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
44
<OutputPath>..\..\build\$(Configuration)\$(Platform)</OutputPath>
55
</PropertyGroup>
6+
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard2.0|AnyCPU'">
7+
<WarningLevel>4</WarningLevel>
8+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
9+
<WarningsAsErrors />
10+
</PropertyGroup>
611
<ItemGroup>
712
<ProjectReference Include="..\Midinette\Midinette.fsproj" />
813
</ItemGroup>

src/Midinette/Midi.fs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,45 @@ type MidiNote =
1414
| ASharp = 0xauy
1515
| B = 0xbuy
1616

17+
// https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message
1718
type MidiMessageType =
19+
/// This message is sent when a note is released (ended). (kkkkkkk) is the key (note) number. (vvvvvvv) is the velocity.
1820
| NoteOff = 0x80uy
21+
/// This message is sent when a note is depressed (start). (kkkkkkk) is the key (note) number. (vvvvvvv) is the velocity.
1922
| NoteOn = 0x90uy
23+
/// This message is most often sent by pressing down on the key after it "bottoms out". (kkkkkkk) is the key (note) number. (vvvvvvv) is the pressure value.
2024
| PolyKeyPressure = 0xa0uy
25+
/// This message is sent when a controller value changes. Controllers include devices such as pedals and levers. Controller numbers 120-127 are reserved as "Channel Mode Messages" (below). (ccccccc) is the controller number (0-119). (vvvvvvv) is the controller value (0-127).
2126
| ControllerChange = 0xb0uy
27+
/// This message sent when the patch number changes. (ppppppp) is the new program number.
2228
| ProgramChange = 0xc0uy
29+
/// This message is most often sent by pressing down on the key after it "bottoms out". This message is different from polyphonic after-touch. Use this message to send the single greatest pressure value (of all the current depressed keys). (vvvvvvv) is the pressure value.
2330
| ChannelPressure = 0xd0uy
31+
/// Pitch Bend Change. This message is sent to indicate a change in the pitch bender (wheel or lever, typically). The pitch bender is measured by a fourteen bit value. Center (no pitch change) is 2000H. Sensitivity is a function of the receiver, but may be set using RPN 0. (lllllll) are the least significant 7 bits. (mmmmmmm) are the most significant 7 bits.
2432
| PitchBendChange = 0xe0uy
33+
///
2534
| SysEx = 240uy
35+
/// MIDI Time Code Quarter Frame. nnn = Message Type dddd = Values
2636
| MidiTimeCodeQuarterFrame = 241uy
37+
/// This is an internal 14 bit register that holds the number of MIDI beats (1 beat= six MIDI clocks) since the start of the song. l is the LSB, m the MSB.
2738
| SongPositionPointer = 242uy
39+
/// The Song Select specifies which sequence or song is to be played.
2840
| SongSelect = 243uy
41+
/// Tune Request. Upon receiving a Tune Request, all analog synthesizers should tune their oscillators.
2942
| TuneRequest = 246uy
43+
/// End of Exclusive. Used to terminate a System Exclusive dump (see above).
3044
| SysExEnd = 247uy
45+
/// Timing Clock. Sent 24 times per quarter note when synchronization is required (see text).
3146
| TimingClock = 248uy
47+
/// Start. Start the current sequence playing. (This message will be followed with Timing Clocks).
3248
| Start = 250uy
49+
/// Continue. Continue at the point the sequence was Stopped.
3350
| Continue = 251uy
51+
/// Stop. Stop the current sequence.
3452
| Stop = 252uy
53+
/// Active Sensing. This message is intended to be sent repeatedly to tell the receiver that a connection is alive. Use of this message is optional. When initially received, the receiver will expect to receive another Active Sensing message each 300ms (max), and if it does not then it will assume that the connection has been terminated. At termination, the receiver will turn off all voices and return to normal (non- active sensing) operation.
3554
| ActiveSensing = 254uy
55+
/// Reset. Reset all receivers in the system to power-up status. This should be used sparingly, preferably under manual control. In particular, it should not be sent on power-up.
3656
| SystemReset = 255uy
3757

3858

0 commit comments

Comments
 (0)