Skip to content

Commit f5b3422

Browse files
author
Simon Holliday
committed
- Use new external PyMidiDefs for canonical MIDI definitions
1 parent 6772a70 commit f5b3422

6 files changed

Lines changed: 66 additions & 423 deletions

File tree

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2599,7 +2599,9 @@ Turns real-time International Space Station telemetry into an evolving compositi
25992599
- `subsequence.constants.durations` provides beat-based duration constants. Import as `import subsequence.constants.durations as dur` and write `length = 9 * dur.SIXTEENTH` or `step = dur.DOTTED_EIGHTH` instead of raw floats. Constants: `THIRTYSECOND`, `SIXTEENTH`, `DOTTED_SIXTEENTH`, `TRIPLET_EIGHTH`, `EIGHTH`, `DOTTED_EIGHTH`, `TRIPLET_QUARTER`, `QUARTER`, `DOTTED_QUARTER`, `HALF`, `DOTTED_HALF`, `WHOLE`.
26002600
- `subsequence.constants.velocity` provides MIDI velocity constants. `DEFAULT_VELOCITY = 100` (most notes), `DEFAULT_CHORD_VELOCITY = 90` (harmonic content), `VELOCITY_SHAPE_LOW = 64` and `VELOCITY_SHAPE_HIGH = 127` (velocity shaping boundaries), `MIN_VELOCITY = 0`, `MAX_VELOCITY = 127`.
26012601
- `subsequence.constants.gm_drums` provides the General MIDI Level 1 drum note map. `GM_DRUM_MAP` can be passed as `drum_note_map`; individual constants like `KICK_1` are also available.
2602-
- `subsequence.constants.midi_notes` provides named MIDI note constants C0–G9 (MIDI 12–127). Import as `import subsequence.constants.midi_notes as notes`. Convention: `C4 = 60` (Middle C, MMA standard). Naturals: `C4`, `D4`, … `B4`. Sharps: `CS4` (C♯4), `DS4`, `FS4`, `GS4`, `AS4`. Use instead of raw integers: `root = notes.E2` (40), `p.note(notes.A4)` (69).
2602+
- `subsequence.constants.instruments.gm_instruments` provides all 128 General MIDI Level 1 instrument program numbers. `GM_INSTRUMENT_MAP` for string lookup, `GM_INSTRUMENT_NAMES` for display, `GM_FAMILIES` for family ranges, and individual constants like `VIOLIN`, `FLUTE`, etc.
2603+
- `subsequence.constants.midi_notes` provides named MIDI note constants C_NEG1–G9 (MIDI 0–127). Import as `import subsequence.constants.midi_notes as notes`. Convention: `C4 = 60` (Middle C, MMA standard). Naturals: `C4`, `D4`, … `B4`. Sharps: `CS4` (C♯4), `DS4`, `FS4`, `GS4`, `AS4`. Also provides `note_to_name(60) → "C4"` and `name_to_note("Db4") → 61`. Use instead of raw integers: `root = notes.E2` (40), `p.note(notes.A4)` (69).
2604+
- `subsequence.constants.midi_cc` provides named MIDI CC constants (0–127). Import as `import subsequence.constants.midi_cc as cc`. Constants: `FILTER_CUTOFF` (74), `SUSTAIN_PEDAL` (64), `MODULATION_WHEEL` (1), `VOLUME` (7), `PAN` (10), `ALL_NOTES_OFF` (123), etc. `GM_CC_MAP` dict for string lookup.
26032605
- `subsequence.constants.pulses` provides pulse-based MIDI timing constants used internally by the engine.
26042606

