| jupyter |
|
|---|
MIDI synthesizer could be used with the midistream library.
%load_ext pythonhere
%connect-there%%there
from midistream import Synthesizer
midi = Synthesizer()Get syntesizer configuration:
%%there
from pprint import pprint
pprint(midi.config)%%there
midi.write([0x90, 60, 127])- 0x90 - code for MIDI event Note ON on channel 0
- 60 - C4 (middle C) note number
- 127 - maximum velocity
MIDI commands reference:
Off middle C note:
%%there
midi.write([0x80, 60, 127])midistream.helpers module could be used to construct MIDI messages
%%there
from midistream.helpers import (
Control,
Note,
midi_control_change,
midi_note_on,
midi_note_off,
midi_channels,
midi_instruments,
midi_program_change,
note_name,
)%%there
program = 44
print(f"Change program on channel 0 to: {midi_instruments[program]}")
midi.write(midi_program_change(program))
midi.write(midi_note_on(Note.Es4))%%there -d 1
midi.write(midi_note_off(Note.Es4))MIDI channels allows to play different parts at the same time.
16 channels are available, channel 9 is a persussion channel.
%%there
print(list(midi_channels()))Helpers functions accept an optional channel argument:
%%there
midi.write(midi_program_change(20, channel=1))
midi.write(midi_note_on(Note.A3, channel=1))
midi.write(midi_program_change(21, channel=2))
midi.write(midi_note_on(Note.E4, channel=2))%%there -d 1
midi.write(midi_note_off(Note.A3, channel=1))At this point, A3 note is off on channel 1, but channel 2 is continue to sound.
%%there -d 1
midi.write(midi_note_off(Note.E4, channel=2))%%there
midi.write(
midi_control_change(Control.volume, 110) +
midi_control_change(Control.modulation, 90) +
midi_program_change(62) +
midi_note_on(Note.D3) + midi_note_on(Note.F3)
)%%there -d 2
midi.write(midi_control_change(Control.all_sound_off)) # Turns off sounds on a channel instantly
midi.write(midi_control_change(Control.modulation, 0))are controlled with Synthesizer properties:
%%there
from midistream import ReverbPreset
midi.volume = 100 # Set maximum master volume
midi.reverb = ReverbPreset.ROOM # Enable Room presetPlay note every second:
%%there
from kivy.clock import Clock
beat_program = 33
beat_note = Note.C3
def beat(_):
midi.write(midi_program_change(beat_program, channel=1))
midi.write(midi_note_on(beat_note, channel=1))
beat_event = Clock.schedule_interval(beat, .5)Change playing instrument and note:
%%there -d 2
beat_note += 12
beat_program = 115Stop the scheduled event:
%%there -d 2
Clock.unschedule(beat_event)%%there kv
BoxLayout:%%there
from kivy.properties import NumericProperty
from kivy.uix.label import Label
class NoteLabel(Label):
"""Play and show note."""
prev_note = 0
note = NumericProperty(Note.E2)
font_size = 150
def on_note(self, obj, note):
note = int(note) # animation produce float values
if note == self.prev_note: # do not repeat same note
return
self.text = f"{note} : {note_name(note):3}"
self.prev_note = note
midi.write(midi_note_on(note) + midi_note_on(note + 7))%%there
from kivy.animation import Animation
root.clear_widgets()
widget = NoteLabel()
root.add_widget(widget)
midi.write(midi_program_change(36))
sequence = (
Animation(note=widget.note + 6, duration=1.5, transition="in_elastic") +
Animation(note=widget.note + 13, duration=1.5, transition="in_elastic") +
Animation(note=widget.note, duration=4, transition="out_sine") +
Animation(note=widget.note, duration=.8)
)
sequence.bind(on_complete=lambda *args: midi.write(midi_control_change(Control.all_sound_off)))
sequence.start(widget)%there -d8 screenshot -w 200%%there kv
#:import midi_instruments midistream.helpers.midi_instruments
<InstrumentButton>:
group: "instruments"
text: "[b]{:3}[/b] : {}".format(self.instrument_code, midi_instruments[self.instrument_code])
markup: True
size_hint: 1, None
text_size: self.size
valign: "center"
padding_x: 40
ScrollView:
do_scroll_x: False
size_hint: 1, 1
BoxLayout:
id: instruments
orientation: "vertical"
size_hint: 1, None
height: self.minimum_size[1]%%there
from kivy.properties import NumericProperty
from kivy.uix.togglebutton import ToggleButton
class InstrumentButton(ToggleButton):
instrument_code = NumericProperty()
notes = Note.A3, Note.D4, Note.A5
def on_state(self, widget, value):
if value == "down":
midi.write(midi_program_change(self.instrument_code))
for note_code in self.notes:
midi.write(midi_note_on(note_code))
else:
for note_code in self.notes:
midi.write(midi_note_off(note_code))
for code in list(midi_instruments.keys()):
root.ids.instruments.add_widget(InstrumentButton(instrument_code=code))%there -d 1 screenshot -w 200