Skip to content

Commit

Permalink
Removed MidiFile errors option. It should have been a branch.
Browse files Browse the repository at this point in the history
  • Loading branch information
olemb committed Oct 29, 2016
1 parent 352491a commit 0994d0e
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 140 deletions.
2 changes: 1 addition & 1 deletion bin/mido-play
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def parse_args():


def play_file(output, filename, print_messages):
midi_file = MidiFile(filename, errors='warn')
midi_file = MidiFile(filename)

print('Playing {}.'.format(midi_file.filename))
length = midi_file.length
Expand Down
2 changes: 1 addition & 1 deletion mido/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
from .messages import parse_string, parse_string_stream, format_as_string
from .frozen import FrozenMessage, FrozenMetaMessage, freeze
from .parser import Parser, parse, parse_all
from .midifiles import MidiFile, MidiFileError, MidiTrack, merge_tracks
from .midifiles import MidiFile, MidiTrack, merge_tracks
from .midifiles import MetaMessage, bpm2tempo, tempo2bpm
from .syx import read_syx_file, write_syx_file

Expand Down
2 changes: 1 addition & 1 deletion mido/midifiles/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .meta import bpm2tempo, tempo2bpm, MetaMessage
from .tracks import MidiTrack, merge_tracks
from .midifiles import MidiFile, MidiFileError
from .midifiles import MidiFile
163 changes: 50 additions & 113 deletions mido/midifiles/midifiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import time
import string
import struct
import warnings
from ..messages import Message, SPEC_BY_STATUS
from .meta import MetaMessage, build_meta_message, meta_charset
from .meta import MetaSpec, add_meta_spec, encode_variable_int
Expand All @@ -31,11 +30,6 @@
DEFAULT_TEMPO = 500000
DEFAULT_TICKS_PER_BEAT = 480


class MidiFileError(IOError):
pass


def print_byte(byte, pos=0):
char = chr(byte)
if char.isspace() or not char in string.printable:
Expand All @@ -44,19 +38,7 @@ def print_byte(byte, pos=0):
print(' {:06x}: {:02x} {}'.format(pos, byte, char))


class FileWrapper(object):
def __init__(self, file, error_mode='raise'):
self.file = file

def read(self, size):
return self.file.read(size)

def tell(self):
return self.file.tell()



class DebugFileWrapper(FileWrapper):
class DebugFileWrapper(object):
def __init__(self, file):
self.file = file

Expand Down Expand Up @@ -88,37 +70,6 @@ def _dbg(text=''):
print(text)


def _handle_error(error_msg, error_mode):
if error_mode == 'raise':
raise MidiFileError(error_msg)
elif error_mode == 'warn':
warnings.warn(error_msg)
elif error_mode == 'debug':
_dbg(error_msg)
elif error_mode == 'ignore':
pass
else:
raise ValueError('unknown error mode {!r}'.format(error_mode))


def _clip_data_bytes(data_bytes, error_mode):
"""Clip data byte to 0..127.
Raises MidiFileError if error_mode =='raise'.
"""

clipped_bytes = []
for byte in data_bytes:
if byte > 127:
_handle_error('out of range data byte in MIDI file', error_mode)
if error_mode != 'raise':
byte = 127

clipped_bytes.append(byte)

return clipped_bytes


# We can't use the chunk module for two reasons:
#
# 1. we may have mixed big and little endian chunk sizes. (RIFF is
Expand Down Expand Up @@ -151,7 +102,7 @@ def read_file_header(infile):
return struct.unpack('>hhh', data[:6])


def read_message(infile, status_byte, peek_data, delta, error_mode):
def read_message(infile, status_byte, peek_data, delta):
try:
spec = SPEC_BY_STATUS[status_byte]
except LookupError:
Expand All @@ -161,12 +112,14 @@ def read_message(infile, status_byte, peek_data, delta, error_mode):
size = spec['length'] - 1 - len(peek_data)
data_bytes = peek_data + read_bytes(infile, size)

data_bytes = _clip_data_bytes(data_bytes, error_mode)
for byte in data_bytes:
if byte > 127:
raise IOError('data byte must be in range 0..127')

return Message.from_safe_bytes([status_byte] + data_bytes, time=delta)


def read_sysex(infile, delta, error_mode):
def read_sysex(infile, delta):
length = read_variable_int(infile)
data = read_bytes(infile, length)

Expand All @@ -177,8 +130,6 @@ def read_sysex(infile, delta, error_mode):
if data and data[-1] == 0xf7:
data = data[:-1]

data_bytes = _clip_data_bytes(data_bytes, error_mode)

return Message('sysex', data=data, time=delta)


Expand All @@ -192,16 +143,14 @@ def read_variable_int(infile):
return delta


def read_meta_message(infile, delta, error_mode):
def read_meta_message(infile, delta):
type = read_byte(infile)
length = read_variable_int(infile)
# Meta message data bytes are allowed to be > 127, so they don't
# need to be clipped.
data = read_bytes(infile, length)
return build_meta_message(type, data, delta)


def read_track(infile, debug, error_mode):
def read_track(infile, debug=False):
track = MidiTrack()

name, size = read_chunk_header(infile)
Expand All @@ -216,51 +165,47 @@ def read_track(infile, debug, error_mode):
start = infile.tell()
last_status = None

