
    3j                       U d Z ddlmZ g dZddlmZ ddlZddlmZ ddlZ	ddl
mZ ddlmZ dd	lmZ dd
lmZ ddlmZ  ej"                  d      Z G d dej&                        Zed*d       Zed+d       Zd,dZd-dZd.dZd/dZd0dZ G d de      Z G d de      Z G d de      Z G d de      ZdZeez  ez  ez  e	j>                  d   z  Z  G d  d!ejB                        Z" G d" de"      Z# G d# d$ejB                        Z$ G d% d&ejB                        Z%e"e#e$e%gZ&d'e'd(<   e(d)k(  rddlZ ejR                          yy)1ax  
Objects and tools for processing MIDI data.  Converts from MIDI files to
:class:`~music21.midi.MidiEvent`, :class:`~music21.midi.MidiTrack`, and
:class:`~music21.midi.MidiFile` objects, and vice versa.

This module originally used routines from Will Ware's public domain midi.py
library from 2001 which was once posted at
groups.google.com/g/alt.sources/msg/0c5fc523e050c35e
    )annotations)ChannelModeMessagesChannelVoiceMessages	DeltaTimeMETAEVENT_MARKER
MetaEvents	MidiEventMidiExceptionMidiFile	MidiTrackSysExEvents	getNumbergetVariableLengthNumber	putNumberputNumbersAsListputVariableLengthNumber)IterableN)overload)ContainsEnum)defaults)environment)exceptions21)prebasez	midi.basec                      e Zd Zy)r
   N)__name__
__module____qualname__     >/DATA/.local/lib/python3.12/site-packages/music21/midi/base.pyr
   r
   :   s    r   r
   c                     y Nr   	midiByteslengths     r    r   r   >       r   c                     y r"   r   r#   s     r    r   r   B   r&   r   c                    d}t        | t              r$t        |      D ]  }| |   }|dz  |z   } || |d fS | }||d|z  z	  d|z  z  z
  }||z
  }||fS )ao  
    Extract the first `length` bytes from a bytes object (or int)
    and return it and the remaining bytes object as a 2-tuple

    The `length` is the number of chars to read.
    This will sum a length greater than 1 if desired.

    Note that MIDI uses big-endian for everything.
    This is the inverse of Python's chr() function.

    Read first two bytes from the bytes encoding of "tests" and return the remainder

    >>> midi.getNumber(b'tests', 2)
    (29797, b'sts')

    That number comes from:

    >>> ord('t') * 256 + ord('e')
    29797

    Demonstration of reading the whole length of a byte in:

    >>> midi.getNumber(b'test', 4)
    (1952805748, b'')

    Reading in zero bytes leaves the midiStr unchanged:

    >>> midi.getNumber(b'test', 0)
    (0, b'test')

    The method can also take in an integer and return an integer and the remainder part.
    This usage might be deprecated in the future and has already been replaced within
    music21 internal code.

    >>> midi.getNumber(516, 1)   # = 2*256 + 4
    (4, 512)

    * Changed in v10: remove Python 2 legacy string as input -- use bytes instead.
    r      N)
isinstancebytesrange)r$   r%   	summationimidiBytemidNumbigBytess          r    r   r   F   s    P I)U#vA |H"a83I  )FG,,,v!f*51v:FG	I%(""r   c                x    d}d}|dk  r'| |   }|dz  |dz  z   }|dz  }|dz  s|| |d fS |dk  r't        d      )	u  
    Given a string or bytes of data, strip off the first character, or all high-byte characters
    terminating with one whose ord() function is < 0x80.  Thus, a variable number of bytes
    might be read.

    After finding the appropriate termination,
    return the remaining string.

    This is necessary as DeltaTime times are given with variable size,
    and thus may be of different numbers if characters are used.

    >>> midi.getVariableLengthNumber(b'A-u')
    (65, b'-u')
    >>> midi.getVariableLengthNumber(b'-u')
    (45, b'u')
    >>> midi.getVariableLengthNumber(b'u')
    (117, b'')

    >>> midi.getVariableLengthNumber(b'test')
    (116, b'est')
    >>> midi.getVariableLengthNumber(b'E@-E')
    (69, b'@-E')
    >>> midi.getVariableLengthNumber(b'@-E')
    (64, b'-E')
    >>> midi.getVariableLengthNumber(b'-E')
    (45, b'E')
    >>> midi.getVariableLengthNumber(b'E')
    (69, b'')

    Test that variable length characters work:

    >>> midi.getVariableLengthNumber(b'\xff\x7f')
    (16383, b'')
    >>> midi.getVariableLengthNumber('中xy'.encode('utf-8'))
    (210638584, b'y')

    If no low-byte character is encoded, raises an IndexError

    >>> midi.getVariableLengthNumber('中国'.encode('utf-8'))
    Traceback (most recent call last):
    IndexError: index out of range

    Up to v9.7, this method could also take a string as input and return a string.  This usage is
    now removed.
    r   i              Nz#did not find the end of the number!)r
   )r$   r-   r.   xs       r    r   r   {   sk    b I	A
c'aL !^D1		QDim++ c' =
>>r   c                    t               }t        |      D ]&  }d|dz
  |z
  z  }| |z	  dz  }|j                  |       ( t        |      S )a  
    Put a single number as a hex number at the end of a bytes object `length` bytes long.

    >>> midi.putNumber(3, 4)
    b'\x00\x00\x00\x03'
    >>> midi.putNumber(0, 1)
    b'\x00'

    >>> midi.putNumber(258, 2)
    b'\x01\x02'

    If the number is larger than the length currently only the least significant
    bytes are returned. This behavior may change in the near future to raise an
    exception instead.

    >>> midi.putNumber(258, 1)
    b'\x02'
    r)   r5      )	bytearrayr,   appendr+   )numr%   lstr.   nthisNums         r    r   r      sQ    & +C6]!a !8t#

7 
 :r   c                    | dk  rt        d|        t               }	 | dz  | dz	  } }|j                  |dz          | dk(  rn%|j                          |d   dz  |d<   t	        |      S )a  
    Turn an integer number into the smallest bytes object that can hold it for MIDI

    Numbers < 128 are encoded as single bytes and are the same as bytes([x])

    >>> midi.putVariableLengthNumber(4)
    b'\x04'
    >>> midi.putVariableLengthNumber(127)
    b'\x7f'
    >>> bytes([127])
    b'\x7f'
    >>> midi.putVariableLengthNumber(0)
    b'\x00'

    Numbers > 7bit but < 16384 need two characters,
    with the first character set as 0x80 + n // 128
    and the second character set as n % 128:

    >>> midi.putVariableLengthNumber(128)
    b'\x81\x00'
    >>> midi.putVariableLengthNumber(129)
    b'\x81\x01'
    >>> midi.putVariableLengthNumber(255)
    b'\x81\x7f'

    This differs from the 8-bit representation of
    `bytes([x])`:

    >>> bytes([128])
    b'\x80'
    >>> bytes([255])
    b'\xff'

    This method can also deal with numbers > 255, which `bytes` cannot.

    >>> midi.putVariableLengthNumber(256)
    b'\x82\x00'
    >>> bytes([256])
    Traceback (most recent call last):
    ValueError: bytes must be in range(0, 256)

    It also differs from the normal way of representing 256 in Python bytes:

    >>> bytes([1, 0])
    b'\x01\x00'

    Here are MIDI representation of other numbers that are too large to fit in 8 bits
    but stored in two bytes in MIDI.

    >>> midi.putVariableLengthNumber(1024)
    b'\x88\x00'

    Notice that the least significant byte is second.

    >>> midi.putVariableLengthNumber(8192)
    b'\xc0\x00'
    >>> midi.putVariableLengthNumber(8193)
    b'\xc0\x01'

    This is the maximum normal MIDI number that can be stored in 2 bytes.

    >>> midi.putVariableLengthNumber(16383)
    b'\xff\x7f'

    Numbers >= 16384 are not used in 2-byte classic MIDI,
    but this routine continues the basic principle from above:

    >>> midi.putVariableLengthNumber(16384)
    b'\x81\x80\x00'
    >>> midi.putVariableLengthNumber(16384 + 128)
    b'\x81\x81\x00'
    >>> midi.putVariableLengthNumber(16384 * 2)
    b'\x82\x80\x00'
    >>> midi.putVariableLengthNumber(16384 ** 2)
    b'\x81\x80\x80\x80\x00'

    Negative numbers raise MidiException

    >>> midi.putVariableLengthNumber(-1)
    Traceback (most recent call last):
    music21.midi.base.MidiException: cannot putVariableLengthNumber() when number is negative: -1
    r   z:cannot putVariableLengthNumber() when number is negative: r4   r3   r6   )r
   r:   r;   reverser+   )r7   r=   ys      r    r   r      s    j 	1uXYZX[\]]
+C
4xa1

1t86	 
 KKM"gnCG:r   c                    t               }| D ]0  }|dk  r|dz  }|dk\  rt        d|       |j                  |       2 t        |      S )aS  
    Translate a list of numbers (0-255) into bytes.
    Used for encoding data messages where each byte encodes a different discrete value.

    >>> midi.putNumbersAsList([0, 0, 0, 3])
    b'\x00\x00\x00\x03'

    For positive numbers this method behaves the same as `bytes(numList)` and that
    method should be used if you are sure all numbers are positive.

    >>> bytes([0, 0, 0, 3])
    b'\x00\x00\x00\x03'

    If a number is < 0 then it wraps around from the top.  This is used in places
    like MIDI key signatures where flats are represented by `256 - flats`.

    >>> midi.putNumbersAsList([0, 0, 0, -3])
    b'\x00\x00\x00\xfd'
    >>> midi.putNumbersAsList([0, 0, 0, -1])
    b'\x00\x00\x00\xff'

    The behavior with negative numbers is different from `bytes(numList)`

    List can be of any length

    >>> midi.putNumbersAsList([1, 16, 255])
    b'\x01\x10\xff'

    Any number > 255 (or less than -255) raises an exception:

    >>> midi.putNumbersAsList([256])
    Traceback (most recent call last):
    music21.midi.base.MidiException: Cannot place a number > 255 in a list: 256
    r      z'Cannot place a number > 255 in a list: )r:   r
   r;   r+   )numListpostr>   s      r    r   r   ;  sW    F ;Dq5CA8"I! MNNA  ;r   c                  ,    e Zd ZdZdZdZdZdZdZdZ	dZ
y	)
r   zo
    ChannelVoiceMessages are the main MIDI messages for channel 1-16, such as
    NOTE_ON, NOTE_OFF, etc.
    r6                     N)r   r   r   __doc__NOTE_OFFNOTE_ONPOLYPHONIC_KEY_PRESSURECONTROLLER_CHANGEPROGRAM_CHANGECHANNEL_KEY_PRESSURE
PITCH_BENDr   r   r    r   r   i  s/     HG"NJr   r   c                  ,    e Zd ZdZdZdZdZdZdZdZ	dZ
y	)
r   x   y   z   {   |   }   ~   r4   N)r   r   r   ALL_SOUND_OFFRESET_ALL_CONTROLLERSLOCAL_CONTROLALL_NOTES_OFFOMNI_MODE_OFFOMNI_MODE_ONMONO_MODE_ONPOLY_MODE_ONr   r   r    r   r   w  s+    M MMMLLLr   r   c                  X    e Zd ZdZdZdZdZdZdZdZ	dZ
d	Zd
ZdZdZdZdZdZdZdZdZdZy)r   r   r5                  r3   r)   	       !   /   Q   T   X   Y   r4   r9   N)r   r   r   SEQUENCE_NUMBER
TEXT_EVENTCOPYRIGHT_NOTICESEQUENCE_TRACK_NAMEINSTRUMENT_NAMELYRICMARKER	CUE_POINTPROGRAM_NAMESOUND_SET_UNSUPPORTEDMIDI_CHANNEL_PREFIX	MIDI_PORTEND_OF_TRACK	SET_TEMPOSMPTE_OFFSETTIME_SIGNATUREKEY_SIGNATURESEQUENCER_SPECIFIC_META_EVENTUNKNOWNr   r   r    r   r     sh    OJOEFIL !ILILNM$(!Gr   r   c                      e Zd ZdZdZy)r         N)r   r   r   F0_SYSEX_EVENTF7_SYSEX_EVENTr   r   r    r   r     s    NNr   r   r9   r   c                  P   e Zd ZdZdej
                  ddf	 	 	 	 	 	 	 ddZedd       ZddZ	edd       Z
e
j                  dd	       Z
edd
       Zej                  dd       Zedd       Zej                  dd       ZddZdd dZd!dZd!dZd"dZddZddZddZd#dZy)$r	   a	  
    A model of a MIDI event, including note-on, note-off, program change,
    controller change, any many others.

    MidiEvent objects are paired (preceded) by :class:`~music21.midi.DeltaTime`
    objects in the list of events in a MidiTrack object.

    The `track` argument must be a :class:`~music21.midi.MidiTrack` object.

    The `type` attribute is an enumeration of a Midi event from the ChannelVoiceMessages,
    ChannelModeMessages, SysExEvents, or MetaEvents enums; if unspecified,
    defaults to MetaEvents.UNKNOWN.

    The `channel` attribute is an integer channel id, from 1 to 16.

    The `time` attribute is an integer duration of the event in ticks. This value
    can be zero. This value is not essential, as ultimate time positioning is
    determined by :class:`~music21.midi.DeltaTime` objects.

    The `pitch` attribute is only defined for note-on and note-off messages.
    The attribute stores an integer representation (0-127, with 60 = middle C).

    The `velocity` attribute is only defined for note-on and note-off messages.
    The attribute stores an integer representation (0-127).  A note-on message with
    velocity 0 is generally assumed to be the same as a note-off message.

    The `data` attribute is used for storing other messages,
    such as SEQUENCE_TRACK_NAME string values.

    .. warning::

        The attributes `.midiProgram` and `.midiChannel` on :class:`~music21.instrument.Instrument`
        objects are 0-indexed, just as they need to be in the written binary .mid.
        However, as a convenience, :attr:`MidiEvent.channel` is 1-indexed. No
        analogous convenience is provided for program change data.

    >>> mt = midi.MidiTrack(1)
    >>> me1 = midi.MidiEvent(mt)
    >>> me1.type = midi.ChannelVoiceMessages.NOTE_ON
    >>> me1.channel = 3
    >>> me1.time = 200
    >>> me1.pitch = 60
    >>> me1.velocity = 120
    >>> me1
    <music21.midi.MidiEvent NOTE_ON, t=200, track=1, channel=3, pitch=60, velocity=120>

    >>> me2 = midi.MidiEvent(mt)
    >>> me2.type = midi.MetaEvents.SEQUENCE_TRACK_NAME
    >>> me2.data = 'guitar'
    >>> me2
    <music21.midi.MidiEvent SEQUENCE_TRACK_NAME, track=1, data=b'guitar'>

    * Changed in v9.7: None is not a valid type anymore.  Use MetaEvents.UNKNOWN instead.
        Channel defaults to 1.
    Nr   r5   c                    || _         || _        || _        || _        d | _        d | _        d | _        d | _        d | _        y r"   )	tracktypetimechannel
parameter1
parameter2	centShiftcorrespondingEventlastStatusByte)selfr   r   r   r   s        r    __init__zMidiEvent.__init__  sK    
 &+
$(		#*.*. $(
 37 )-r   c                |    | j                   t        j                  k(  ry| j                   t        j                  k(  ryy)ah  
        Ensure that for MidiEvents at the same "time", that order is
        NOTE_OFF, PITCH_BEND, all others.

        >>> CVM = midi.ChannelVoiceMessages
        >>> noteOn = midi.MidiEvent(type=CVM.NOTE_ON)
        >>> noteOff = midi.MidiEvent(type=CVM.NOTE_OFF)
        >>> pitchBend = midi.MidiEvent(type=CVM.PITCH_BEND)

        >>> sorted([noteOn, noteOff, pitchBend], key=lambda me: me.sortOrder)
        [<music21.midi.MidiEvent NOTE_OFF, track=None, channel=1>,
         <music21.midi.MidiEvent PITCH_BEND, track=None, channel=1>,
         <music21.midi.MidiEvent NOTE_ON, track=None, channel=1>]
        iir   )r   r   rP   rV   r   s    r    	sortOrderzMidiEvent.sortOrder  s3    " 99,555YY.999r   c                   | j                   d }n=t        | j                   t              r| j                   }n| j                   j                  }t        | j                  t
              r| j                  j                  }n$| j                  d}nt        | j                        }| d}| j                  dk7  r|d| j                  dz  }|d| z  }| j                         r|d| j                  z  }| j                  t        j                  t        j                  fv rddg}n| j                  d	g}nd
dg}|D ]0  }t        | |      |dz   |z   dz   t        t        | |            z   }2 |S )NNone, r   zt=ztrack=z
, channel=pitchvelocitydatar   r   =)r   r*   intindexr   r   namereprr   isChannelEventr   r   rQ   rP   r   getattr)r   
trackIndex	printTyperattrListattribs         r    _reprInternalzMidiEvent._reprInternal  sW   ::J

C(J))J dii.		IYYITYYIk99>2dii]"%%A	vj\"" :dll-..A99-557K7T7TUU,H&"8(,7FtV$0Hv%+d743H.II  r   c                    | j                   t        j                  t        j                  fv r!| j                  t        | j                        S y r"   )r   r   rQ   rP   r   r   r   s    r    r   zMidiEvent.pitchB  s?     II.668L8U8UVVOO/t''r   c                    || _         y r"   r   r   values     r    r   zMidiEvent.pitchK  	    r   c                P    t        | j                  t              sy | j                  S r"   )r*   r   r   r   s    r    r   zMidiEvent.velocityO  s    $//3/r   c                    || _         y r"   )r   r   s     r    r   zMidiEvent.velocityU  r   r   c                    | j                   S )z
        Read or set the data (`.parameter1`) for the object

        Does some automatic conversions:

        >>> me = midi.MidiEvent(type=midi.ChannelModeMessages.LOCAL_CONTROL)
        >>> me.data = True
        >>> me.data
        b'\x01'
        r   r   s    r    r   zMidiEvent.dataZ  s     r   c                    |^t        |t              sNt        |t              r|j                  d      }|| _        y t        |t              rt        t        |      g      }|| _        y )Nzutf-8)r*   r+   strencodeboolr   r   r   s     r    r   zMidiEvent.datah  sU    Zu%=%%W-   E4(s5zl+r   c                N    | j                   t        v xs | j                   t        v S )a  
        returns bool if the channel matters for this event

        >>> me = midi.MidiEvent(type=midi.ChannelVoiceMessages.NOTE_ON, channel=4)
        >>> me.isChannelEvent()
        True
        >>> dt = midi.DeltaTime()
        >>> dt.isChannelEvent()
        False
        )r   r   r   r   s    r    r   zMidiEvent.isChannelEventq  s#     yy00TDIIAT4TTr   c                V   |dz  }||kD  r|}n|d|z  k  rd|z  }d}d|z
  }|}||z  }|dkD  rt        t        ||z              }n|dk  rt        t        ||z              }nd}||z   }	t        |	      }
|
d   }|dz  }t        |
      dkD  r|
d   }|dz  }n|}d}|| _        || _        y)	a  
        Treat this event as a pitch bend value, and set the .parameter1 and
         .parameter2 fields appropriately given a specified bend value in cents.

        Also called Pitch Wheel

        The `bendRange` parameter gives the number of half steps in the bend range.

        >>> mt = midi.MidiTrack(1)
        >>> me1 = midi.MidiEvent(mt, type=midi.ChannelVoiceMessages.PITCH_BEND)
        >>> me1.setPitchBend(50)
        >>> me1.parameter1, me1.parameter2
        (0, 80)
        >>> me1.setPitchBend(100)
        >>> me1.parameter1, me1.parameter2
        (0, 96)

        Neutral is 0, 64

        >>> me1.setPitchBend(0)
        >>> me1.parameter1, me1.parameter2
        (0, 64)

        Parameter 2 is the most significant digit, not
        parameter 1.

        >>> me1.setPitchBend(101)
        >>> me1.parameter1, me1.parameter2
        (40, 96)

        Exceeding maximum pitch bend sets the max (127, 127)

        >>> me1.setPitchBend(200)
        >>> me1.parameter1, me1.parameter2
        (127, 127)
        >>> me1.setPitchBend(300)
        >>> me1.parameter1, me1.parameter2
        (127, 127)

        >>> me1.setPitchBend(-50)
        >>> me1.parameter1, me1.parameter2
        (0, 48)
        >>> me1.setPitchBend(-100)
        >>> me1.parameter1, me1.parameter2
        (0, 32)
        >>> me1.setPitchBend(-196)
        >>> me1.parameter1, me1.parameter2
        (36, 1)
        >>> me1.setPitchBend(-200)
        >>> me1.parameter1, me1.parameter2
        (0, 0)

        Again, excess trimmed

        >>> me1.setPitchBend(-300)
        >>> me1.parameter1, me1.parameter2
        (0, 0)

        But a larger `bendRange` can be set in semitones for non-GM devices:

        >>> me1.setPitchBend(-300, bendRange=4)
        >>> me1.parameter1, me1.parameter2
        (0, 16)
        >>> me1.setPitchBend(-399, bendRange=4)
        >>> me1.parameter1, me1.parameter2
        (20, 0)

        OMIT_FROM_DOCS

        Pitch bends very close to 0 formerly had problems

        >>> me1.setPitchBend(-196)
        >>> me1.parameter1, me1.parameter2
        (36, 1)
        >>> me1.setPitchBend(-197)
        >>> me1.parameter1, me1.parameter2
        (123, 0)
        d   rA   i    i?  r   r4   r5   N)r   roundr   lenr   r   )r   cents	bendRange	centRangecentertopSpan
