MIDI | index ./free/MIDI.py |
This module offers functions: concatenate_scores(), grep(),
merge_scores(), mix_scores(), midi2opus(), midi2score(), opus2midi(),
opus2score(), play_score(), score2midi(), score2opus(), score2stats(),
score_type(), segment(), timeshift() and to_millisecs(),
where "midi" means the MIDI-file bytes (as can be put in a .mid file,
or piped into aplaymidi), and "opus" and "score" are list-structures
as inspired by Sean Burke's MIDI-Perl CPAN module.
Warning: Version 6.4 is not necessarily backward-compatible with
previous versions, in that text-data is now bytes, not strings.
This reflects the fact that many MIDI files have text data in
encodings other that ISO-8859-1, for example in Shift-JIS.
Download MIDI.py from http://www.pjb.com.au/midi/free/MIDI.py
and put it in your PYTHONPATH. MIDI.py depends on Python3.
There is also a call-compatible translation into Lua of this
module: see http://www.pjb.com.au/comp/lua/MIDI.html
The "opus" is a direct translation of the midi-file-events, where
the times are delta-times, in ticks, since the previous event.
The "score" is more human-centric; it uses absolute times, and
combines the separate note_on and note_off events into one "note"
event, with a duration:
['note', start_time, duration, channel, note, velocity] # in a "score"
EVENTS (in an "opus" structure)
['note_off', dtime, channel, note, velocity] # in an "opus"
['note_on', dtime, channel, note, velocity] # in an "opus"
['key_after_touch', dtime, channel, note, velocity]
['control_change', dtime, channel, controller(0-127), value(0-127)]
['patch_change', dtime, channel, patch]
['channel_after_touch', dtime, channel, velocity]
['pitch_wheel_change', dtime, channel, pitch_wheel]
['text_event', dtime, text]
['copyright_text_event', dtime, text]
['track_name', dtime, text]
['instrument_name', dtime, text]
['lyric', dtime, text]
['marker', dtime, text]
['cue_point', dtime, text]
['text_event_08', dtime, text]
['text_event_09', dtime, text]
['text_event_0a', dtime, text]
['text_event_0b', dtime, text]
['text_event_0c', dtime, text]
['text_event_0d', dtime, text]
['text_event_0e', dtime, text]
['text_event_0f', dtime, text]
['end_track', dtime]
['set_tempo', dtime, tempo]
['smpte_offset', dtime, hr, mn, se, fr, ff]
['time_signature', dtime, nn, dd, cc, bb]
['key_signature', dtime, sf, mi]
['sequencer_specific', dtime, raw]
['raw_meta_event', dtime, command(0-255), raw]
['sysex_f0', dtime, raw]
['sysex_f7', dtime, raw]
['song_position', dtime, song_pos]
['song_select', dtime, song_number]
['tune_request', dtime]
DATA TYPES
channel = a value 0 to 15
controller = 0 to 127 (see http://www.pjb.com.au/muscript/gm.html#cc )
dtime = time measured in "ticks", 0 to 268435455
velocity = a value 0 (soft) to 127 (loud)
note = a value 0 to 127 (middle-C is 60)
patch = 0 to 127 (see http://www.pjb.com.au/muscript/gm.html )
pitch_wheel = a value -8192 to 8191 (0x1FFF)
raw = bytes, of length 0 or more (for sysex events see below)
sequence_number = a value 0 to 65,535 (0xFFFF)
song_pos = a value 0 to 16,383 (0x3FFF)
song_number = a value 0 to 127
tempo = microseconds per crochet (quarter-note), 0 to 16777215
text = bytes, of length 0 or more
ticks = the number of ticks per crochet (quarter-note)
In sysex_f0 events, the raw data must not start with a \xF0 byte,
since this gets added automatically;
but it must end with an explicit \xF7 byte!
In the very unlikely case that you ever need to split sysex data
into one sysex_f0 followed by one or more sysex_f7s, then only the
last of those sysex_f7 events must end with the explicit \xF7 byte
(again, the raw data of individual sysex_f7 events must not start
with any \xF7 byte, since this gets added automatically).
Since version 6.4, text data is in bytes, not in a ISO-8859-1 string.
GOING THROUGH A SCORE WITHIN A PYTHON PROGRAM
channels = {2,3,5,8,13}
itrack = 1 # skip 1st element which is ticks
while itrack < len(score):
for event in score[itrack]:
if event[0] == 'note': # for example,
pass # do something to all notes
# or, to work on events in only particular channels...
channel_index = MIDI.Event2channelindex.get(event[0], False)
if channel_index and (event[channel_index] in channels):
pass # do something to channels 2,3,5,8 and 13
itrack += 1
Copyright 2009 Peter J. Billam
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Modules | ||||||
|
Functions | ||
|
Data | ||
All_events = ('note_off', 'note_on', 'key_after_touch', 'control_change', 'patch_change', 'channel_after_touch', 'pitch_wheel_change', 'text_event', 'copyright_text_event', 'track_name', 'instrument_name', 'lyric', 'marker', 'cue_point', 'text_event_08', 'text_event_09', 'text_event_0a', 'text_event_0b', 'text_event_0c', 'text_event_0d', ...) Event2channelindex = {'channel_after_touch': 2, 'control_change': 2, 'key_after_touch': 2, 'note': 3, 'note_off': 2, 'note_on': 2, 'patch_change': 2, 'pitch_wheel_change': 2} MIDI_events = ('note_off', 'note_on', 'key_after_touch', 'control_change', 'patch_change', 'channel_after_touch', 'pitch_wheel_change') Meta_events = ('text_event', 'copyright_text_event', 'track_name', 'instrument_name', 'lyric', 'marker', 'cue_point', 'text_event_08', 'text_event_09', 'text_event_0a', 'text_event_0b', 'text_event_0c', 'text_event_0d', 'text_event_0e', 'text_event_0f', 'end_track', 'set_tempo', 'smpte_offset', 'time_signature', 'key_signature', ...) Nontext_meta_events = ('end_track', 'set_tempo', 'smpte_offset', 'time_signature', 'key_signature', 'sequencer_specific', 'raw_meta_event', 'sysex_f0', 'sysex_f7', 'song_position', 'song_select', 'tune_request') Notenum2percussion = {35: 'Acoustic Bass Drum', 36: 'Bass Drum 1', 37: 'Side Stick', 38: 'Acoustic Snare', 39: 'Hand Clap', 40: 'Electric Snare', 41: 'Low Floor Tom', 42: 'Closed Hi-Hat', 43: 'High Floor Tom', 44: 'Pedal Hi-Hat', ...} Number2patch = {0: 'Acoustic Grand', 1: 'Bright Acoustic', 2: 'Electric Grand', 3: 'Honky-Tonk', 4: 'Electric Piano 1', 5: 'Electric Piano 2', 6: 'Harpsichord', 7: 'Clav', 8: 'Celesta', 9: 'Glockenspiel', ...} Text_events = ('text_event', 'copyright_text_event', 'track_name', 'instrument_name', 'lyric', 'marker', 'cue_point', 'text_event_08', 'text_event_09', 'text_event_0a', 'text_event_0b', 'text_event_0c', 'text_event_0d', 'text_event_0e', 'text_event_0f') Version = '6.7' VersionDate = '20201120' |