try:
while True:
# End of track reached.
if infile.tell() - start == size:
break
while True:
# End of track reached.
if infile.tell() - start == size:
break

if debug:
_dbg('Message:')
if debug:
_dbg('Message:')

delta = read_variable_int(infile)
delta = read_variable_int(infile)

if debug:
_dbg('-> delta={}'.format(delta))
if debug:
_dbg('-> delta={}'.format(delta))

# Todo: not all messages have running status
status_byte = read_byte(infile)
# Todo: not all messages have running status
status_byte = read_byte(infile)

if status_byte < 0x80:
if last_status is None:
raise IOError('running status without last_status')
peek_data = [status_byte]
status_byte = last_status
else:
if status_byte != 0xff:
# Meta messages don't set running status.
last_status = status_byte
peek_data = []

if status_byte == 0xff:
msg = read_meta_message(infile, delta, error_mode)
elif status_byte in [0xf0, 0xf7]:
# Todo: I'm not quite clear on the difference between
# f0 and f7 events.
msg = read_sysex(infile, delta, error_mode)
else:
msg = read_message(infile, status_byte, peek_data, delta,
error_mode)
if status_byte < 0x80:
if last_status is None:
raise IOError('running status without last_status')
peek_data = [status_byte]
status_byte = last_status
else:
if status_byte != 0xff:
# Meta messages don't set running status.
last_status = status_byte
peek_data = []

if status_byte == 0xff:
msg = read_meta_message(infile, delta)
elif status_byte in [0xf0, 0xf7]:
# Todo: I'm not quite clear on the difference between
# f0 and f7 events.
msg = read_sysex(infile, delta)
else:
msg = read_message(infile, status_byte, peek_data, delta)

track.append(msg)
track.append(msg)

if debug:
_dbg('-> {!r}'.format(msg))
_dbg()
except EOFError:
_handle_error('unexpected end of file while reading track', error_mode)
if debug:
_dbg('-> {!r}'.format(msg))
_dbg()

return track

Expand Down Expand Up @@ -320,33 +265,26 @@ def get_seconds_per_tick(tempo, ticks_per_beat):
class MidiFile(object):
def __init__(self, filename=None, file=None,
type=1, ticks_per_beat=DEFAULT_TICKS_PER_BEAT,
charset='latin1', debug=False, errors='raise'):
charset='latin1',
debug=False):

self.filename = filename
self.type = type
self.ticks_per_beat = ticks_per_beat
self.charset = charset
self.debug = debug

if errors not in ['raise', 'warn', 'ignore']:
raise ValueError('unknown error mode {!r}'.format(errors))

if debug:
error_mode = 'debug'
else:
error_mode = errors

self.tracks = []

if type not in range(3):
raise ValueError(
'invalid format {} (must be 0, 1 or 2)'.format(format))

if file is not None:
self._load(file, error_mode)
self._load(file)
elif self.filename is not None:
with io.open(filename, 'rb') as file:
self._load(file, error_mode)
self._load(file)

def add_track(self, name=None):
"""Add a new track to the file.
Expand All @@ -360,7 +298,7 @@ def add_track(self, name=None):
self.tracks.append(track)
return track

def _load(self, infile, error_mode):
def _load(self, infile):
if self.debug:
infile = DebugFileWrapper(infile)

Expand All @@ -381,10 +319,9 @@ def _load(self, infile, error_mode):
if self.debug:
_dbg('Track {}:'.format(i))

self.tracks.append(read_track(infile,
debug=self.debug,
error_mode=error_mode))

self.tracks.append(read_track(infile, debug=self.debug))
# Todo: used to ignore EOFError. I hope things still work.

@property
def length(self):
"""Playback time in seconds.
Expand Down
29 changes: 5 additions & 24 deletions mido/midifiles/test_midifiles.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import io
from pytest import raises
from ..messages import Message
from .midifiles import MidiFile, MidiFileError
from .midifiles import MidiFile

HEADER_ONE_TRACK = """
4d 54 68 64 # MThd
Expand All @@ -19,8 +19,8 @@ def parse_hexdump(hexdump):
return data


def read_file(hexdump, errors='raise'):
return MidiFile(file=io.BytesIO(parse_hexdump(hexdump)), errors=errors)
def read_file(hexdump):
return MidiFile(file=io.BytesIO(parse_hexdump(hexdump)))


def test_no_tracks():
Expand Down Expand Up @@ -57,7 +57,7 @@ def test_empty_file():


def test_eof_in_track():
with raises(MidiFileError):
with raises(EOFError):
read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 01 # Chunk size
Expand All @@ -67,28 +67,9 @@ def test_eof_in_track():

def test_invalid_data_byte():
# Todo: should this raise IOError?
with raises(MidiFileError):
read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 04 # Chunk size
00 90 ff 40 # note_on note=255 velocity=64
""")


def test_ignore_track_eof():
read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 01 # Chunk size
# Oops, no data here.
""", errors='ignore')


def test_ignore_invalid_data_byte():
# Todo: should this raise IOError?
with raises(MidiFileError):
with raises(IOError):
read_file(HEADER_ONE_TRACK + """
4d 54 72 6b # MTrk
00 00 00 04 # Chunk size
00 90 ff 40 # note_on note=255 velocity=64
00 f0 01 ff # note_on note=255 velocity=64
""")

0 comments on commit 0994d0e

Please sign in to comment.