bottomSpanshiftScalarshifttarget	charValued1d2s                r    setPitchBendzMidiEvent.setPitchBend~  s    b O	9ER)^#NEv%
i'19kG345EQYkJ678EE% ,F3	q\$Yy>A1BdBBB r   c                >   t        |      dk  rt        d|d      |d   }|d   }d}t        |      dkD  r|d   }|dz  }|dz  }|dz   | _        t        |      | _        | j                  t        j
                  t        j                  fv r,|dkD  rt        d	| j                  d
|       || _        |dd S | j                  t        j                  k(  rd}t        j                  |      rft        |      | _        | j                  t        j                  k(  rd}|d   dk(  | _        n)| j                  t        j                  k(  rd}|d   | _        |s|| _        || _        |dd S | j                  t        j                   k(  r|| _        || _        |dd S | j                  t        j"                  t        j$                  fv r|| _        || _        |dd S | j                  t        j*                  k(  r|| _        || _        |dd S t-        d| j                         )a\	  
        Take a set of bytes that represent a ChannelVoiceMessage and set the
        appropriate event type and data, returning the remaining bytes.

        Channel voice messages start with a one-byte message from 0x80 to 0xEF,
        where the first nibble (8-E) is the message type and the second nibble (0-F)
        is the channel represented in hex.

        First let's create a MidiEvent that does not know its type.

        >>> mt = midi.MidiTrack(1)
        >>> me1 = midi.MidiEvent(mt)
        >>> me1
        <music21.midi.MidiEvent UNKNOWN, track=1>

        Now create two messages -- a note on (at pitch = 60 or C4) at velocity 120

        >>> midBytes = bytes([0x92, 60, 120])
        >>> midBytes
        b'\x92<x'

        Add a second hypothetical message (would normally be a delta time)

        >>> midBytes += b'hello'
        >>> midBytes
        b'\x92<xhello'

        Now see how parsing this ChannelVoiceMessage changes the MidiEvent

        >>> remainder = me1.parseChannelVoiceMessage(midBytes)
        >>> me1
        <music21.midi.MidiEvent NOTE_ON, track=1, channel=3, pitch=60, velocity=120>

        The remainder would probably contain a delta time and following
        events, but here we'll just show that it passes whatever is left through.

        >>> remainder
        b'hello'

        We will ignore remainders for the rest of this demo.  Note that all
        attributes are set properly:

        >>> me1.type
        <ChannelVoiceMessages.NOTE_ON: 0x90>
        >>> me1.pitch  # 60 = middle C
        60
        >>> me1.velocity
        120

        The channel is 3 because 0x90 is NOTE_ON for channel 1, 0x91 is 2, and 0x92 is 3, etc.

        >>> me1.channel
        3

        Now let's make a program change on channel 16:

        >>> me2 = midi.MidiEvent(mt)
        >>> rem = me2.parseChannelVoiceMessage(bytes([0xCF, 71]))
        >>> me2
        <music21.midi.MidiEvent PROGRAM_CHANGE, track=1, channel=16, data=71>
        >>> me2.data  # 71 = clarinet (0-127 indexed)
        71

        Program changes and channel pressures only go to 127.  More than that signals an error:

        >>> me2.parseChannelVoiceMessage(bytes([0xCF, 200]))
        Traceback (most recent call last):
        music21.midi.base.MidiException: Cannot have a
            <ChannelVoiceMessages.PROGRAM_CHANGE: 0xC0> followed by a byte > 127: 200
        rh   z
length of z must be at least 2r   r5   r      r4   zCannot have a z followed by a byte > 127: NFTri   z"expected ChannelVoiceMessage, got )r   
ValueErrorr   r   r   rT   rU   r
   r   rS   r   hasValuera   re   r   r   rV   rQ   rP   r   r   rR   	TypeError)r   r$   byte0byte1byte2	msgNybblechannelNybblespecificDataSets           r    parseChannelVoiceMessagez"MidiEvent.parseChannelVoiceMessage  s%   T y>Az)6IJKK!!y>AaLE	"T\$q((3	 99-<<-BBD Ds{#$TYYM1LUGTV VDIQR= YY.@@@#O"++E2/6	99 3 A AA&*O!*1!5DIYY"5"B"BB&*O )!DI""'"'QR= YY.999#DO#DOQR= YY/779M9V9VWWDJ!DMQR= YY.FFF#DO#DOQR= <TYYKHIIr   c           	     (   t        |      dk  r!t        j                  dt        |      g       y|d   }|dk  r0| j                  t        | j                  g      }nd}||z   }|d   }n|dk7  r|| _        |d	z  }|d
   }t        j                  |      r| j                  |      S t        j                  |      r0t        |      | _
        t        |d
d       \  }}|d| | _        ||d S |t        k(  r[t        j                  |      rt        |      | _
        nt        j                  | _
        t        |dd       \  }}|d| | _        ||d S t        j                  dt!        |      dt!        |d
         g       t#        dt!        |             )a  
        Parse the bytes given and take the beginning
        section and convert it into data for this event and return the
        now truncated bytes.

        >>> channel = 0x2
        >>> noteOnMessage = midi.ChannelVoiceMessages.NOTE_ON | channel
        >>> hex(noteOnMessage)
        '0x92'

        This is how the system reads note-on messages (0x90-0x9F) and channels

        >>> hex(0x91 & 0xF0)  # testing message type extraction
        '0x90'
        >>> hex(0x92 & 0xF0)  # testing message type extraction
        '0x90'
        >>> (0x90 & 0x0F) + 1  # getting the channel
        1
        >>> (0x9F & 0x0F) + 1  # getting the channel
        16
        rh   z%MidiEvent.read(): got bad data stringr   r   r6   N   r9   r   r5   zgot unknown midi event typezhex(midiBytes[1])zUnknown midi event type )r   environLocal
printDebugr   r   r+   r   r   r   r   r   r   r   r   r   r   hexr
   )r   r$   r   runningStatusBytemsgTyper   r%   midiBytesAfterLengths           r    readzMidiEvent.readu  s   , y>A ##8$y/JL
 q\ 4< "".$)4+>+>*?$@!$+! *I5IaLEd]"'Dt|q\  ((100;;!!%(#E*DI+B9QR=+Q(F(,Wf5DI'00 &&""5)&u-	 '..	+B9QR=+Q(F(,Wf5DI'00 ##%BCJ%8#il:K%M N":3u:, GHHr   c           	     	   t         j                  rt        | j                  t              sJ | j                  t
        v rt        | j                  dz
  | j                  j                  z   g      }| j                  t
        j                  t
        j                  fvr| j                  | j                  t        d      t        | j                  t              rt        | j                  g      }n| j                  }t        | j                  t              rt        | j                  g      }n| j                  }||z   }||z   S | j                  t
        j                  k(  r5t        | j                  t              rt        | j                  g      }||z   S 	 t        | j                  t              rt        | j                  g      }n2t        | j                  t              r| j                  }nt!        d      ||z   S | j                  t$        v r| j                  t        d| j                  z   dz
  g      }t        | j                  j                  g      }t        | j                  t              rt        | j                  g      }n| j                  }||z   |z   S | j                  t&        v rt        | j                  t              rjt        | j                  j                  g      }|t)        t+        | j                              z   }t        j,                  t        | j                        }||z   S | j                  t.        v rt        | j                  t        t0        f      r~t        t2        g      }|t        | j                  j                  g      z  }|t)        t+        | j                              z  }	 t        j,                  t        | j                        }||z   S t        d| j                        # t         t"        f$ r  t        d|  d| j                  ddz         w xY w# t4        t         f$ rN |t7        j8                  d	t        j,                  t0        | j                              j;                  d
d      z   cY S w xY w)a  
        Return a set of bytes for this MIDI event (used for translating from music21 to MIDI)

        >>> noteOn = midi.MidiEvent(type=midi.ChannelVoiceMessages.NOTE_ON, channel=10)
        >>> noteOn.pitch = 60
        >>> noteOn.velocity = 127
        >>> noteOn.getBytes()
        b'\x99<\x7f'

        Changing the pitch changes the second byte (from less than to greater than):

        >>> noteOn.pitch = 62
        >>> noteOn.getBytes()
        b'\x99>\x7f'
        >>> ord('>')
        62

        Changing the velocity changes the third byte:

        >>> noteOn.velocity = 64
        >>> noteOn.getBytes()
        b'\x99>@'

        The third byte is `\x40` but it is represented by '@'

        >>> ord('@')
        64
        >>> hex(64)
        '0x40'

        And changing the channel changes the first byte:

        >>> noteOn.channel = 11
        >>> noteOn.getBytes()
        b'\x9a>@'
        r5   z>Cannot write MIDI event without parameter1 and parameter2 set.zdata must be bytes or intzGot incorrect data for z in .data: r   z"cannot parse Miscellaneous MessagerK   NFKDasciiignorezunknown midi event type: )tTYPE_CHECKINGr*   r   r   r   r+   r   r   rT   rU   r   r   r
   r   r   r   r   r   r   r   r   castr   r   r   UnicodeDecodeErrorunicodedata	normalizer   )	r   bytes0
param1data
param2datar   channelModemsgValuessys_datas	            r    getByteszMidiEvent.getBytes  s   J ??dii66699,,DLL1,tyy>?@Fyy!5!D!D!5!J!J!L L??*doo.E'X  doos3!&'8!9J!%J doos3!&'8!9J!%J!J. D=  2AAAjQUQZQZ\_F`dii[) D= 
@!$))S1$dii[1#DIIu5#yy'(CDD
 D= YY--$))2G!4q!8 9:Kdiioo./H$))S)dii[)yy)D00YY+%*TYY*Ftyy'(A+C		N;;AvveTYY/Hx<YY*$DIIs|)L'()A		())A(TYY88A,66%38|#  ";DII= IJJO ":. @'1${499-rR>?@ @@: '	2 ,
 ;00FF3		* &(+, ,,s    A#O; :(P- ;/P*-AR
	R
c                ^    | j                   t        j                  k(  r| j                  dk7  ryy)a  
        Return a boolean if this is a note-on message and velocity is not zero.

        >>> mt = midi.MidiTrack(1)
        >>> me1 = midi.MidiEvent(mt)
        >>> me1.type = midi.ChannelVoiceMessages.NOTE_ON
        >>> me1.velocity = 120
        >>> me1.isNoteOn()
        True
        >>> me1.isNoteOff()
        False

        A zero velocity note-on is treated as a note-off:

        >>> me1.velocity = 0
        >>> me1.isNoteOn()
        False
        >>> me1.isNoteOff()
        True

        A midi event can be neither a note on or a note off.

        >>> me1.type = midi.ChannelVoiceMessages.PROGRAM_CHANGE
        >>> me1.isNoteOn()
        False
        >>> me1.isNoteOff()
        False
        r   TF)r   r   rQ   r   r   s    r    isNoteOnzMidiEvent.isNoteOnC  s(    : 99,444!9Kr   c                    | j                   t        j                  k(  ry| j                   t        j                  k(  r| j                  dk(  ryy)a  
        Return a boolean if this should be interpreted as a note-off message,
        either as a real note-off or as a note-on with zero velocity.

        >>> mt = midi.MidiTrack(1)
        >>> me1 = midi.MidiEvent(mt)
        >>> me1.type = midi.ChannelVoiceMessages.NOTE_OFF
        >>> me1.isNoteOn()
        False
        >>> me1.isNoteOff()
        True

        >>> me2 = midi.MidiEvent(mt)
        >>> me2.type = midi.ChannelVoiceMessages.NOTE_ON
        >>> me2.velocity = 0
        >>> me2.isNoteOn()
        False
        >>> me2.isNoteOff()
        True

        See :meth:`~music21.midi.MidiEvent.isNoteOn` for more examples.
        Tr   F)r   r   rP   rQ   r   r   s    r    	isNoteOffzMidiEvent.isNoteOffd  s>    . 99,555YY.6664==A;Mr   c                $    | j                   dk(  ryy)z
        Return a boolean if this is a DeltaTime subclass.

        >>> mt = midi.MidiTrack(1)
        >>> dt = midi.DeltaTime(mt)
        >>> dt.isDeltaTime()
        True
        r   TF)r   r   s    r    isDeltaTimezMidiEvent.isDeltaTime  s     99#r   c                    | j                         rC|j                         r3| j                  |j                  k(  r| j                  |j                  k(  ryy)a	  
        Returns True if `other` is a MIDI event that specifies
        a note-off message for this message.  That is, this event
        is a NOTE_ON message, and the other is a NOTE_OFF message
        for this pitch on this channel.  Otherwise returns False

        >>> mt = midi.MidiTrack(1)
        >>> me1 = midi.MidiEvent(mt)
        >>> me1.type = midi.ChannelVoiceMessages.NOTE_ON
        >>> me1.velocity = 120
        >>> me1.pitch = 60

        >>> me2 = midi.MidiEvent(mt)
        >>> me2.type = midi.ChannelVoiceMessages.NOTE_ON
        >>> me2.velocity = 0
        >>> me2.pitch = 60

        `me2` is a Note off for `me1` because it has velocity 0 and matches
        pitch.

        >>> me1.matchedNoteOff(me2)
        True

        Now the pitch does not match, so it does not work.

        >>> me2.pitch = 61
        >>> me1.matchedNoteOff(me2)
        False

        >>> me2.type = midi.ChannelVoiceMessages.NOTE_OFF
        >>> me1.matchedNoteOff(me2)
        False

        >>> me2.pitch = 60
        >>> me1.matchedNoteOff(me2)
        True

        Channels must match also:

        >>> me2.channel = 12
        >>> me1.matchedNoteOff(me2)
        False

        Note that this method is no longer used in MIDI Parsing
        because it is inefficient.
        TF)r   r   r   r   )r   others     r    matchedNoteOffzMidiEvent.matchedNoteOff  s<    ^ ==?u0zzU[[(T\\U]]-Jr   )r   MidiTrack | Noner   MidiEventTypesr   r   r   r   returnr   r  r   )r  
int | None)r   r  )r  zint | bytes | None)r   zint | str | bytes | bool | Noner  r   )rh   )r   zint | floatr  r   r$   r+   r  r+   r  r+   )r   r	   r  r   )r   r   r   rO   r   r   r   propertyr   r   r   setterr   r   r   r   r   r   r   r   r   r   r   r   r   r    r	   r	     s   6t *.(2(:(: !	-&-%- - 	-6  ."J   \\     
 __      
[[   Uvp}J~VIpsKlB:2r   r	   c                  Z     e Zd ZdZ	 	 	 d	 	 	 	 	 d fdZd	 fdZd
dZddZddZ xZ	S )r   a  
    A :class:`~music21.midi.MidiEvent` subclass that stores the
    time change (in ticks) since the start or since the last MidiEvent.

    Pairs of DeltaTime and MidiEvent objects are the basic presentation of temporal data.

    The `track` argument must be a :class:`~music21.midi.MidiTrack` object.

    Time values are in integers, representing ticks.

    The `channel` attribute, inherited from MidiEvent is not used and set to 1
    unless overridden (it does not do anything if set and is not written out to MIDI).

    >>> mt = midi.MidiTrack(1)
    >>> dt = midi.DeltaTime(mt)
    >>> dt.time = 1
    >>> dt
    <music21.midi.DeltaTime t=1, track=1>

    Changed in v9.7 -- DeltaTime repr no longer lists its channel (to de-emphasize it).
        Channel defaults to 1.
    c                8    t         |   |||       d| _        y )N)r   r   r   )superr   r   )r   r   r   r   	__class__s       r    r   zDeltaTime.__init__  s      	T7;	r   c                p    t         |          }|j                  dd      }| j                  dk(  rd|z   }|S )Nz'DeltaTime',  r   z(empty) )r  r   replacer   )r   repr  s     r    r   zDeltaTime._reprInternal  s:    g#%kk/2.99>s"C
r   c                     y)z9
        Save processing time -- is always False
        Fr   r   s    r    r   zDeltaTime.isChannelEvent  s     r   c                D    t        |      \  | _        }| j                  |fS )a  
        Read a byte-string until hitting a character below 0x80
        and return the converted number and the rest of the bytes.

        The first number is also stored in `self.time`.

        >>> mt = midi.MidiTrack(1)
        >>> dt = midi.DeltaTime(mt)
        >>> dt.time
        0

        >>> dt.readUntilLowByte(b'\x20')
        (32, b'')
        >>> dt.time
        32

        >>> dt.readUntilLowByte(b'\x20hello')
        (32, b'hello')

        here the '\x82' is above 0x80 so the 'h' is read
        as part of the continuation.

        >>> dt.readUntilLowByte(b'\x82hello')
        (360, b'ello')

        * Changed in v9: had an incompatible signature with MidiEvent.
        )r   r   )r   oldBytesnewBytess      r    readUntilLowBytezDeltaTime.readUntilLowByte  s$    8 6h?	8yy(""r   c                0    t        | j                        }|S )a  
        Convert the time integer into a set of bytes.

        >>> mt = midi.MidiTrack(1)
        >>> dt = midi.DeltaTime(mt)
        >>> dt.time = 1
        >>> dt.getBytes()
        b'\x01'

        >>> dt.time = 128
        >>> dt.getBytes()
        b'\x81\x00'

        >>> dt.time = 257
        >>> dt.getBytes()
        b'\x82\x01'

        >>> dt.time = 16385
        >>> dt.getBytes()
        b'\x81\x80\x01'
        )r   r   )r   r$   s     r    r   zDeltaTime.getBytes  s    , ,DII6	r   )Nr   r5   )r   r   r   r   r   r   r  r  )r  r+   r  tuple[int, bytes]r  )
r   r   r   rO   r   r   r   r  r   __classcell__)r  s   @r    r   r     sH    0 !%	     	 #>r   c                      e Zd ZdZdZdddZddZedd       ZddZ	dddZ
ddZd	 Zdd
ZddZddZddZddZy)r   am  
    A MIDI Track. Each track contains an index and a list of events.

    All events are stored in the `events` list, in order.

    An `index` is an integer identifier for this object.  It is often called
    "trackId" though technically the id for a MidiTrack is always b'MTrk'

    >>> mt = midi.MidiTrack(index=3)
    >>> mt.events
    []
    >>> mt.index
    3

    The data consists of all the midi data after b'MTrk'

    >>> mt.data
    b''

    And the .length is the same as the data's length

    >>> mt.length
    0

    After reading a string

    >>> mt.read(b'MTrk\x00\x00\x00\x16\x00\xff\x03\x00\x00'
    ...         + b'\xe0\x00@\x00\x90CZ\x88\x00\x80C\x00\x88\x00\xff/\x00')
    b''

    The returned value is what is left over after the track is read
    (for instance the data for another track)

    >>> mt.length
    22

    * New in v9.7: len(mt) returns the same as mt.length, but more Pythonic.

    >>> len(mt)
    22

    >>> mt.data[:8]
    b'\x00\xff\x03\x00\x00\xe0\x00@'

    Note that the '\x16' got translated to ascii '@'.

    >>> mt.events
    [<music21.midi.DeltaTime (empty) track=3>,
     <music21.midi.MidiEvent SEQUENCE_TRACK_NAME, track=3, data=b''>,
     <music21.midi.DeltaTime (empty) track=3>,
     <music21.midi.MidiEvent PITCH_BEND, track=3, channel=1, parameter1=0, parameter2=64>,
     <music21.midi.DeltaTime (empty) track=3>,
     <music21.midi.MidiEvent NOTE_ON, track=3, channel=1, pitch=67, velocity=90>,
     <music21.midi.DeltaTime t=1024, track=3>,
     <music21.midi.MidiEvent NOTE_OFF, track=3, channel=1, pitch=67, velocity=0>,
     <music21.midi.DeltaTime t=1024, track=3>,
     <music21.midi.MidiEvent END_OF_TRACK, track=3, data=b''>]

    >>> mt
    <music21.midi.MidiTrack 3 -- 10 events>

    There is a class attribute of the headerId which is the same for
    all MidiTrack objects

    >>> midi.MidiTrack.headerId
    b'MTrk'
    s   MTrkc                .    || _         g | _        d| _        y )Nr   )r   eventsr   )r   r   s     r    r   zMidiTrack.__init__o  s    
')	r   c                ,    t        | j                        S )z 
        * New in v9.7.
        r   r   r   s    r    __len__zMidiTrack.__len__t  s     499~r   c                ,    t        | j                        S r"   r  r   s    r    r%   zMidiTrack.lengthz  s    499~r   c                    |dd | j                   k(  st        d      t        |dd d      \  }}|d| }|| _        ||d }| j	                  |       |S )a  
        Read as much of the bytes object (representing midi data) as necessary;
        return the remaining bytes object for reassignment and further processing.

        The string should begin with `MTrk`, specifying a Midi Track

        Stores the read data (after the header and length information) in self.data

        Calls `processDataToEvents` which creates
        :class:`~music21.midi.DeltaTime`
        and :class:`~music21.midi.MidiEvent` objects and stores them in self.events
        Nrj   z.badly formed midi string: missing leading MTrk)headerIdr
   r   r   processDataToEvents)r   r$   r%   	trackData	remainders        r    r   zMidiTrack.read~  sn     !}- PQQ%imQ7	 gv&		fg&	  +r   c                Z   d}d}|rt        |       }|j                  |      \  }}||z   }t        |       }||j                  |_        	 |j	                  |      }|}| j                  j                  |       | j                  j                  |       |}|ryy# t
        $ r |}Y w xY w)zE
        Populate .events with trackData.  Called by .read()
        r   N)r   )r   r  r	   r   r   r
   r  r;   )	r   r$  r   previousMidiEventdelta_tdttrackDataCandidatetimeCandidate	midiEvents	            r    r#  zMidiTrack.processDataToEvents  s     ,0 d+G%,%=%=i%H"B" 2IM "-I ,+<+K+K	(%NN+=>	$ KKw'KKy) )7 " !  /	s   B B*)B*c           	     8   g }| j                   D ]"  }	 |j                  |j                                $ dj                  |      }| j                  t        t        |      d      z   |z   S # t        $ r&}t        j                  d| d| d       Y d}~d}~ww xY w)a  
        Returns bytes of midi-data from the `.events` in the object.
        For conversion from music21 to MIDI.

        For example: two events, one note-on and one note-off on C4 which appears as "<"
        in the data.

        >>> mt = midi.MidiTrack(index=2)
        >>> dt1 = midi.DeltaTime(mt, time=0)
        >>> noteOn = midi.MidiEvent(mt, type=midi.ChannelVoiceMessages.NOTE_ON, channel=3)
        >>> noteOn.pitch = 60
        >>> noteOn.velocity = 20
        >>> dt2 = midi.DeltaTime(mt, time=1030)
        >>> noteOff = midi.MidiEvent(mt, type=midi.ChannelVoiceMessages.NOTE_OFF, channel=3)
        >>> noteOff.pitch = 60
        >>> noteOff.velocity = 0

        >>> mt.events = [dt1, noteOn, dt2, noteOff]
        >>> bytes = mt.getBytes()
        >>> bytes
        b'MTrk\x00\x00\x00\t\x00\x92<\x14\x88\x06\x82<\x00'

        Explanation:

        The first four bytes `MTrk` are the header for any MIDI Track

        >>> bytes[:4]
        b'MTrk'

        The next four bytes are the length of the data in bytes, the final `b'\t'`
        indicates there are 9 bytes to follow.

        >>> midi.base.getNumber(bytes[4:8], 4)
        (9, b'')

        The next byte is an empty delta time event.

        >>> bytes[8]
        0

        The next three bytes are the note-on event.

        >>> noteOn2 = midi.MidiEvent()
        >>> _ = noteOn2.parseChannelVoiceMessage(bytes[9:12])
        >>> noteOn2
        <music21.midi.MidiEvent NOTE_ON, track=None, channel=3, pitch=60, velocity=20>

        Followed by two bytes for the DeltaTime of 1030 ticks.

        >>> dt3 = midi.DeltaTime()
        >>> dt3.readUntilLowByte(bytes[12:14])
        (1030, b'')

        The 1030 is stored as b'\x88\x06' where the first byte is 136 and the second 6,
        but MIDI stores each continuing digit as the last 7 bits of a 8-bit number with
        first bit 1, so we remove the first bit by subtracting 128: 136 - 128 = 8.
        Then we multiply 8 by 128 to get 1024 and add 6 to get 1030.

        Finally, the last three bytes are the note-off event.

        >>> noteOff2 = midi.MidiEvent()
        >>> _ = noteOff2.parseChannelVoiceMessage(bytes[14:])
        >>> noteOff2
        <music21.midi.MidiEvent NOTE_OFF, track=None, channel=3, pitch=60, velocity=0>
        zConversion error for z: z
; ignored.Nr   rj   )
r  r;   r   r
   r   warnjoinr"  r   r   )r   r$   r,  ex	bytes_outs        r    r   zMidiTrack.getBytes  s    J "$	IW  !3!3!56 % HHY'	}}yY;;iGG	 ! W!!$9)Brd*"UVVWs   A**	B3BBc                P    | j                    dt        | j                         d}|S )Nz -- z events)r   r   r  )r   r   s     r    r   zMidiTrack._reprInternal  s'    zzl$s4;;/08r   c                4    | j                   D ]	  }| |_         y)a  
        We may attach events to this track before setting their `track` parameter.
        This method will move through all events and set their track to this track.

        >>> mt = midi.MidiTrack(index=2)
        >>> noteOn = midi.MidiEvent(type=midi.ChannelVoiceMessages.NOTE_ON, channel=1)
        >>> noteOn.pitch = 60
        >>> noteOn.velocity = 20
        >>> noteOn
        <music21.midi.MidiEvent NOTE_ON, track=None, channel=1, pitch=60, velocity=20>

        >>> mt.events = [noteOn]
        >>> mt.updateEvents()
        >>> noteOn
        <music21.midi.MidiEvent NOTE_ON, track=2, channel=1, pitch=60, velocity=20>
        >>> noteOn.track is mt
        True
        N)r  r   r   es     r    updateEventszMidiTrack.updateEvents  s    & AAG r   c                J    | j                   D ]  }|j                         s y y)a>  
        Return True/False if this track has any note-ons defined.

        >>> mt = midi.MidiTrack(index=2)
        >>> mt.hasNotes()
        False

        >>> noteOn = midi.MidiEvent(type=midi.ChannelVoiceMessages.NOTE_ON, channel=1)
        >>> mt.events = [noteOn]

        >>> mt.hasNotes()
        True
        TF)r  r   r4  s     r    hasNoteszMidiTrack.hasNotes,  s#     Azz|  r   c                l    |t        dd      vrt        d|       | j                  D ]	  }||_         y)a  
        Set the one-indexed channel of all events in this Track.

        >>> mt = midi.MidiTrack(index=2)
        >>> noteOn = midi.MidiEvent(type=midi.ChannelVoiceMessages.NOTE_ON, channel=1)
        >>> mt.events = [noteOn]
        >>> mt.setChannel(11)
        >>> noteOn.channel
        11

        Channel must be a value from 1-16

        >>> mt.setChannel(22)
        Traceback (most recent call last):
        music21.midi.base.MidiException: bad channel value: 22
        r5      zbad channel value: N)r,   r
   r  r   )r   r   r5  s      r    
setChannelzMidiTrack.setChannel?  s:    " a$"5eW =>>AAI r   c                    t               }| j                  D ].  }|j                         s|j                  |j                         0 t        |      S )a  
        Get all channels used in this Track (sorted) excepting events like DeltaTime
        or end events where the channel does not matter

        >>> mt = midi.MidiTrack(index=2)
        >>> noteOn = midi.MidiEvent(type=midi.ChannelVoiceMessages.NOTE_ON, channel=14)
        >>> noteOn2 = midi.MidiEvent(type=midi.ChannelVoiceMessages.NOTE_ON, channel=5)
        >>> dt = midi.DeltaTime(1000, channel=2)  # channel does not matter
        >>> noteOn3 = midi.MidiEvent(type=midi.ChannelVoiceMessages.NOTE_ON, channel=14)
        >>> noteOn4 = midi.MidiEvent(type=midi.ChannelVoiceMessages.PROGRAM_CHANGE, channel=5)

        >>> mt.events = [noteOn, noteOn2, dt, noteOn3, noteOn4]
        >>> mt.getChannels()
        [5, 14]
        )setr  r   addr   sortedr   rG   r5  s      r    getChannelszMidiTrack.getChannelsU  sA      A!#  d|r   c                    g }| j                   D ]e  }|j                  t        j                  k(  s!t	        |j
                  t              s<|j
                  |vsK|j                  |j
                         g |S )a  
        Get all unique program changes used in this Track, in order they appear.

        >>> mt = midi.MidiTrack(index=2)
        >>> pc1 = midi.MidiEvent(type=midi.ChannelVoiceMessages.PROGRAM_CHANGE)
        >>> pc1.data = 14
        >>> noteOn = midi.MidiEvent(type=midi.ChannelVoiceMessages.NOTE_ON, channel=14)
        >>> pc2 = midi.MidiEvent(type=midi.ChannelVoiceMessages.PROGRAM_CHANGE)
        >>> pc2.data = 1

        >>> mt.events = [pc1, noteOn, pc2]
        >>> mt.getProgramChanges()
        [14, 1]
        )r  r   r   rT   r*   r   r   r;   r@  s      r    getProgramChangeszMidiTrack.getProgramChangesk  s[     A.==="1663/d*AFF#	 
 r   N)r   )r   r   r  r  )r   )r$  r+   r  r   r  r  r   r  )r   r   r  r   )r  z	list[int])r   r   r   rO   r"  r   r  r  r%   r   r#  r   r   r6  r8  r;  rA  rC  r   r   r    r   r   )  s_    BF H
  6&*PNH`
,&,,r   r   c                  f    e Zd ZdZdZddZdddZddZddZddZ	ddZ
dd	Zdd
ZddZddZy)r   a  
    Low-level MIDI file writing, emulating methods from normal Python files.

    For most users, do not go here, simply use:

        score = converter.parse('path/to/file/in.mid')
        midi_out = score.write('midi', fp='path/to/file/out.mid')

    The `ticksPerQuarterNote` attribute must be set before writing. 1024 is a common value.

    This object is returned by some properties for directly writing files of midi representations.

    >>> mf = midi.MidiFile()
    >>> mf
    <music21.midi.MidiFile 0 tracks>

    Music21 can read format 0 and format 1, and writes format 1.  Format 2 files
    are not parsable.

    >>> mf.format
    1

    After loading or before writing, tracks are stored in this list.

    >>> mf.tracks
    []

    Most midi files store `ticksPerQuarterNote` and not `ticksPerSecond`

    >>> mf.ticksPerQuarterNote
    10080
    >>> mf.ticksPerSecond is None
    True

    All MidiFiles have the same `headerId`

    >>> midi.MidiFile.headerId
    b'MThd'
       MThdc                f    d | _         d| _        g | _        t        j                  | _        d | _        y )Nr5   )fileformattracksr   ticksPerQuarterticksPerQuarterNoteticksPerSecondr   s    r    r   zMidiFile.__init__  s-    %)	')(0(@(@ (,r   c                    |dvrt        d|      t        j                  t        j                  t	        ||            | _        y)z}
        Open a MIDI file path for reading or writing.

        For writing to a MIDI file, `attrib` should be "wb".
        )rbwbz0cannot read or write unless in binary mode, not:N)r
   r   r   BinaryIOopenrH  )r   filenamer   s      r    rR  zMidiFile.open  s8     % RTZ[[FF1::tHf'=>	r   c                    || _         y)a  
        Assign a file-like object, such as those provided by BytesIO, as an open file object.

        >>> from io import BytesIO
        >>> fileLikeOpen = BytesIO()
        >>> mf = midi.MidiFile()
        >>> mf.openFileLike(fileLikeOpen)
        >>> mf.close()
        N)rH  )r   fileLikes     r    openFileLikezMidiFile.openFileLike  s     	r   c                L    t        | j                        }|dk7  rdnd}| d| S )Nr5   r   r  z track)r   rJ  )r   	lenTracksplurals      r    r   zMidiFile._reprInternal  s.    $	!QBF6(++r   c                R    | j                   sy| j                   j                          y)z!
        Close the file.
        N)rH  closer   s    r    r[  zMidiFile.close  s     yy		r   c                    | j                   st        d      | j                  | j                   j                                y)z<
        Read and parse MIDI data stored in a file.
        No file is open.N)rH  r   readstrr   r   s    r    r   zMidiFile.read  s.     yy.//TYY^^%&r   c                   |dd dk(  st        d|dd       t        |dd d      \  }}|dk7  rt        d      t        |d      \  }}|| _        |d	vrt        d
t               t        |d      \  }}t        |d      \  }}|dz  r2|dz	  dz   }|dz  }|dvrt        d|       |dk(  rd}||z  | _        n
|dz  | _        t        |      D ]9  }t        |      }	|	j                  |      }| j                  j                  |	       ; y)a  
        Read and parse MIDI data as a bytes, putting the
        data in `.ticksPerQuarterNote` and a list of
        `MidiTrack` objects in the attribute `.tracks`.

        The name readstr is a carryover from Python 2.
        It works on bytes objects, not strings
        Nrj   rF  z!badly formatted midi bytes, got:    rl   zbadly formatted midi bytesrh   )r   r5   z cannot handle midi file format:    r)   ir9   )            zcannot handle ticks per frame: rd  re  i  )
r
   r   rI  rM  rL  r,   r   r   rJ  r;   )
r   r$   r%   midiFormatType	numTracksdivisionframesPerSecondticksPerFramer.   trks
             r    r^  zMidiFile.readstr  sN    !}'"CIcrNCU VWW &imQ7	Q; <==$-i$;!	$'"B6( KLL(A6	9'	15) f!)Q% 78O$tOM$44#&Em_$UVV" ""//"AD'/&'8D$ y!AA,C+IKKs# "r   c                    | j                   st        d      | j                         }| j                   j                  |       y)zN
        Write MIDI data as a file to the file opened with `.open()`.
        r]  N)rH  r   writestrwrite)r   wss     r    rn  zMidiFile.write  s2     yy.//]]_		r   c                n    | j                         }| j                  D ]  }||j                         z   } |S )z
        Generate the MIDI data header and convert the list of
        MidiTrack objects in self.tracks into MIDI data and return it as bytes.

        The name `writestr` is a carry-over from Python 2.  It works on bytes, not strings.
        )writeMThdStrrJ  r   )r   r$   rk  s      r    rm  zMidiFile.writestr  s6     %%'	;;C!CLLN2I r   c                   | j                   }|dz  dk7  rt        d      | j                  t        dd      z   t        | j                  d      z   }|t        t        | j                        d      z   }|t        |d      z   }|S )z
        Convert the information in self.ticksPerQuarterNote
        into MIDI data header and return it as bytes.

        The name `writeMThdStr` is a carry-over from Python 2.  It works on bytes, not strings.
        ra  r   zMCannot write midi bytes unless self.ticksPerQuarterNote is a multiple of 1024rl   rj   rh   )rL  r
   r"  r   rI  r   rJ  )r   rh  r$   s      r    rq  zMidiFile.writeMThdStr(  s     ++v!#_a aMMIaO3iQ6OO		#dkk*:A >>		(A 66	r   NrD  )rO  )rU  z
t.BinaryIOr  r   r  )r$   r+   r  r   r  )r   r   r   rO   r"  r   rR  rV  r   r[  r   r^  rn  rm  rq  r   r   r    r   r     sB    &N H-	?
,
'-$^
r   r   z
list[type]
_DOC_ORDER__main__)r$   r   r%   r   r  ztuple[int, int])r$   r+   r%   r   r  r  )r$   zbytes | intr%   r   r  ztuple[int, bytes | int])r$   r+   r  r  )r<   r   r%   r   r  r+   )r7   r   r  r+   )rF   zIterable[int]r  r+   )*rO   
__future__r   __all__collections.abcr   r   typingr   r   music21.common.enumsr   music21r   r   r   r   Environmentr   Music21Exceptionr
   r   r   r   r   r   r   r   r   r   r   Literalr   ProtoM21Objectr	   r   r   r   rs  __annotations__r   mainTestr   r   r    <module>r     s   #$ %    -     &{&&{3
	L11 	 
 
 
 
2#j<?~:aH+\< ,  6, 
     ii	 P&& Pfc	 cLW&& Wt
tw%% tr $Y	8D
J DzG r   