Last active
November 14, 2025 12:39
-
-
Save voxels/16a793881b00527a056dbaef27a366ac to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Foundation | |
| /// A `Sendable` struct for defining a single MIDI Note message. | |
| public struct MidiNoteMapping: Sendable, Equatable { | |
| public var channel: UInt8 | |
| public var note: UInt8 | |
| } | |
| /// A `Sendable` struct for defining a single MIDI Control Change (CC) message. | |
| public struct MidiControlChangeMapping: Sendable, Equatable { | |
| public var channel: UInt8 | |
| public var controller: UInt8 | |
| } | |
| /// Defines all the mappable controls for a single Traktor deck. | |
| public struct TraktorDeckMapping: Sendable, Equatable { | |
| // Transport | |
| public var play: MidiNoteMapping | |
| public var cue: MidiNoteMapping | |
| public var sync: MidiNoteMapping | |
| public var reverse: MidiNoteMapping | |
| // Jog Wheel | |
| public var jogTouch: MidiNoteMapping | |
| public var jogTurn: MidiControlChangeMapping // Relative | |
| // Mixer (Typically on Channel 1 for both) | |
| public var volume: MidiControlChangeMapping // Absolute | |
| public var eqHigh: MidiControlChangeMapping // Absolute | |
| public var eqMid: MidiControlChangeMapping // Absolute | |
| public var eqLow: MidiControlChangeMapping // Absolute | |
| public var filter: MidiControlChangeMapping // Absolute | |
| // Tempo | |
| public var tempo: MidiControlChangeMapping // Absolute | |
| // Hot Cues (A-H) | |
| public var hotCueA: MidiNoteMapping | |
| public var hotCueB: MidiNoteMapping | |
| public var hotCueC: MidiNoteMapping | |
| public var hotCueD: MidiNoteMapping | |
| public var hotCueE: MidiNoteMapping | |
| public var hotCueF: MidiNoteMapping | |
| public var hotCueG: MidiNoteMapping | |
| public var hotCueH: MidiNoteMapping | |
| // Looping | |
| public var loopIn: MidiNoteMapping | |
| public var loopOut: MidiNoteMapping | |
| public var reloopExit: MidiNoteMapping | |
| public var loop4Beat: MidiNoteMapping | |
| public var loop8Beat: MidiNoteMapping | |
| // Beat Jump | |
| public var beatJumpFwd: MidiNoteMapping | |
| public var beatJumpRev: MidiNoteMapping | |
| } | |
| /// A container for all deck and mixer mappings. | |
| public struct TraktorMapping: Sendable, Equatable { | |
| public var deckA: TraktorDeckMapping | |
| public var deckB: TraktorDeckMapping | |
| // Global controls (typically on Channel 1) | |
| public var crossfader: MidiControlChangeMapping | |
| public var browserRotate: MidiControlChangeMapping | |
| /// Provides a default, example mapping. | |
| /// You can edit this to match your Traktor controller setup. | |
| /// This example uses Channel 0 (MIDI Ch 1) for Deck A and Channel 1 (MIDI Ch 2) for Deck B. | |
| /// Global controls are on Channel 0. | |
| public static var defaultNI: TraktorMapping { | |
| .init( | |
| deckA: .init( | |
| play: .init(channel: 0, note: 37), // C#1 | |
| cue: .init(channel: 0, note: 39), // D#1 | |
| sync: .init(channel: 0, note: 35), // B0 | |
| reverse: .init(channel: 0, note: 36), // C1 | |
| jogTouch: .init(channel: 0, note: 40), // E1 | |
| jogTurn: .init(channel: 0, controller: 20), | |
| volume: .init(channel: 0, controller: 7), | |
| eqHigh: .init(channel: 0, controller: 10), | |
| eqMid: .init(channel: 0, controller: 11), | |
| eqLow: .init(channel: 0, controller: 12), | |
| filter: .init(channel: 0, controller: 13), | |
| tempo: .init(channel: 0, controller: 14), | |
| hotCueA: .init(channel: 0, note: 50), // D2 | |
| hotCueB: .init(channel: 0, note: 51), // D#2 | |
| hotCueC: .init(channel: 0, note: 52), // E2 | |
| hotCueD: .init(channel: 0, note: 53), // F2 | |
| hotCueE: .init(channel: 0, note: 54), // F#2 | |
| hotCueF: .init(channel: 0, note: 55), // G2 | |
| hotCueG: .init(channel: 0, note: 56), // G#2 | |
| hotCueH: .init(channel: 0, note: 57), // A2 | |
| loopIn: .init(channel: 0, note: 41), // F1 | |
| loopOut: .init(channel: 0, note: 42), // F#1 | |
| reloopExit: .init(channel: 0, note: 43), // G1 | |
| loop4Beat: .init(channel: 0, note: 44), // G#1 | |
| loop8Beat: .init(channel: 0, note: 45), // A1 | |
| beatJumpFwd: .init(channel: 0, note: 46), // A#1 | |
| beatJumpRev: .init(channel: 0, note: 47) // B1 | |
| ), | |
| deckB: .init( | |
| play: .init(channel: 1, note: 37), // C#1 | |
| cue: .init(channel: 1, note: 39), // D#1 | |
| sync: .init(channel: 1, note: 35), // B0 | |
| reverse: .init(channel: 1, note: 36), // C1 | |
| jogTouch: .init(channel: 1, note: 40), // E1 | |
| jogTurn: .init(channel: 1, controller: 20), | |
| volume: .init(channel: 1, controller: 7), | |
| eqHigh: .init(channel: 1, controller: 10), | |
| eqMid: .init(channel: 1, controller: 11), | |
| eqLow: .init(channel: 1, controller: 12), | |
| filter: .init(channel: 1, controller: 13), | |
| tempo: .init(channel: 1, controller: 14), | |
| hotCueA: .init(channel: 1, note: 50), // D2 | |
| hotCueB: .init(channel: 1, note: 51), // D#2 | |
| hotCueC: .init(channel: 1, note: 52), // E2 | |
| hotCueD: .init(channel: 1, note: 53), // F2 | |
| hotCueE: .init(channel: 1, note: 54), // F#2 | |
| hotCueF: .init(channel: 1, note: 55), // G2 | |
| hotCueG: .init(channel: 1, note: 56), // G#2 | |
| hotCueH: .init(channel: 1, note: 57), // A2 | |
| loopIn: .init(channel: 1, note: 41), // F1 | |
| loopOut: .init(channel: 1, note: 42), // F#1 | |
| reloopExit: .init(channel: 1, note: 43), // G1 | |
| loop4Beat: .init(channel: 1, note: 44), // G#1 | |
| loop8Beat: .init(channel: 1, note: 45), // A1 | |
| beatJumpFwd: .init(channel: 1, note: 46), // A#1 | |
| beatJumpRev: .init(channel: 1, note: 47) // B1 | |
| ), | |
| crossfader: .init(channel: 0, controller: 8), // Ch 1, CC 8 | |
| browserRotate: .init(channel: 0, controller: 21) // Ch 1, CC 21 | |
| ) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment