##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+## along with this program; if not, see <http://www.gnu.org/licenses/>.
id = 'midi'
name = 'MIDI'
longname = 'Musical Instrument Digital Interface'
desc = 'Musical Instrument Digital Interface (MIDI) protocol.'
license = 'gplv2+'
inputs = ['uart']
id = 'midi'
name = 'MIDI'
longname = 'Musical Instrument Digital Interface'
desc = 'Musical Instrument Digital Interface (MIDI) protocol.'
license = 'gplv2+'
inputs = ['uart']
annotations = (
('text-verbose', 'Human-readable text (verbose)'),
('text-sysreal-verbose', 'Human-readable SysReal text (verbose)'),
annotations = (
('text-verbose', 'Human-readable text (verbose)'),
('text-sysreal-verbose', 'Human-readable SysReal text (verbose)'),
return 'assuming ' + percussion_notes.get(note, 'undefined')
def check_for_garbage_flush(self, is_flushed):
return 'assuming ' + percussion_notes.get(note, 'undefined')
def check_for_garbage_flush(self, is_flushed):
self.cmd.insert(0, self.status_byte)
self.handle_garbage_msg(None)
self.cmd.insert(0, self.status_byte)
self.handle_garbage_msg(None)
def handle_sysex_msg(self, newbyte):
# SysEx message: 1 status byte, 1-3 manuf. bytes, x data bytes, EOX byte
#
def handle_sysex_msg(self, newbyte):
# SysEx message: 1 status byte, 1-3 manuf. bytes, x data bytes, EOX byte
#
#
# Note: While the MIDI lists 0xf7 as a "system common" message, it
# is actually only used with SysEx messages so it is processed there.
#
# Note: While the MIDI lists 0xf7 as a "system common" message, it
# is actually only used with SysEx messages so it is processed there.
def handle_sysrealtime_msg(self, newbyte):
# System realtime message: 0b11111ttt (t = message type)
#
def handle_sysrealtime_msg(self, newbyte):
# System realtime message: 0b11111ttt (t = message type)
#
# because they are allowed to temporarily interrupt other messages.
# The interrupted messages resume after the realtime message is done.
# Thus, they mostly leave 'self' the way it was found.
# because they are allowed to temporarily interrupt other messages.
# The interrupted messages resume after the realtime message is done.
# Thus, they mostly leave 'self' the way it was found.
- # Note: all System message code doesn't utilize self.status_byte
- old_ss_block = self.ss_block
- old_es_block = self.es_block
- self.ss_block = self.ss
- self.es_block = self.es
+ #
+ # Note: All System message codes don't utilize self.status_byte.
+ old_ss_block, old_es_block = self.ss_block, self.es_block
+ self.ss_block, self.es_block = self.ss, self.es
group = ('System Realtime', 'SysReal', 'SR')
self.putx([1, ['%s: %s' % (group[0], status_bytes[newbyte][0]),
'%s: %s' % (group[1], status_bytes[newbyte][1]),
'%s: %s' % (group[2], status_bytes[newbyte][2])]])
group = ('System Realtime', 'SysReal', 'SR')
self.putx([1, ['%s: %s' % (group[0], status_bytes[newbyte][0]),
'%s: %s' % (group[1], status_bytes[newbyte][1]),
'%s: %s' % (group[2], status_bytes[newbyte][2])]])
- self.ss_block = old_ss_block
- self.es_block = old_es_block
- # Deliberately not resetting self.cmd or self.state
+ self.ss_block, self.es_block = old_ss_block, old_es_block
+ # Deliberately not resetting self.cmd or self.state.
- max_bytes = 16 # Put a limit on the length on the hex dump
- for index in range( 0, len(self.cmd) ):
+ max_bytes = 16 # Put a limit on the length on the hex dump.
+ for index in range(len(self.cmd)):
self.cmd, self.state = [], 'IDLE'
self.hard_clear_status_byte()
def handle_state(self, state, newbyte):
# 'newbyte' can either be:
self.cmd, self.state = [], 'IDLE'
self.hard_clear_status_byte()
def handle_state(self, state, newbyte):
# 'newbyte' can either be:
- # 1. Value between 0x00-0xff, deal with the byte normally
- # 2. Value of 'None' which means flush any buffered data.
+ # 1. Value between 0x00-0xff, deal with the byte normally.
+ # 2. Value of 'None' which means "flush any buffered data".
if state == 'HANDLE CHANNEL MSG':
self.handle_channel_msg(newbyte)
elif state == 'HANDLE SYSEX MSG':
if state == 'HANDLE CHANNEL MSG':
self.handle_channel_msg(newbyte)
elif state == 'HANDLE SYSEX MSG':
self.handle_garbage_msg(newbyte)
def get_next_state(self, newbyte):
self.handle_garbage_msg(newbyte)
def get_next_state(self, newbyte):
if newbyte in range(0x80, 0xef + 1):
return 'HANDLE CHANNEL MSG'
if newbyte == 0xf0:
if newbyte in range(0x80, 0xef + 1):
return 'HANDLE CHANNEL MSG'
if newbyte == 0xf0:
return'HANDLE SYSCOMMON MSG'
if newbyte in range(0xf8, 0xff + 1):
return 'HANDLE SYSREALTIME MSG'
return'HANDLE SYSCOMMON MSG'
if newbyte in range(0xf8, 0xff + 1):
return 'HANDLE SYSREALTIME MSG'
if self.status_byte < 0x80:
return 'BUFFER GARBAGE MSG'
return self.get_next_state(self.status_byte)
if self.status_byte < 0x80:
return 'BUFFER GARBAGE MSG'
return self.get_next_state(self.status_byte)
# - Most messages: 1 status byte, 1-2 data bytes.
# - Real-time system messages: always 1 byte.
# - SysEx messages: 1 status byte, n data bytes, EOX byte.
# - Most messages: 1 status byte, 1-2 data bytes.
# - Real-time system messages: always 1 byte.
# - SysEx messages: 1 status byte, n data bytes, EOX byte.
# Aspects of the MIDI protocol that complicate decoding:
# - MIDI System Realtime messages can briefly interrupt other
# Aspects of the MIDI protocol that complicate decoding:
# - MIDI System Realtime messages can briefly interrupt other
# - "Running Status" allows for omitting the status byte in most
# scenarios if sequential messages have the same status byte.
# - System Exclusive (SysEx) messages can be terminated by ANY
# - "Running Status" allows for omitting the status byte in most
# scenarios if sequential messages have the same status byte.
# - System Exclusive (SysEx) messages can be terminated by ANY
if pdata >= 0x80 and pdata != 0xf7:
state = self.get_next_state(pdata)
if state != 'HANDLE SYSREALTIME MSG' and self.state != 'IDLE':
if pdata >= 0x80 and pdata != 0xf7:
state = self.get_next_state(pdata)
if state != 'HANDLE SYSREALTIME MSG' and self.state != 'IDLE':
self.ss, self.es = ss, es
# This is a status byte, remember the start sample.
self.ss, self.es = ss, es
# This is a status byte, remember the start sample.
state = self.get_next_state(pdata)
else:
self.ss, self.es = ss, es
state = self.get_next_state(pdata)
else:
self.ss, self.es = ss, es