MIDI::Opus(3pm)
π NAME π
MIDI::Opus β functions and methods for MIDI opuses. that's it, that's the name, and yes this is the MIDI::Opus(3pm) page from the User Contributed Perl Documentation section, we're doing the whole thing bestie π
β¨ SYNOPSIS β¨
use MIDI; # uses MIDI::Opus et al
foreach $one (@ARGV) {
my $opus = MIDI::Opus->new({ 'from_file' => $one, 'no_parse' => 1 });
print "$one has ", scalar( $opus->tracks ) " tracks\n";
}
exit;
π¦ DESCRIPTION π¦
MIDI::Opus gives you a constructor and a bunch of methods for objects that represent a MIDI opus (AKA a "song", iykyk). it's part of the MIDI suite, one big happy family π«Ά
so an opus object has three attributes:
- a format (
0for MIDI Format 0) - a tick parameter (that's the parameter
"division"inMIDI::Filespec) - and a list of tracks objects β these are literally the real content of that opus, the main characters fr
also, heads up, real talk: options you specify for encoding or decoding an opus might not be documented in this module's docs at all, because they may be β and honestly, generally are β options that just get passed down to the decoder/encoder in MIDI::Event. so like. go see MIDI::Event for an explanation of most of them, actually. it's giving "not my department" π
π CONSTRUCTOR AND METHODS π
MIDI::Opus providesβ¦ a whole menu, let's go one by one π
π the constructor MIDI::Opus->new({ ...options... })
this returns a brand new opus object. the options β which are optional, cute β are an anonymous hash.
by default you get a new format-0 opus with no tracks and a tick parameter of 96.
there are six recognized options:
"format"β sets the MIDI format number of the new object (generally either0or1)"ticks"β sets its ticks parameter"tracks"β sets the tracks of the new opus to the contents of the list-reference you provide"tracks_r"β an exact synonym of"tracks""from_file"β reads the opus from the given filespec"from_handle"β reads the opus from the given filehandle reference (e.g.,*STDIN{IO}), after having calledbinmode()on that handle, if that's a problem
if you specify either "from_file" or "from_handle", you probably don't want to specify any of the other options β altho you may well want to specify options that'll get passed down to the decoder in MIDI::Event, such as 'include' => ['sysex_f0', 'sysex_f7'], just for example.
finally, the option "no_parse" can be used in conjunction with either "from_file" or "from_handle", and, if true, it will block MTrk tracks' data from being parsed into MIDI events, and will leave them as track data (i.e., what you get from $track->data). this is useful if you are just moving tracks around across files (or just counting them in files, as in the code in the SYNOPSIS above), without having to deal with any of the events in them. (actually, this option is implemented in code in MIDI::Track, but in a routine there that's left undocumented, as you should access it only thru here. mysterious. iconic.)
π« the method $new_opus = $opus->copy
this duplicates the contents of the given opus, and returns the duplicate. if you are unclear on why you may need this function, go read the documentation for the "copy" method in MIDI::Track. it'll make sense, promise π€
π the method $opus->tracks( @tracks )
returns the list of tracks in the opus, possibly after having set it to @tracks, if specified and not empty.
careful bestie: if you happen to want to set the list of tracks to an empty list, for whatever reason, you have to use $opus->tracks_r([]). no shortcuts on this one.
in other words: $opus->tracks(@tracks) is how to set the list of tracks (assuming @tracks is not empty), and @tracks = $opus->tracks is how to read the list of tracks. slay β¨
π§ the method $opus->tracks_r( $tracks_r )
returns a reference to the list of tracks in the opus, possibly after having set it to $tracks_r, if specified. $tracks_r can actually be any listref, whether it comes from a scalar as in $some_tracks_r, or from something like [@tracks], or just plain old \@tracks.
originally $opus->tracks was the only way to deal with tracks, but $opus->tracks_r got added to make possible:
- setting the list of tracks to
(), for whatever that's worth - parallel structure between
MIDI::Opus::tracks[_r]andMIDI::Tracks::events[_r] - so you can directly manipulate the opus's tracks, without having to copy the list of tracks back and forth
this way, you can say:
$tracks_r = $opus->tracks_r();
@some_stuff = splice(@$tracks_r, 4, 6);
but if you don't know how to deal with listrefs like that, that's OK, just use $opus->tracks. no pressure, no gatekeeping π«Ά
πΈ the method $opus->ticks( $tick_parameter )
returns the tick parameter from $opus, after having set it to $tick_parameter, if provided.
β the method $opus->format( $format )
returns the MIDI format for $opus, after having set it to $format, if provided.
π¦ the method $new_opus = $opus->skyline
this skylines an $opus. it first converts the score to format 0 with the skyline result as the track. see the documentation for Score::skyline() and Track::skyline().
heads up: the original$opusis modified β useMIDI::Opus::copyif you want to take a copy first. also, appearance order of concurrent events may not be preserved. π
π the method $new_opus = $opus->format0
this converts $opus to format 0 by collapsing all tracks into one.
again: original$opusis modified, useMIDI::Opus::copyif you want to take a copy first.
π the method $new_opus = $opus->quantize
this grid quantizes an opus. it simply calls MIDI::Score::quantize on every track. see the docs for MIDI::Score::quantize.
and again, for real this time: the original opus is destroyed, use MIDI::Opus::copy if you want to take a copy first. that's not a vibe, that's a fact π
π the method $opus->dump( { ...options...} )
dumps the opus object as a bunch of text, for your perusal. options include:
"flat"β if true, will have each event in the opus as a tab-delimited line β or as delimited with whatever you specify with option"delimiter"- otherwise, dump the data as Perl code that, if run, would/should reproduce the opus
"dump_tracks"β for concision's sake, the track data isn't dumped, unless you specify this option as true
π the method $opus->write_to_file('filespec', { ...options...} )
writes $opus as a MIDI file named by the given filespec. the options hash is optional, and whatever you specify as options percolates down to the calls to MIDI::Event::encode β which see. currently this just opens the file, calls $opus->write_to_handle on the resulting filehandle, and closes the file. tidy β¨
π« the method $opus->write_to_handle(IOREF, { ...options...} )
writes $opus as a MIDI file to the IO handle you pass a reference to (example: *STDOUT{IO}). the options hash is optional, and whatever you specify as options percolates down to the calls to MIDI::Event::encode β which see.
note: this is probably not what you'd want for sending music to /dev/sequencer, since MIDI files are not MIDI-on-the-wire. different beast entirely bestie π
π the method $opus->draw({ ...options...})
this currently experimental method returns a new GD image object that's a graphic representation of the notes in the given opus. options include:
"width"β the width of the image in pixels (defaults to600)"bgcolor"β a six-digit hex RGB representation of the background color for the image (defaults to$MIDI::Opus::BG_color, currently'000000')"channel_colors"β a reference to a list of colors (in six-digit hex RGB) to use for representing notes on given channels. defaults to@MIDI::Opus::Channel_colors. this list is a list of pairs of colors, such that: the first of a pair (colorN*2) is the color for the first pixel in a note on channelN; and the second (colorN*2 + 1) is the color for the remaining pixels of that note. if you specify only enough colors for channels0toM, notes on channels aboveMwill use 'recycled' colors β they will be plotted with the color for channel"channel_number % M"(where"%"= the MOD operator)
so this means that if you specify
channel_colors => ['00ffff','0000ff']
then all the channels' notes will be plotted with an aqua pixel followed by blue ones; and if you specify
channel_colors => ['00ffff','0000ff', 'ff00ff','ff0000']
then all the even channels' notes will be plotted with an aqua pixel followed by blue ones, and all the odd channels' notes will be plotted with a purple pixel followed by red ones. color theory but make it MIDI π
as to what to do with the object you get back, you probably want something like:
$im = $chachacha->draw;
open(OUT, ">$gif_out"); binmode(OUT);
print OUT $im->gif;
close(OUT);
using this method will cause a "die" if it can't successfully "use GD".
i emphasise that "draw" is experimental, and, in any case, is only meant to be a crude hack. notably, it does not address well some basic problems:
- neither volume nor patch-selection (nor any notable aspects of the patch selected) are represented
- pitch-wheel changes are not represented
- percussion (whether on percussive patches or on channel 10) is not specially represented, as it probably should be
- notes overlapping are not represented at all well
so like. it's a sketch, not a masterpiece. manage expectations π¨
π WHERE'S THE DESTRUCTOR? π
short answer: you don't need one. because MIDI objects (whether opuses or tracks) do not contain any circular data structures, you don't need to explicitly destroy them in order to deallocate their memory. consider this code snippet:
use MIDI;
foreach $one (@ARGV) {
my $opus = MIDI::Opus->new({ 'from_file' => $one, 'no_parse' => 1 });
print "$one has ", scalar( $opus->tracks ) " tracks\n";
}
at the end of each iteration of the foreach loop, the variable $opus goes away, along with its contents, a reference to the opus object. since no other references to it exist (i.e., you didn't do anything like push(@All_opuses,$opus) where @All_opuses is a global), the object is automagically destroyed and its memory marked for recovery. the garbage collector said "not me forgetting about you" β¨
if you wanted to explicitly free up the memory used by a given opus object (and its tracks, if those tracks aren't used anywhere else) without having to wait for it to pass out of scope, just replace it with a new empty object:
$opus = MIDI::Opus->new;
or replace it with anything at all β or even just undef it:
undef $opus;
of course: in the latter case, you can't then use $opus as an opus object anymore, since it isn't one. it's gone, bestie. it's giving ghosted π»
π NOTE ON TICKS π
Okay bestie, gather round, because ticks can go negative and that's a whole thing. The spec says, and I quote: "If division is negative, it represents the division of a second represented by the delta-times in the file,[...]" β so if you wanna roll that way, you'll want syntax that looks like this:
$opus->ticks( ( -25 << 8 ) | 80 );
...which gets you bit resolution (80) at 25 f/s. Slay. β¨
This is tested and it works properly β it produces the right header values in the resulting output file, no cap. BUT β and this is the tea, so sip slowly β at last check, a lot of MIDI synthesizers just do not support this header format. The ones that ghost you: VLC/Fluidsynth and wildmidi. The ones that actually show up for you: Windows Media Player and Timidity++. So like, vibe check your target before you commit, okay? π
π NOTE ON WARN-ING AND DIE-ING π
So here's the situation. If you try to parse a malformed MIDI file (which honestly is not a common thing in my experience), this module β or MIDI::Track, or MIDI::Event β may warn() or die(). (Technically it's carp() or croak(), but bestie, it's all the same in the end.) π
For this reason, you should not use this suite in a case where the script can't warn or die. And yes, that means exactly what you think it means β like, for example, in a CGI that scans for text events in an uploaded MIDI file that may or may not be well-formed. That scenario? Not it. It will not go well.
If that's the kind of task you, or someone you know, may want to do β let me know and I'll consider some kind of 'no_die' parameter in future releases. π«Ά (Or honestly, just trap the die in an eval { } around your call to anything you think could die. Lowkey the move. Main character problem-solving energy.)
π COPYRIGHT π
Copyright (c) 1998-2002 Sean M. Burke. All rights reserved.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
πΈ AUTHORS πΈ
- Sean M. Burke
"sburke@cpan.org"(until 2010) - Darrell Conklin
"conklin@cpan.org"(from 2010)
And that's the whole page, bestie!! π The source/version line at the bottom says perl v5.36.0, dated 2023-10-28, and the page itself is MIDI::Opus(3pm) β so like, that's the vintage, that's the timestamp, that's the whole vibe. Go make some MIDI files, you absolute icon. π
π¦β¨
π¦ where this page comes from
- debian trixie: libmidi-perl 0.84-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libmidi-perl 0.84-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libmidi-perl 0.84-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libmidi-perl 0.84-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms