Audio::MIDI::Note - playable MIDI note
This module lets you play notes on a MIDI hardware or software device using methods that allow to replicate sheet music.
- NAME
- DESCRIPTION
- NON Perl 6 REQUIREMENTS
- SYNOPSIS
- PLAYING TIPS
- ATTRIBUTES
- METHODS
- REPOSITORY
- BUGS
- AUTHOR
- LICENSE
This module uses Audio::PortMIDI
under the hood, which requires portmidi C library.
On Debian and derivatives, you can install it with sudo apt-get install libportmidi0
Replication of this sheet music.
Note: on my system, the midi player on that page kills timidity and I have
to do sudo service timidity restart
to get my scripts to work again...
use Audio::PortMIDI;
use Audio::MIDI::Note;
my $stream = Audio::PortMIDI.new.open-output: 3, 32;
END { $stream.close }
my Audio::MIDI::Note $note .= new: :20tempo :$stream, :value(½), :49velocity;
# Pachelbel `Canon` (in C key)
# Comments reference this sheet music: http://www.8notes.com/scores/420.asp
$note .play(<C4 E4>).play(<G3 D4>) # first line of bars, with one repeat
.play(<A3 C4>).play(<E3 B3>)
.play(<F3 A3>).play(<C3 G3>)
.play(<F3 A3>).play(<G3 B3>)
for ^2;
$note .play(<C4 G4 E5>).play(<G3 B4 D5>) # second line of bars
.play(<A3 C5 >).play(<E3 G4 B4>)
.play(<F3 C4 A4>).play(<C3 E4 G4>)
.play(<F3 F4 A4>).play(<G3 D4 B4>)
# first two notes of the chord are half-notes and third one is a crotchet,
# so we play the half-notes in async with .aplay, and then do
# the crotchet series with blocking .play
.velocity(64).value(¼) # play louder and switch to quarter note default
.aplay(<C4 E4>, ½).play('C5').play('C5')
.aplay(<G3 D4>, ½).play('D5').play('B4')
# 10th bar
.aplay(<A3 C4>, ½).play('C5').play('E5')
.aplay('E3', ½).play('G5').play('G4')
# 11th and 12th bars
.aplay(<F3 A3>, ½).play('A4').play('F4')
.aplay('C3' , ½).play('E4').play('G4')
.aplay(<F3 A3>, ½).play('F4').play('C5')
.aplay(<G3 B3>, ½).play('B5').play('G4')
# 13th bar; after the first chord, we're asked to play louder (velocity)
.aplay(<C4 E4>, ½).play('C5')
.velocity(80)
.play('E5', ⅛).play('G5', ⅛).play('G5', ⅛)
.play('A5', ⅛).play('G5', ⅛).play('F5', ⅛)
.aplay(<A3 C4>, ½).play('E5', ¼+⅛).play('E5', ⅛)
.aplay(<E3 G3>, ½).play('E5', ⅛).play('F5', ⅛).play('E5', ⅛).play('D5', ⅛)
# 15th, 16th bar
.aplay(<F3 A3>, ½).play('C5', ⅛).play('Bb4', ⅛).play('A4', ⅛).play('Bb4', ⅛)
.aplay(<C3 E3>, ½).play('G4').play('E4')
.aplay(<F3 A3>, ½).play('C4').play('F4', ⅛).play('E4', ⅛)
.aplay(<G3 B3>, ½).play('D4').play('G4', ⅛).play('F4', ⅛)
# 17th bar: we'll sound half-notes in async, and will use .rest
# to play the quarter-note rest on the treble clef.
# .rest can take a rest value as argument, but our current value is
# already a crotchet, so no argument is needed:
.aplay(<C4 E3>, ½).rest.velocity(64).play('C5')
.aplay('G3', ½).play('D5').play('B4')
# Last row of bars
.aplay(<A3 C4>, ½).play('C5').play('E4')
.aplay(<E3 B3>, ½).play('G4', ¼+⅛).play('A4', ⅛)
.aplay(<F3 A3>, ½).play('F4').play('C4')
.aplay(<C3 G3>, ½).play('E4').play('G4')
.aplay(<F3 A3>, ½).play('F4').play('E4')
.aplay(<G3 B3>, ½).play('D4').play('G4')
.play(<C3 C4 E3>, 1)
;
Part of the solo from A World to Win, showing swap of instruments, re-use of repeating pieces of music, calculation of triplet note values, and use of on- and off-beat velocity shortcuts.
use Audio::MIDI::Note;
my $stream = Audio::PortMIDI.new.open-output: 3, 32;
END { $stream.close }
my Audio::MIDI::Note $note .= new: :31tempo :30instrument :$stream :value(⅔ * ⅛);
# Looping `Gorgoroth - A World to Win` solo with organ chord in the background.
# We use triplet notes and save repeating pieces into variables for reuse
my &rhythm = *.play('D#5').play('D5').play('C5');
my &riff = {
.aplay(<C4 E4 G4>, 4, :19instrument, :40velocity)
.play('C5', ⅔*(¼+⅛) )
.riff(&rhythm).play('C5', ⅔*(¼+⅛) )
.riff(&rhythm).play('G#4', ⅔*(¼+⅛) )
.riff(&rhythm).play('A#4', ⅔*(¼+⅛) )
.riff(&rhythm).play('C5', ⅔*(¼+⅛) )
.riff(&rhythm).play('C5', ⅔*(¼+⅛) )
.riff(&rhythm);
};
$note .riff(&riff)
.play('G5', ¼, :on).play('F5', ¼, :off)
.play('D#5', ¼ ).play('D5', ¼, :off)
.riff(&riff)
.play('F5', ¼,:on ).play('D#5', ¼, :off)
.play('D5', ¼ ).play('A#4', ¼, :off)
for ^10;
- Save repeating chunks into subs and play them with
.riff
method - Play the piece using the shortest notes and rests it requires, using
.play
/.rest
methods. Use the asynchronous.aplay
method to play longer notes that overlap these shorter ones.
Note: to facilitate chaining, all attributes can be either assigned to directly or be given the new value as an argument:
$note.value = ½;
$note.value(½); # same as above; returns invocant
The Audio::PortMIDI::Stream
object opened at a MIDI output device. See
Audio::PortMIDI for details.
Positive Int
.
Specifies the tempo of the piece in beats per minute per WHOLE note.
Defaults to: 40
Numeric
. Specifies the default value (amount of time it rings) of the played
notes. Defaults to: ¼
0 <= Int <= 127
. Specifies the default velocity (similar to volume) of the
played notes; 127
indicates as loud as possible.
Defaults to: 80
(mf
loudness in sheet music).
Int
. MIDI Patch code
for the default instrument to use. Defaults to: 0
(piano)
Int
. Specifies the MIDI channel to use. Defaults to: 0
my Audio::MIDI::Note $note .= new:
:stream(Audio::PortMIDI.new.open-output: 3, 32)
:20tempo
:value(½)
:49velocity
:30instrument
:0channel;
Creates and returns a new Audio::MIDI::Note
object. See ATTRIBUTES
section for details on the accepted parameters.
$note.aplay('C5')
.aplay(<C4 E4 G4>, ⅛, :on-on, :100velocity, :30instrument);
Same as .play
, but is asynchronous and returns immediately.
Useful to play longer notes that overlap shorter ones (that you would play
with .play
). Takes the same arguments as .play
$note.play('C5')
.play(<C4 E4 G4>, ⅛, :on-on, :100velocity, :30instrument);
Plays one or more notes (simultaneously). Takes the following arguments:
An Str
or a List
of strings representing the notes to play. Multiple notes
will be played at the same time, not consecutively. Valid notes are from
C0
through G#10
/Ab10
. Use hashmark (#
) to indicate sharps and lower-case B
(b
) to indicate flats. This argument is case-sensitive.
Note value for the currently played notes. Defaults to: the value of
:value
attribute.
Note velocity for the currently played notes. Defaults to: the value of
:velocity
attribute.
MIDI patch code for the instrument for the currently played notes.
Defaults to: the value of :instrument
attribute.
Velocity control shortcuts for on/off beats. :on-on
is the loudest,
:on
is less so, but still louder than normal velocity; :off
is less loud
than normal velocity.
$note.rest
.rest(⅛);
"Plays" a rest by blocking execution for the specified note value. Takes
Numeric
note value, which defaults to: the value of :value
attribute.
my &riff = *.play('C4').play('E4').play('G4');
$note.&riff;
$note.riff(&riff);
The last two lines in the code above are equivalent. The only issue is you can't
split a method chain that uses &riff
onto multiple lines. This is where the
.riff
method comes into play. It takes your Callable
as an argument and
lets you break up your method chains on multiple lines.
Storing bits of music into variables and then playing them with .riff
lets
you re-use repeating bars or fragments in a piece of music.
Also worth noting: method chains started on a WhateverStar
also cannot be
broken up into multiple lines. Just use a set of curlies instead:
my &riff = {
.play('C4').play('E4')
.play('G4').play('C5');
}
Fork this module on GitHub: https://github.com/zoffixznet/perl6-Audio-MIDI-Note
To report bugs or request features, please use https://github.com/zoffixznet/perl6-Audio-MIDI-Note/issues
Zoffix Znet (http://zoffix.com/)
You can use and distribute this module under the terms of the
The Artistic License 2.0. See the LICENSE
file included in this
distribution for complete details.