MidiPacket
概述
MidiPacket
类封装了 MIDI 消息,包括其端口、状态和数据。它提供用于创建、分析和操作 MIDI 消息的实用方法,用于输入和输出操作。
Python MidiPacket 类实现位于 Applications/Python/PikaPython/MatrixOS_MidiPacket.py,类型提示位于 Applications/Python/PikaPython/_MatrixOS_MidiPacket.pyi。
Constructor
MidiPacket(*val)
def __init__(self, *val) -> None
Creates a MIDI packet with variable arguments depending on the message type.
Parameters:
*val
: Variable arguments for constructing different types of MIDI messages
Static Factory Methods
Note Messages
NoteOn
def NoteOn(self, channel: int, note: int, velocity: int = 127) -> MidiPacket
Creates a MIDI Note On message.
Parameters:
channel
(int
): MIDI channel (0-15)note
(int
): MIDI note number (0-127)velocity
(int
, optional): Note velocity (0-127, defaults to 127)
Returns:
MidiPacket
: Note On packet
Example:
# Play middle C on channel 1 with medium velocity
packet = MidiPacket()
note_on = packet.NoteOn(0, 60, 64)
MatrixOS.MIDI.Send(note_on)
NoteOff
def NoteOff(self, channel: int, note: int, velocity: int = 0) -> MidiPacket
Creates a MIDI Note Off message.
Parameters:
channel
(int
): MIDI channel (0-15)note
(int
): MIDI note number (0-127)velocity
(int
, optional): Release velocity (0-127, defaults to 0)
Example:
# Stop middle C on channel 1
packet = MidiPacket()
note_off = packet.NoteOff(0, 60, 0)
MatrixOS.MIDI.Send(note_off)
Control Messages
ControlChange
def ControlChange(self, channel: int, controller: int, value: int) -> MidiPacket
Creates a MIDI Control Change message.
Parameters:
channel
(int
): MIDI channel (0-15)controller
(int
): Controller number (0-127)value
(int
): Controller value (0-127)
Example:
# Set volume (CC 7) to maximum on channel 1
packet = MidiPacket()
volume_cc = packet.ControlChange(0, 7, 127)
MatrixOS.MIDI.Send(volume_cc)
ProgramChange
def ProgramChange(self, channel: int, program: int) -> MidiPacket
Creates a MIDI Program Change message.
Parameters:
channel
(int
): MIDI channel (0-15)program
(int
): Program number (0-127)
Example:
# Change to program 1 (piano) on channel 1
packet = MidiPacket()
program_change = packet.ProgramChange(0, 0)
MatrixOS.MIDI.Send(program_change)
PitchBend
def PitchBend(self, channel: int, value: int) -> MidiPacket
Creates a MIDI Pitch Bend message.
Parameters:
channel
(int
): MIDI channel (0-15)value
(int
): Pitch bend value (0-16383, 8192 = center)
Example:
# Bend pitch up on channel 1
packet = MidiPacket()
pitch_bend = packet.PitchBend(0, 10000)
MatrixOS.MIDI.Send(pitch_bend)
Pressure Messages
AfterTouch
def AfterTouch(self, channel: int, note: int, pressure: int) -> MidiPacket
Creates a MIDI Polyphonic Key Pressure (Aftertouch) message.
Parameters:
channel
(int
): MIDI channel (0-15)note
(int
): MIDI note number (0-127)pressure
(int
): Pressure value (0-127)
ChannelPressure
def ChannelPressure(self, channel: int, pressure: int) -> MidiPacket
Creates a MIDI Channel Pressure message.
Parameters:
channel
(int
): MIDI channel (0-15)pressure
(int
): Pressure value (0-127)
System Real-Time Messages
Clock
def Clock(self) -> MidiPacket
Creates a MIDI Clock message for tempo synchronization.
Start
def Start(self) -> MidiPacket
Creates a MIDI Start message to begin sequencer playback.
Continue
def Continue(self) -> MidiPacket
Creates a MIDI Continue message to resume sequencer playback.
Stop
def Stop(self) -> MidiPacket
Creates a MIDI Stop message to halt sequencer playback.
ActiveSense
def ActiveSense(self) -> MidiPacket
Creates a MIDI Active Sensing message.
Reset
def Reset(self) -> MidiPacket
Creates a MIDI System Reset message.
System Common Messages
SongPosition
def SongPosition(self, position: int) -> MidiPacket
Creates a MIDI Song Position Pointer message.
Parameters:
position
(int
): Song position in MIDI beats
SongSelect
def SongSelect(self, song: int) -> MidiPacket
Creates a MIDI Song Select message.
Parameters:
song
(int
): Song number (0-127)
TuneRequest
def TuneRequest(self) -> MidiPacket
Creates a MIDI Tune Request message.
Getter Methods
Port
def Port(self) -> MidiPortID
Gets the MIDI port identifier for this packet.
Returns:
MidiPortID
: The port identifier
Status
def Status(self) -> MidiStatus
Gets the MIDI status/message type.
Returns:
MidiStatus
: The message status type
Channel
def Channel(self) -> int
Gets the MIDI channel number.
Returns:
int
: Channel number (0-15)
Note
def Note(self) -> int
Gets the MIDI note number or controller number.
Returns:
int
: Note/controller value (0-127)
Controller
def Controller(self) -> int
Gets the MIDI controller number (alias for Note()).
Returns:
int
: Controller number (0-127)
Velocity
def Velocity(self) -> int
Gets the velocity or pressure value.
Returns:
int
: Velocity/pressure value (0-127)
Value
def Value(self) -> int
Gets the value associated with the MIDI message (e.g., pitch bend, control change value).
Returns:
int
: The message value
Setter Methods
SetStatus
def SetStatus(self, status: MidiStatus) -> None
Sets the MIDI status/message type.
SetChannel
def SetChannel(self, channel: int) -> None
Sets the MIDI channel number.
SetNote
def SetNote(self, note: int) -> None
Sets the MIDI note or controller number.
SetController
def SetController(self, controller: int) -> None
Sets the MIDI controller number.
SetVelocity
def SetVelocity(self, velocity: int) -> None
Sets the velocity or pressure value.
SetValue
def SetValue(self, value: int) -> None
Sets the message value.
Helper Methods
Length
def Length(self) -> int
Gets the length of the MIDI message based on its status.
Returns:
int
: Message length in bytes
SysEx
def SysEx(self) -> bool
Checks if the message is a System Exclusive (SysEx) message.
Returns:
bool
: True if SysEx message
SysExStart
def SysExStart(self) -> bool
Checks if the message is the start of a SysEx message.
Returns:
bool
: True if SysEx start message
Usage Examples
Basic Note Playing
def play_scale():
"""Play a C major scale"""
notes = [60, 62, 64, 65, 67, 69, 71, 72] # C major scale
channel = 0
for note in notes:
# Play note
packet = MidiPacket()
note_on = packet.NoteOn(channel, note, 64)
MatrixOS.MIDI.Send(note_on)
# Wait
MatrixOS.SYS.DelayMs(500)
# Stop note
note_off_packet = MidiPacket()
note_off = note_off_packet.NoteOff(channel, note, 0)
MatrixOS.MIDI.Send(note_off)
MatrixOS.SYS.DelayMs(100)
play_scale()
MIDI Controller
def midi_controller():
"""Create a MIDI controller using the key grid"""
# Define scale and channel
scale = [60, 62, 64, 65, 67, 69, 71, 72] # C major
channel = 0
active_notes = {}
print("MIDI Controller Active")
print("Press keys to play notes")
while True:
key_event = MatrixOS.KeyPad.Get(50)
if key_event is not None:
key_id = key_event.ID()
key_info = key_event.KeyInfo()
xy = MatrixOS.KeyPad.ID2XY(key_id)
if key_info.Active() and key_id not in active_notes:
# Map key to note
if key_id < len(scale):
note = scale[key_id]
velocity = int(key_info.Velocity() * 127)
# Send Note On
packet = MidiPacket()
note_on = packet.NoteOn(channel, note, velocity)
MatrixOS.MIDI.Send(note_on)
# Track active note
active_notes[key_id] = note
# Light up key
MatrixOS.LED.SetColor(xy, Color(0, 255, 0))
print(f"Note On: {note}, Velocity: {velocity}")
elif not key_info.Active() and key_id in active_notes:
# Send Note Off
note = active_notes[key_id]
packet = MidiPacket()
note_off = packet.NoteOff(channel, note, 0)
MatrixOS.MIDI.Send(note_off)
# Remove from active notes
del active_notes[key_id]
# Turn off LED
MatrixOS.LED.SetColor(xy, Color(0, 0, 0))
print(f"Note Off: {note}")
MatrixOS.LED.Update()
midi_controller()
Control Change Interface
def cc_controller():
"""Create a Control Change interface"""
channel = 0
controllers = {
0: 7, # Volume
1: 10, # Pan
2: 71, # Resonance
3: 74, # Cutoff
8: 91, # Reverb
9: 93, # Chorus
}
current_values = {cc: 64 for cc in controllers.values()} # Initialize to middle
def update_display():
"""Update LED display based on controller values"""
for key_id, cc_num in controllers.items():
value = current_values[cc_num]
xy = MatrixOS.KeyPad.ID2XY(key_id)
# Map value to color intensity
intensity = int((value / 127.0) * 255)
MatrixOS.LED.SetColor(xy, Color(255, 0, 0))
MatrixOS.LED.Update()
print("Control Change Interface")
print("Keys 0,1,2,3,8,9 control different CCs")
update_display()
while True:
key_event = MatrixOS.KeyPad.Get(100)
if key_event is not None:
key_id = key_event.ID()
key_info = key_event.KeyInfo()
if key_info.Active() and key_id in controllers:
cc_num = controllers[key_id]
velocity = key_info.Velocity()
# Map velocity to CC value
cc_value = int(velocity * 127)
current_values[cc_num] = cc_value
# Send Control Change
packet = MidiPacket()
cc_msg = packet.ControlChange(channel, cc_num, cc_value)
MatrixOS.MIDI.Send(cc_msg)
print(f"CC {cc_num}: {cc_value}")
update_display()
cc_controller()
MIDI Sequencer
def simple_sequencer():
"""Simple 8-step MIDI sequencer"""
channel = 0
sequence = [60, 62, 64, 65, 67, 69, 71, 72] # C major scale
current_step = 0
playing = False
bpm = 120
step_time = int(60000 / (bpm * 4)) # 16th notes
def update_display():
"""Update sequencer display"""
for i in range(8):
xy = MatrixOS.KeyPad.ID2XY(i)
if i == current_step and playing:
MatrixOS.LED.SetColor(xy, Color(255, 255, 255)) # Current step
elif sequence[i] > 0:
MatrixOS.LED.SetColor(xy, Color(0, 255, 0)) # Active step
else:
MatrixOS.LED.SetColor(xy, Color(100, 0, 0)) # Inactive step
# Play/stop button
play_xy = MatrixOS.KeyPad.ID2XY(56) # Bottom-right corner
color = Color(0, 255, 0) if playing else Color(255, 0, 0)
MatrixOS.LED.SetColor(play_xy, color)
MatrixOS.LED.Update()
last_step_time = MatrixOS.SYS.Millis()
active_note = None
print("MIDI Sequencer")
print("Keys 0-7: Edit sequence")
print("Key 56: Play/Stop")
update_display()
while True:
current_time = MatrixOS.SYS.Millis()
# Handle sequencer timing
if playing and (current_time - last_step_time) >= step_time:
# Stop previous note
if active_note is not None:
packet = MidiPacket()
note_off = packet.NoteOff(channel, active_note, 0)
MatrixOS.MIDI.Send(note_off)
# Play current step if active
if sequence[current_step] > 0:
packet = MidiPacket()
note_on = packet.NoteOn(channel, sequence[current_step], 64)
MatrixOS.MIDI.Send(note_on)
active_note = sequence[current_step]
else:
active_note = None
# Advance step
current_step = (current_step + 1) % 8
last_step_time = current_time
update_display()
# Handle key input
key_event = MatrixOS.KeyPad.Get(10)
if key_event is not None:
key_id = key_event.ID()
key_info = key_event.KeyInfo()
if key_info.Active():
if key_id < 8:
# Edit sequence step
if sequence[key_id] > 0:
sequence[key_id] = 0 # Turn off
else:
sequence[key_id] = 60 + key_id # Turn on with note
update_display()
elif key_id == 56:
# Toggle play/stop
playing = not playing
if not playing and active_note is not None:
# Stop current note
packet = MidiPacket()
note_off = packet.NoteOff(channel, active_note, 0)
MatrixOS.MIDI.Send(note_off)
active_note = None
print("Sequencer:", "Playing" if playing else "Stopped")
update_display()
simple_sequencer()
MIDI Message Analysis
def midi_monitor():
"""Monitor and analyze incoming MIDI messages"""
def analyze_packet(packet):
"""Analyze MIDI packet and print details"""
status = packet.Status()
channel = packet.Channel()
print(f"MIDI Message - Status: {status}, Channel: {channel}")
# Analyze based on message type
if status == MidiStatus.NoteOn:
note = packet.Note()
velocity = packet.Velocity()
print(f" Note On: Note {note}, Velocity {velocity}")
elif status == MidiStatus.NoteOff:
note = packet.Note()
velocity = packet.Velocity()
print(f" Note Off: Note {note}, Velocity {velocity}")
elif status == MidiStatus.ControlChange:
controller = packet.Controller()
value = packet.Value()
print(f" Control Change: CC {controller}, Value {value}")
elif status == MidiStatus.ProgramChange:
program = packet.Note()
print(f" Program Change: Program {program}")
elif status == MidiStatus.PitchChange:
value = packet.Value()
print(f" Pitch Bend: Value {value}")
print(f" Port: {packet.Port()}")
print(f" Length: {packet.Length()}")
print(f" SysEx: {packet.SysEx()}")
print("---")
print("MIDI Monitor - analyzing incoming messages...")
while True:
# This would typically receive MIDI messages
# For demonstration, create some test messages
packet1 = MidiPacket()
packet2 = MidiPacket()
packet3 = MidiPacket()
test_messages = [
packet1.NoteOn(0, 60, 64),
packet2.ControlChange(0, 7, 100),
packet3.NoteOff(0, 60, 0),
]
for packet in test_messages:
analyze_packet(packet)
MatrixOS.SYS.DelayMs(2000)
midi_monitor()
Comments