26052607
### Infrastructure
@@ -2681,6 +2683,7 @@ Subsequence makes use of these excellent open-source libraries:
26812683
| [mido ↗](https://github.com/mido/mido) | MIDI message handling and file I/O | MIT |
26822684
| [python-rtmidi ↗](https://github.com/SpotlightKid/python-rtmidi) | Real-time MIDI I/O | MIT |
26832685
| [python-osc ↗](https://github.com/attwad/python-osc) | OSC protocol support | Unlicense |
2686+
| [pymididefs ↗](https://github.com/simonholliday/PyMidiDefs) | Canonical MIDI 1.0/2.0 constant definitions | MIT |
26842687
| [websockets ↗](https://github.com/python-websockets/websockets) | Web UI dashboard communication | BSD-3-Clause |
26852688
| [aalink ↗](https://github.com/artfwo/aalink) *(optional)* | Ableton Link integration | GPL-3.0 |
26862689

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ dependencies = [
1919
"python-rtmidi",
2020
"python-osc",
2121
"websockets",
22+
"pymididefs @ git+https://github.com/simonholliday/PyMidiDefs.git",
2223
]
2324

2425
[project.urls]

subsequence/constants/instruments/gm_drums.py

Lines changed: 5 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -23,144 +23,10 @@ def drums (p):
2323
@composition.pattern(channel=9, length=4)
2424
def drums (p):
2525
p.hit_steps(subsequence.constants.instruments.gm_drums.KICK_1, [0, 4, 8, 12], velocity=127)
26-
"""
27-
28-
import typing
29-
30-
31-
# ─── Individual note constants ───────────────────────────────────────
32-
#
33-
# General MIDI Level 1 percussion key map (notes 27-87).
34-
# Names follow the GM specification with underscores for readability.
3526
36-
HIGH_Q = 27
37-
SLAP = 28
38-
SCRATCH_PUSH = 29
39-
SCRATCH_PULL = 30
40-
STICKS = 31
41-
SQUARE_CLICK = 32
42-
METRONOME_CLICK = 33
43-
METRONOME_BELL = 34
44-
KICK_2 = 35
45-
KICK_1 = 36
46-
SIDE_STICK = 37
47-
SNARE_1 = 38
48-
HAND_CLAP = 39
49-
SNARE_2 = 40
50-
LOW_FLOOR_TOM = 41
51-
HI_HAT_CLOSED = 42
52-
HIGH_FLOOR_TOM = 43
53-
HI_HAT_PEDAL = 44
54-
LOW_TOM = 45
55-
HI_HAT_OPEN = 46
56-
LOW_MID_TOM = 47
57-
HIGH_MID_TOM = 48
58-
CRASH_1 = 49
59-
HIGH_TOM = 50
60-
RIDE_1 = 51
61-
CHINESE_CYMBAL = 52
62-
RIDE_BELL = 53
63-
TAMBOURINE = 54
64-
SPLASH_CYMBAL = 55
65-
COWBELL = 56
66-
CRASH_2 = 57
67-
VIBRASLAP = 58
68-
RIDE_2 = 59
69-
HIGH_BONGO = 60
70-
LOW_BONGO = 61
71-
MUTE_HIGH_CONGA = 62
72-
OPEN_HIGH_CONGA = 63
73-
LOW_CONGA = 64
74-
HIGH_TIMBALE = 65
75-
LOW_TIMBALE = 66
76-
HIGH_AGOGO = 67
77-
LOW_AGOGO = 68
78-
CABASA = 69
79-
MARACAS = 70
80-
SHORT_WHISTLE = 71
81-
LONG_WHISTLE = 72
82-
SHORT_GUIRO = 73
83-
LONG_GUIRO = 74
84-
CLAVES = 75
85-
HIGH_WOODBLOCK = 76
86-
LOW_WOODBLOCK = 77
87-
MUTE_CUICA = 78
88-
OPEN_CUICA = 79
89-
MUTE_TRIANGLE = 80
90-
OPEN_TRIANGLE = 81
91-
SHAKER = 82
92-
JINGLE_BELL = 83
93-
BELL_TREE = 84
94-
CASTANETS = 85
95-
MUTE_SURDO = 86
96-
OPEN_SURDO = 87
97-
98-
99-
# ─── Complete drum note map ──────────────────────────────────────────
100-
#
101-
# Pass this dict as the drum_note_map parameter to use string names
102-
# in hit_steps(), hit(), note(), euclidean(), and bresenham().
27+
Canonical source: `pymididefs <https://github.com/simonholliday/PyMidiDefs>`_.
28+
"""
10329

104-
GM_DRUM_MAP: typing.Dict[str, int] = {
105-
"high_q": HIGH_Q,
106-
"slap": SLAP,
107-
"scratch_push": SCRATCH_PUSH,
108-
"scratch_pull": SCRATCH_PULL,
109-
"sticks": STICKS,
110-
"square_click": SQUARE_CLICK,
111-
"metronome_click": METRONOME_CLICK,
112-
"metronome_bell": METRONOME_BELL,
113-
"kick_2": KICK_2,
114-
"kick_1": KICK_1,
115-
"side_stick": SIDE_STICK,
116-
"snare_1": SNARE_1,
117-
"hand_clap": HAND_CLAP,
118-
"snare_2": SNARE_2,
119-
"low_floor_tom": LOW_FLOOR_TOM,
120-
"hi_hat_closed": HI_HAT_CLOSED,
121-
"high_floor_tom": HIGH_FLOOR_TOM,
122-
"hi_hat_pedal": HI_HAT_PEDAL,
123-
"low_tom": LOW_TOM,
124-
"hi_hat_open": HI_HAT_OPEN,
125-
"low_mid_tom": LOW_MID_TOM,
126-
"high_mid_tom": HIGH_MID_TOM,
127-
"crash_1": CRASH_1,
128-
"high_tom": HIGH_TOM,
129-
"ride_1": RIDE_1,
130-
"chinese_cymbal": CHINESE_CYMBAL,
131-
"ride_bell": RIDE_BELL,
132-
"tambourine": TAMBOURINE,
133-
"splash_cymbal": SPLASH_CYMBAL,
134-
"cowbell": COWBELL,
135-
"crash_2": CRASH_2,
136-
"vibraslap": VIBRASLAP,
137-
"ride_2": RIDE_2,
138-
"high_bongo": HIGH_BONGO,
139-
"low_bongo": LOW_BONGO,
140-
"mute_high_conga": MUTE_HIGH_CONGA,
141-
"open_high_conga": OPEN_HIGH_CONGA,
142-
"low_conga": LOW_CONGA,
143-
"high_timbale": HIGH_TIMBALE,
144-
"low_timbale": LOW_TIMBALE,
145-
"high_agogo": HIGH_AGOGO,
146-
"low_agogo": LOW_AGOGO,
147-
"cabasa": CABASA,
148-
"maracas": MARACAS,
149-
"short_whistle": SHORT_WHISTLE,
150-
"long_whistle": LONG_WHISTLE,
151-
"short_guiro": SHORT_GUIRO,
152-
"long_guiro": LONG_GUIRO,
153-
"claves": CLAVES,
154-
"high_woodblock": HIGH_WOODBLOCK,
155-
"low_woodblock": LOW_WOODBLOCK,
156-
"mute_cuica": MUTE_CUICA,
157-
"open_cuica": OPEN_CUICA,
158-
"mute_triangle": MUTE_TRIANGLE,
159-
"open_triangle": OPEN_TRIANGLE,
160-
"shaker": SHAKER,
161-
"jingle_bell": JINGLE_BELL,
162-
"bell_tree": BELL_TREE,
163-
"castanets": CASTANETS,
164-
"mute_surdo": MUTE_SURDO,
165-
"open_surdo": OPEN_SURDO,
166-
}
30+
# Re-export everything from pymididefs.drums — all drum constants and the lookup dict.
31+
from pymididefs.drums import * # noqa: F401,F403
32+
from pymididefs.drums import GM_DRUM_MAP # noqa: F401 — explicit re-export for type checkers
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""General MIDI Level 1 instrument program numbers.
2+
3+
All 128 GM Level 1 instrument assignments, organised into 16 families of 8.
4+
Use these constants with ``p.program_change()`` to select instruments on
5+
GM-compatible synthesizers and sound modules.
6+
7+
Usage::
8+
9+
import subsequence.constants.instruments.gm_instruments as gm
10+
11+
@composition.pattern(channel=1, length=4)
12+
def strings (p):
13+
p.program_change(gm.VIOLIN)
14+
p.note(60, beat=0)
15+
16+
# Lookup by name
17+
gm.GM_INSTRUMENT_MAP["flute"] # 73
18+
gm.GM_INSTRUMENT_NAMES[73] # "Flute"
19+
20+
# Family ranges
21+
gm.GM_FAMILIES["piano"] # (0, 7)
22+
23+
Canonical source: `pymididefs <https://github.com/simonholliday/PyMidiDefs>`_.
24+
"""
25+
26+
# Re-export everything from pymididefs.gm — all instrument constants and lookup tables.
27+
from pymididefs.gm import * # noqa: F401,F403
28+
from pymididefs.gm import ( # noqa: F401 — explicit re-exports for type checkers
29+
GM_FAMILIES,
30+
GM_INSTRUMENT_MAP,
31+
GM_INSTRUMENT_NAMES,
32+
)

subsequence/constants/midi_cc.py

Lines changed: 11 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -8,163 +8,30 @@
88
1. **As constants** - reference CC numbers directly when sending raw MIDI or mapping features::
99
1010
import subsequence.constants.midi_cc as cc
11-
11+
1212
@composition.pattern(...)
1313
def sweep (p):
1414
p.cc(cc.FILTER_CUTOFF, 127)
1515
1616
2. **As a lookup map** - pass ``GM_CC_MAP`` if you want to allow string-based mapping in your extensions::
1717
1818
print(subsequence.constants.midi_cc.GM_CC_MAP["filter_cutoff"]) # 74
19+
20+
Canonical source: `pymididefs <https://github.com/simonholliday/PyMidiDefs>`_.
1921
"""
2022

2123
import typing
2224

23-
# ─── Common Continuous Controllers (MSB) ─────────────────────────────
24-
BANK_SELECT_MSB = 0
25-
MODULATION_WHEEL = 1
26-
BREATH_CONTROLLER = 2
27-
FOOT_CONTROLLER = 4
28-
PORTAMENTO_TIME = 5
29-
DATA_ENTRY_MSB = 6
30-
VOLUME = 7
31-
BALANCE = 8
32-
PAN = 10
33-
EXPRESSION = 11
34-
EFFECT_CONTROL_1 = 12
35-
EFFECT_CONTROL_2 = 13
36-
37-
# ─── General Purpose Controllers (MSB) ───────────────────────────────
38-
GENERAL_PURPOSE_1 = 16
39-
GENERAL_PURPOSE_2 = 17
40-
GENERAL_PURPOSE_3 = 18
41-
GENERAL_PURPOSE_4 = 19
42-
43-
# ─── Common Continuous Controllers (LSB) ─────────────────────────────
44-
BANK_SELECT_LSB = 32
45-
MODULATION_WHEEL_LSB = 33
46-
BREATH_CONTROLLER_LSB = 34
47-
FOOT_PEDAL_LSB = 36
48-
PORTAMENTO_TIME_LSB = 37
49-
DATA_ENTRY_LSB = 38
50-
51-
# ─── On/Off Switches (0-63=Off, 64-127=On) ───────────────────────────
52-
SUSTAIN_PEDAL = 64
53-
PORTAMENTO_ON_OFF = 65
54-
SOSTENUTO_PEDAL = 66
55-
SOFT_PEDAL = 67
56-
LEGATO_PEDAL = 68
57-
HOLD_2 = 69
58-
59-
# ─── Sound Controllers ───────────────────────────────────────────────
60-
SOUND_VARIATION = 70
61-
FILTER_RESONANCE = 71
62-
RELEASE_TIME = 72
63-
ATTACK_TIME = 73
64-
FILTER_CUTOFF = 74
65-
SOUND_CONTROL_6 = 75
66-
SOUND_CONTROL_7 = 76
67-
SOUND_CONTROL_8 = 77
68-
SOUND_CONTROL_9 = 78
69-
SOUND_CONTROL_10 = 79
70-
71-
# ─── General Purpose Controllers (LSB) ───────────────────────────────
72-
GENERAL_PURPOSE_5 = 80
73-
GENERAL_PURPOSE_6 = 81
74-
GENERAL_PURPOSE_7 = 82
75-
GENERAL_PURPOSE_8 = 83
76-
77-
# ─── Effect Controllers ──────────────────────────────────────────────
78-
PORTAMENTO_CONTROL = 84
79-
REVERB_DEPTH = 91
80-
TREMOLO_DEPTH = 92
81-
CHORUS_DEPTH = 93
82-
CELESTE_DEPTH = 94
83-
PHASER_DEPTH = 95
84-
85-
# ─── Parameter Control ───────────────────────────────────────────────
86-
DATA_INCREMENT = 96
87-
DATA_DECREMENT = 97
88-
NRPN_LSB = 98
89-
NRPN_MSB = 99
90-
RPN_LSB = 100
91-
RPN_MSB = 101
92-
93-
# ─── Channel Mode Messages ───────────────────────────────────────────
94-
ALL_SOUND_OFF = 120
95-
RESET_ALL_CONTROLLERS = 121
96-
LOCAL_CONTROL_ON_OFF = 122
97-
ALL_NOTES_OFF = 123
98-
OMNI_MODE_OFF = 124
99-
OMNI_MODE_ON = 125
100-
MONO_MODE_ON = 126
101-
POLY_MODE_ON = 127
25+
# Re-export everything from pymididefs.cc — all CC constants and the lookup dict.
26+
from pymididefs.cc import * # noqa: F401,F403
27+
from pymididefs.cc import CC_MAP # noqa: F401
10228

103-
# ─── Complete CC map ─────────────────────────────────────────────────
104-
#
105-
# Pass this dict to map string names to CC integers if needed.
29+
# ─── Backward-compatibility aliases ─────────────────────────────────────────
30+
# Subsequence used FOOT_PEDAL_LSB; pymididefs uses FOOT_CONTROLLER_LSB.
31+
FOOT_PEDAL_LSB = FOOT_CONTROLLER_LSB # noqa: F405
10632

33+
# Subsequence exposed the lookup dict as GM_CC_MAP; pymididefs uses CC_MAP.
10734
GM_CC_MAP: typing.Dict[str, int] = {
108-
"bank_select_msb": BANK_SELECT_MSB,
109-
"modulation_wheel": MODULATION_WHEEL,
110-
"breath_controller": BREATH_CONTROLLER,
111-
"foot_controller": FOOT_CONTROLLER,
112-
"portamento_time": PORTAMENTO_TIME,
113-
"data_entry_msb": DATA_ENTRY_MSB,
114-
"volume": VOLUME,
115-
"balance": BALANCE,
116-
"pan": PAN,
117-
"expression": EXPRESSION,
118-
"effect_control_1": EFFECT_CONTROL_1,
119-
"effect_control_2": EFFECT_CONTROL_2,
120-
"general_purpose_1": GENERAL_PURPOSE_1,
121-
"general_purpose_2": GENERAL_PURPOSE_2,
122-
"general_purpose_3": GENERAL_PURPOSE_3,
123-
"general_purpose_4": GENERAL_PURPOSE_4,
124-
"bank_select_lsb": BANK_SELECT_LSB,
125-
"modulation_wheel_lsb": MODULATION_WHEEL_LSB,
126-
"breath_controller_lsb": BREATH_CONTROLLER_LSB,
35+
**CC_MAP,
12736
"foot_pedal_lsb": FOOT_PEDAL_LSB,
128-
"portamento_time_lsb": PORTAMENTO_TIME_LSB,
129-
"data_entry_lsb": DATA_ENTRY_LSB,
130-
"sustain_pedal": SUSTAIN_PEDAL,
131-
"portamento_on_off": PORTAMENTO_ON_OFF,
132-
"sostenuto_pedal": SOSTENUTO_PEDAL,
133-
"soft_pedal": SOFT_PEDAL,
134-
"legato_pedal": LEGATO_PEDAL,
135-
"hold_2": HOLD_2,
136-
"sound_variation": SOUND_VARIATION,
137-
"filter_resonance": FILTER_RESONANCE,
138-
"release_time": RELEASE_TIME,
139-
"attack_time": ATTACK_TIME,
140-
"filter_cutoff": FILTER_CUTOFF,
141-
"sound_control_6": SOUND_CONTROL_6,
142-
"sound_control_7": SOUND_CONTROL_7,
143-
"sound_control_8": SOUND_CONTROL_8,
144-
"sound_control_9": SOUND_CONTROL_9,
145-
"sound_control_10": SOUND_CONTROL_10,
146-
"general_purpose_5": GENERAL_PURPOSE_5,
147-
"general_purpose_6": GENERAL_PURPOSE_6,
148-
"general_purpose_7": GENERAL_PURPOSE_7,
149-
"general_purpose_8": GENERAL_PURPOSE_8,
150-
"portamento_control": PORTAMENTO_CONTROL,
151-
"reverb_depth": REVERB_DEPTH,
152-
"tremolo_depth": TREMOLO_DEPTH,
153-
"chorus_depth": CHORUS_DEPTH,
154-
"celeste_depth": CELESTE_DEPTH,
155-
"phaser_depth": PHASER_DEPTH,
156-
"data_increment": DATA_INCREMENT,
157-
"data_decrement": DATA_DECREMENT,
158-
"nrpn_lsb": NRPN_LSB,
159-
"nrpn_msb": NRPN_MSB,
160-
"rpn_lsb": RPN_LSB,
161-
"rpn_msb": RPN_MSB,
162-
"all_sound_off": ALL_SOUND_OFF,
163-
"reset_all_controllers": RESET_ALL_CONTROLLERS,
164-
"local_control_on_off": LOCAL_CONTROL_ON_OFF,
165-
"all_notes_off": ALL_NOTES_OFF,
166-
"omni_mode_off": OMNI_MODE_OFF,
167-
"omni_mode_on": OMNI_MODE_ON,
168-
"mono_mode_on": MONO_MODE_ON,
169-
"poly_mode_on": POLY_MODE_ON,
17037
}

0 commit comments

Comments
 (0)