Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions music21/midi/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
from typing import overload
import typing as t

if t.TYPE_CHECKING:
import pathlib

from music21.common.enums import ContainsEnum
from music21 import defaults
from music21 import environment
Expand Down Expand Up @@ -491,7 +494,7 @@ def __init__(self,
track: MidiTrack|None = None,
type: MidiEventTypes = MetaEvents.UNKNOWN,
time: int = 0,
channel: int = 1):
channel: int = 1) -> None:
self.track: MidiTrack|None = track # a MidiTrack object
self.type: MidiEventTypes = type
self.time: int = time
Expand Down Expand Up @@ -585,7 +588,7 @@ def pitch(self) -> int|None:
return None

@pitch.setter
def pitch(self, value: int|None):
def pitch(self, value: int|None) -> None:
self.parameter1 = value

@property
Expand All @@ -595,7 +598,7 @@ def velocity(self) -> int|None:
return self.parameter2

@velocity.setter
def velocity(self, value: int|None):
def velocity(self, value: int|None) -> None:
self.parameter2 = value

# store generic data in parameter 1
Expand All @@ -614,7 +617,7 @@ def data(self) -> int|bytes|None:
return self.parameter1

@data.setter
def data(self, value: int|str|bytes|bool|None):
def data(self, value: int|str|bytes|bool|None) -> None:
if value is not None and not isinstance(value, bytes):
if isinstance(value, str):
value = value.encode('utf-8')
Expand All @@ -635,7 +638,7 @@ def isChannelEvent(self) -> bool:
'''
return self.type in ChannelVoiceMessages or self.type in ChannelModeMessages

def setPitchBend(self, cents: int|float, bendRange=2) -> None:
def setPitchBend(self, cents: int|float, bendRange: int = 2) -> None:
'''
Treat this event as a pitch bend value, and set the .parameter1 and
.parameter2 fields appropriately given a specified bend value in cents.
Expand Down Expand Up @@ -1244,7 +1247,7 @@ def __init__(
track: MidiTrack|None = None,
time: int = 0,
channel: int = 1,
):
) -> None:
super().__init__(track, time=time, channel=channel)
self.type = 'DeltaTime'

Expand Down Expand Up @@ -1388,7 +1391,7 @@ class MidiTrack(prebase.ProtoM21Object):
'''
headerId = b'MTrk'

def __init__(self, index: int = 0):
def __init__(self, index: int = 0) -> None:
self.index = index
self.events: list[MidiEvent] = [] # or DeltaTime subclass
self.data = b''
Expand Down Expand Up @@ -1550,7 +1553,7 @@ def getBytes(self) -> bytes:
bytes_out = b''.join(midiBytes)
return self.headerId + putNumber(len(bytes_out), 4) + bytes_out

def _reprInternal(self):
def _reprInternal(self) -> str:
r = f'{self.index} -- {len(self.events)} events'
return r

Expand Down Expand Up @@ -1713,7 +1716,7 @@ def __init__(self) -> None:
self.ticksPerQuarterNote: int = defaults.ticksPerQuarter
self.ticksPerSecond: int|None = None

def open(self, filename, attrib='rb') -> None:
def open(self, filename: str|pathlib.Path, attrib: str = 'rb') -> None:
'''
Open a MIDI file path for reading or writing.

Expand Down
18 changes: 11 additions & 7 deletions music21/midi/percussion.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# ------------------------------------------------------------------------------
from __future__ import annotations

import typing as t
import unittest

from music21 import pitch
Expand Down Expand Up @@ -94,7 +95,7 @@ class PercussionMapper:
# formerly at:
# https://www.midi.org/specifications/item/gm-level-1-sound-set

def midiPitchToInstrument(self, midiPitch):
def midiPitchToInstrument(self, midiPitch: int|pitch.Pitch) -> instrument.Instrument:
'''
Takes a pitch.Pitch object or int ranging from 0-127 and returns
the corresponding instrument in the GM Percussion Map.
Expand Down Expand Up @@ -157,13 +158,15 @@ def midiPitchToInstrument(self, midiPitch):
midiInstrumentObject = midiInstrument()
if (midiInstrumentObject.inGMPercMap is True
and hasattr(midiInstrumentObject, '_percMapPitchToModifier')):
if midiNumber in midiInstrumentObject._percMapPitchToModifier:
modifier = midiInstrumentObject._percMapPitchToModifier[midiNumber]
midiInstrumentObject.modifier = modifier
midiUnpitchedPercussion = t.cast(
instrument.UnpitchedPercussion, midiInstrumentObject)
if midiNumber in midiUnpitchedPercussion._percMapPitchToModifier:
modifier = midiUnpitchedPercussion._percMapPitchToModifier[midiNumber]
midiUnpitchedPercussion.modifier = modifier

return midiInstrumentObject

def midiInstrumentToPitch(self, midiInstrument):
def midiInstrumentToPitch(self, midiInstrument: instrument.Instrument) -> pitch.Pitch:
'''
Takes an instrument.Instrument object and returns a pitch object
with the corresponding 1-indexed MIDI note, according to the GM Percussion Map.
Expand Down Expand Up @@ -191,7 +194,8 @@ def midiInstrumentToPitch(self, midiInstrument):
'''
if not hasattr(midiInstrument, 'inGMPercMap') or midiInstrument.inGMPercMap is False:
raise MIDIPercussionException(f'{midiInstrument!r} is not in the GM Percussion Map!')
midiPitch = midiInstrument.percMapPitch
percInstrument = t.cast(instrument.Percussion, midiInstrument)
midiPitch = t.cast(int, percInstrument.percMapPitch)
pitchObject = pitch.Pitch()
pitchObject.midi = midiPitch
return pitchObject
Expand All @@ -201,7 +205,7 @@ def midiInstrumentToPitch(self, midiInstrument):

class Test(unittest.TestCase):

def testCopyAndDeepcopy(self):
def testCopyAndDeepcopy(self) -> None:
from music21.test.commonTest import testCopyAll
testCopyAll(self, globals())

Expand Down
38 changes: 23 additions & 15 deletions music21/midi/realtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
'''
from __future__ import annotations

from collections.abc import Callable
from importlib.util import find_spec
from io import BytesIO
import typing as t
import unittest

from music21 import defaults
Expand Down Expand Up @@ -80,7 +82,7 @@ def __init__(
mixerBitSize: int = -16,
mixerChannels: int = 2,
mixerBuffer: int = 1024,
):
) -> None:
try:
# noinspection PyPackageRequirements
import pygame # type: ignore
Expand All @@ -93,14 +95,14 @@ def __init__(
self.streamIn = streamIn

def play(self,
busyFunction=None,
busyArgs=None,
endFunction=None,
endArgs=None,
busyWaitMilliseconds=50,
busyFunction: Callable[[t.Any], t.Any]|None = None,
busyArgs: t.Any = None,
endFunction: Callable[[t.Any], t.Any]|None = None,
endArgs: t.Any = None,
busyWaitMilliseconds: int = 50,
*,
playForMilliseconds=float('inf'),
blocked=True):
playForMilliseconds: float = float('inf'),
blocked: bool = True) -> None:
'''
busyFunction is a function that is called with busyArgs when the music is busy every
busyWaitMilliseconds.
Expand All @@ -123,15 +125,21 @@ def play(self,
playForMilliseconds=playForMilliseconds,
blocked=blocked)

def getStringOrBytesIOFile(self):
def getStringOrBytesIOFile(self) -> BytesIO:
streamMidiFile = midiTranslate.streamToMidiFile(self.streamIn)
streamMidiWritten = streamMidiFile.writestr()
return BytesIO(streamMidiWritten)

def playStringIOFile(self, stringIOFile, busyFunction=None, busyArgs=None,
endFunction=None, endArgs=None, busyWaitMilliseconds=50,
def playStringIOFile(self,
stringIOFile: BytesIO,
busyFunction: Callable[[t.Any], t.Any]|None = None,
busyArgs: t.Any = None,
endFunction: Callable[[t.Any], t.Any]|None = None,
endArgs: t.Any = None,
busyWaitMilliseconds: int = 50,
*,
playForMilliseconds=float('inf'), blocked=True):
playForMilliseconds: float = float('inf'),
blocked: bool = True) -> None:
'''
busyFunction is a function that is called with busyArgs when the music is busy every
busyWaitMilliseconds.
Expand Down Expand Up @@ -167,7 +175,7 @@ def playStringIOFile(self, stringIOFile, busyFunction=None, busyArgs=None,
if endFunction is not None:
endFunction(endArgs)

def stop(self):
def stop(self) -> None:
self.pygame.mixer.music.stop()


Expand All @@ -183,7 +191,7 @@ class TestExternal(unittest.TestCase): # pragma: no cover
pygame_installed = False

@unittest.skipUnless(pygame_installed, 'pygame is not installed')
def testBachDetune(self):
def testBachDetune(self) -> None:
from music21 import corpus
import random
b = corpus.parse('bwv66.6')
Expand Down Expand Up @@ -232,7 +240,7 @@ class Mock:
sp = StreamPlayer(b)
sp.play(busyFunction=busyCounter, busyArgs=[timeCounter], busyWaitMilliseconds=500)

def x_testPlayOneMeasureAtATime(self):
def x_testPlayOneMeasureAtATime(self) -> None:
from music21 import corpus
defaults.ticksAtStart = 0
b = corpus.parse('bwv66.6')
Expand Down
Loading
Loading