ALSA(3pm)
π ALSA(3pm) π
okay bestie, you're reading the ALSA(3pm) page, contributed by the absolute legends over in the Perl world (User Contributed Perl Documentation, no cap) π and it's a long one, so grab a snack. it's split into parts, so stay with me.
β¨ NAME β¨
MIDI::ALSA β literally the ALSA library, plus some interface functions to go with it. the whole vibe. πΉ
π SYNOPSIS π
here's the main-character example, copied exactly because we do not mess with code:
use MIDI::ALSA(':CONSTS');
MIDI::ALSA::client( 'Perl MIDI::ALSA client', 1, 1, 0 );
MIDI::ALSA::connectfrom( 0, 14, 0 ); # input port is lower (0)
MIDI::ALSA::connectto( 1, 20, 0 ); # output port is higher (1)
while (1) {
my @alsaevent = MIDI::ALSA::input();
if ($alsaevent[0] == SND_SEQ_EVENT_PORT_UNSUBSCRIBED()) { last; }
if ($alsaevent[0] == SND_SEQ_EVENT_NOTEON()) {
my $channel = $alsaevent[7][0];
my $pitch = $alsaevent[7][1];
my $velocity = $alsaevent[7][2];
} elsif ($alsaevent[0] == SND_SEQ_EVENT_CONTROLLER()) {
my $channel = $alsaevent[7][0];
my $controller = $alsaevent[7][4];
my $value = $alsaevent[7][5];
}
MIDI::ALSA::output( @alsaevent );
}
like, look at it. she's working. π
π DESCRIPTION π
so this module gives you a Perl interface to the ALSA library. it's a call-compatible translation into Perl of the Lua module midialsa β http://www.pjb.com.au/comp/lua/midialsa.html β which is itself based on the Python modules alsaseq.py and alsamidi.py by Patricio Paez. it's giving ancestry, it's giving lineage. π
it also offers some functions to translate events from and to the event format used in Sean Burke's MIDI-Perl module. so if you and MIDI-Perl are already besties, you can bring it into the group chat. π«Ά
nothing is exported by default, but all the functions and constants can be exported, like:
use MIDI::ALSA(client, connectfrom, connectto, id, input, output);
use MIDI::ALSA(':CONSTS');
okay important lore: as from version 1.15, note durations are in seconds rather than milliseconds, for consistency with the timestamps. this introduces a backward incompatibility which only affects you if you're putting together your own alsaevents without using the noteevent() function. in the worst case you have to detect versions:
if ($MIDI::ALSA::VERSION < 1.145) { $alsevent[7][4] *= 1000; }
yeah. version check era. it's lowkey giving trauma but we move. π
π₯ FUNCTIONS π₯
here's where the girlies get sorted into their groups:
- functions based on those in
alsaseq.py:client(),connectfrom(),connectto(),disconnectfrom(),disconnectto(),fd(),id(),input(),inputpending(),output(),start(),status(),stop(),syncoutput() - functions based on those in
alsamidi.py:noteevent(),noteonevent(),noteoffevent(),pgmchangeevent(),pitchbendevent(),controllerevent(),chanpress(),sysex() - functions to interface with MIDI-Perl:
alsa2scoreevent(),scoreevent2alsa() - functions to get the current ALSA status:
listclients(),listnumports(),listconnectedto(),listconnectedfrom(),parse_address()
π« client($name, $ninputports, $noutputports, $createqueue) π«
creates an ALSA sequencer client with zero or more input or output ports, and optionally a timing queue. ninputports and noutputports get created if the quantity requested is between 1 and 64 for each. if createqueue = true, it creates a queue for stamping the arrival time of incoming events and scheduling future start times of outgoing events. β°
for full ALSA functionality, the $name should contain only letters, digits, underscores or spaces, and should contain at least one letter. (so my_client 1 is fine, 999_ is not, bestie, keep a letter in there β¨)
unlike in the alsaseq.py Python module, it returns success or failure.
π« connectfrom( $inputport, $src_client, $src_port ) π«
connect from src_client:src_port to inputport. each input port can connect from more than one client. the input() function will receive events from any intput port and any of the clients connected to each of them. events from each client can be distinguised by their source field. π
unlike in the alsaseq.py Python module, it returns success or failure.
since version 1.11, and unlike in the alsaseq.py Python module, if $src_client contains a letter or $src_port is undefined, then parse_address($src_client) automatically gets invoked. this allows you to refer to the clients by name, for example connectfrom($inputport,'Virtual:1') will connect from port 1 of the 'Virtual Raw MIDI' client. iconic behaviour honestly. π¦
π« connectto( $outputport, $dest_client, $dest_port ) π«
connect outputport to dest_client:dest_port. each output port can be Connected to more than one client. events sent to an output port using the output() function will be sent to all clients that are connected to it using this function. the more the merrier, group chat energy. π
unlike in the alsaseq.py Python module, it returns success or failure.
since version 1.11, and unlike in the alsaseq.py Python module, if $dest_client contains a letter or $dest_port is undefined, then parse_address($dest_client) automatically gets invoked. this allows you to refer to the clients by name, for example connectto($outputport,'Virtual:1') will connect to port 1 of the 'Virtual Raw MIDI' client.
π« disconnectfrom( $inputport, $src_client, $src_port ) π«
disconnect the connection from the remote src_client:src_port to my inputport. returns success or failure. sad girl hours, but it's for your own good. π₯²
since version 1.11, and unlike in the alsaseq.py Python module, if $dest_client contains a letter or $dest_port is undefined, then parse_address($src_client) automatically gets invoked. this allows you to refer to the clients by name, for example disconnectfrom($inputport,'Virtual:1') will disconnect from port 1 of the 'Virtual Raw MIDI' client.
π« disconnectto( $outputport, $dest_client, $dest_port ) π«
disconnect the connection from my outputport to the remote dest_client:dest_port. returns success or failure. blocking them, respectfully. π«
since version 1.11, and unlike in the alsaseq.py Python module, if $dest_client contains a letter or $dest_port is undefined, then parse_address($dest_client) automatically gets invoked. this allows you to refer to the clients by name, for example disconnectto($outputport,'Virtual:1') will disconnect to port 1 of the 'Virtual Raw MIDI' client.
π« fd() π«
return fileno of sequencer. the file descriptor. the little handle to the whole operation. π
this piece of code, contributed by Daren Schwenke, uses the AnyEvent module to build an application which waits both for ALSA events, and for user-input:
my $alsa_midi = AnyEvent->io (
fh => MIDI::ALSA::fd(), poll => "r",
cb => sub {
my @alsaevent = MIDI::ALSA::input();
print "Alsa event: " . Dumper(\@alsaevent);
}
);
we love a contributed snippet. we love Daren. π«Ά
π« id() π«
return the client number, or 0 if the client is not yet created. like, they haven't debuted yet. give them a moment. β
π« input() π«
wait for an ALSA event in any of the input ports and return it. ALSA events are returned as an array with 8 elements:
($type, $flags, $tag, $queue, $time, \@source, \@destination, \@data)
unlike in the alsaseq.py Python module, the time element is in floating-point seconds. the last three elements are also arrays:
@source = ( $src_client, $src_port )
@destination = ( $dest_client, $dest_port )
@data = ( varies depending on type )
the source and destination arrays may be useful within an application for handling events differently according to their source or destination. main character energy is knowing where everything came from. β¨
the event-type constants, beginning with SND_SEQ_, are available as module subroutines with empty prototypes, not as strings, and must therefore be used without any dollar-sign, e.g.:
if ($event[0] == MIDI::ALSA::SND_SEQ_EVENT_PORT_UNSUBSCRIBED) { ...
okay no-dollar-sign thing is a classic gotcha, bestie.SND_SEQ_EVENT_PORT_UNSUBSCRIBED()β parentheses, no$. β
the data array is mostly as documented in http://alsa-project.org/alsa-doc/alsa-lib/seq.html.
- for NOTE events, the elements are
( $channel, $pitch, $velocity, unused, $duration )β where since version 1.15 the duration is in floating-point seconds (unlike in thealsaseq.pyPython module where it is in milliseconds). - for SYSEX events, the data array contains just one element: the byte-string, including any
F0andF7bytes. πΈ - for most other events, the elements are
($channel, unused,unused,unused, $param, $value)
the channel element is always 0..15
in the SND_SEQ_EVENT_PITCHBEND event the value element is from -8192..+8191 β not 0..16383. noted. π
if a connection terminates, then input() returns, and the next event will be of type SND_SEQ_EVENT_PORT_UNSUBSCRIBED
heads up, real talk: note that if the event is of typeSND_SEQ_EVENT_PORT_SUBSCRIBEDorSND_SEQ_EVENT_PORT_UNSUBSCRIBED, then that message has come from the System, and itsdest_porttells you which of your ports is involved. but itssrc_clientandsrc_portdo not tell you which other client disconnected β you'll need to uselistconnectedfrom()orlistconnectedto()to see what's happened. π the System is being mysterious again.
π« inputpending() π«
return the number of bytes available in input buffer. use before input() to wait till an event is ready to be read. it's giving "is it my turn yet". π
π« output($type,$flags,$tag,$queue,$time,\@source,\@destination,\@data) π«
send an ALSA-event from an output port. the format of the event is as discussed in input() above. the event will be output immediately either if no queue was created in the client or if the queue parameter is set to SND_SEQ_QUEUE_DIRECT, and otherwise it will be queued and scheduled. β³
the @source is an array with two elements: ($src_client, $src_port), specifying the local output-port from which the event will be sent. if only one output-port exists, all events are sent from it. if two or more exist, the $src_port determines which to use. the smallest available port-number (as created by client()) will be used if $src_port is less than it, and the largest available will be used if $src_port is greater than it.
the @destination is an array with two elements: ($dest_client, $dest_port), specifying the remote client/port to which the event will be sent. if $dest_client is zero (as generated by scoreevent2alsa() or noteevent()), or is the same as the local client (as generated by input()), then the event will be sent to all clients that the local port is connected to (see connectto() and listconnectedto()). but if you set dest_client to a remote client, then the event will be sent to that dest_client:dest_port and nowhere else. no plus-ones. π
it is possible to send an event to a destination to which there is no connection, but it's not usually the right thing to do. normally, you should set up a connection, to allow the underlying RawMIDI ports to remain open while playing β otherwise, ALSA will reset the port after every event. π¨ don't do the cursed thing, bestie, make the connection first.
if the queue buffer is full, output() will wait until space is available to output the event. use status() to know how many events are scheduled in the queue. (it'll just wait. patiently. like a good listener. π«Ά)
if no queue has been started, a SND_SEQ_EVENT_NOTE event can only emerge as a SND_SEQ_EVENT_NOTEON, since a queue is necessary in order to schedule the corresponding NOTEOFF. so like. no queue, no NOTEOFF. that's the rule. π
π« start() π«
start the queue. it is ignored if the client does not have a queue. (she said "if you don't have a queue I simply will not be participating" π)
π« status() π«
return ($status,$time,$events ) of the queue. three things. a little trio. β¨
Statusβ0if it's stopped,1if it's running.Timeβ the current time in seconds.Eventsβ how many output events are scheduled in the queue.
If the client has no queue at all, bestie, you get (0,0,0) back. And heads up β unlike in the alsaseq.py Python module, the time element here is in floating-point seconds. β¨
stop()β stops the queue. If your client doesn't have a queue, this is justβ¦ ignored. No drama, no error, it simply does nothing. π«Άsyncoutput()β waits until the output events have been processed. Like, actually waits. Patient queen behaviour. πnoteevent( $ch, $key, $vel, $start, $duration )β returns an ALSA-event-array, which you then schedule withoutput(). And unlike in thealsaseq.pyPython module, thestartanddurationelements are in floating-point seconds. πΈnoteonevent( $ch, $key, $vel, $start )β ifstartisn't used, the event gets sent directly. Unlike in thealsaseq.pyPython module: ifstartis provided, the event gets scheduled in a queue. And when you do passstart, it's in floating-point seconds. πnoteoffevent( $ch, $key, $vel, $start )β same vibe: nostartmeans the event goes out directly. Unlike in thealsaseq.pyPython module, ifstartis provided the event gets scheduled in a queue. Thestartelement, when it's there, is in floating-point seconds. πpgmchangeevent( $ch, $value, $start )β returns an ALSA-event-array for apatch_changeevent, to be sent byoutput(). Nostart? Sent directly.startprovided? Scheduled in a queue. And unlike in thealsaseq.pyPython module,start(when you give it) is in floating-point seconds. β¨pitchbendevent( $ch, $value, $start )β returns an ALSA-event-array to be sent byoutput(). Thevalueruns from-8192to8191. Nostartmeans it goes out directly; astartmeans it gets scheduled in a queue. Unlike in thealsaseq.pyPython module,startwhen provided is in floating-point seconds. πcontrollerevent( $ch, $controllernum, $value, $start )β returns an ALSA-event-array to be sent byoutput(). Nostartβ sent directly.startβ scheduled in a queue. And unlike in thealsaseq.pyPython module,startwhen provided is in floating-point seconds. πchanpress( $ch, $value, $start )β returns an ALSA-event-array to be sent byoutput(). Nostartβ straight out the door.startβ parked in a queue. Unlike in thealsaseq.pyPython module,startwhen provided is in floating-point seconds. π¦sysex( $ch, $string, $start )β returns an ALSA-event-array to be sent byoutput(). Nostartβ sent directly;startβ scheduled in a queue. Now the important part, bestie:$stringshould start with your Manufacturer ID, but it should not contain any of theF0orF7bytes β those get added automatically for you. And fr fr, the string must not contain any bytes with the top-bit set. πalsa2scoreevent( @alsaevent )β returns an event in the millisecond-tick score-format used by theMIDI.luaandMIDI.pymodules, based on the score-format in Sean Burke's MIDI-Perl CPAN module. Peep this:
http://www.pjb.com.au/comp/lua/MIDI.html#events
And here's the lil trap: since it combines a note_on and a note_off event into one single note event, it returns nil when you call it with the note_on event. So your calling loop must detect nil and, like, not try to index it. The way that would explode π
scoreevent2alsa( @event )β returns an ALSA-event-array to be scheduled in a queue byoutput(). The input is an event in the millisecond-tick score-format used by theMIDI.luaandMIDI.pymodules, based on the score-format in Sean Burke's MIDI-Perl CPAN module. Same link:
http://www.pjb.com.au/comp/lua/MIDI.html#events
For example:
output(scoreevent2alsa('note',4000,1000,0,62,110))
Okay so real talk: some events in a .mid file have no equivalent real-time-midi event (and real-time-midi is the sort ALSA deals in). Those events will make scoreevent2alsa() return undef. So if you're looping through the events in a midi score converting them with scoreevent2alsa(), check that the result isn't undef before you do anything else with it. No cap. β οΈ
listclients()β returns a hash of the numbers and descriptive strings of all ALSA clients:
my %clientnumber2clientname = MIDI::ALSA::listclients();
my %clientname2clientnumber = reverse %clientnumber2clientname;
listnumports()β returns a hash of the client-numbers and how many ports they're running, so if a client is running 4 ports they'll be numbered0..3:
my %clientnumber2howmanyports = MIDI::ALSA::listnumports();
listconnectedto()β returns a list of arrayrefs, each pointing at a three-element array( $outputport, $dest_client, $dest_port )β exactly the shape that might have been passed toconnectto(), or that you could pass todisconnectto(). πlistconnectedfrom()β returns a list of arrayrefs, each pointing at a three-element array( $inputport, $src_client, $src_port )β exactly the shape that might have been passed toconnectfrom(), or that you could pass todisconnectfrom(). π«parse_address( $client_name )β hand it a string and it gives you back a two-integer array( $client_number, $port_number ), the kindconnectto()orconnectfrom()might need. For example, even ifclient()hasn't been called,"24"returns24,0and"25:1"returns25,1. π₯
And if the local client is running, parse_address() also looks up names. So like, if aconnect -oil reveals a timidity client:
client 128: 'TiMidity' [type=user]
then parse_address("TiM") returns 128,0 and parse_address("TiMi:1") returns 128,1, because it finds the first client with a start-of-string, case-sensitive match to the name you gave. And plot twist β parse_address() gets called automatically by connectto(), connectfrom(), disconnectto() and disconnectfrom() whenever you call them with the third argument left undefined. πͺ
Oh and btw parse_address() was introduced in version 1.11 and it is not present in the alsaseq.py Python module. So don't go looking for it there. π
β¨ CONSTANTS β¨
okay so the event-type constants, the ones that all start with SND_SEQ_, are not plain scalars. they're module subroutines with empty prototypes. which means you use them without a dollar sign, like this bestie:
if ($event[0] == MIDI::ALSA::SND_SEQ_EVENT_PORT_UNSUBSCRIBED) { ...
and sometimes they're extra and need an explicit () at the end, like:
MIDI::ALSA::SND_SEQ_EVENT_PORT_UNSUBSCRIBED()
here's the whole squad, no cap:
SND_SEQ_EVENT_BOUNCE SND_SEQ_EVENT_CHANPRESS SND_SEQ_EVENT_CLIENT_CHANGE SND_SEQ_EVENT_CLIENT_EXIT SND_SEQ_EVENT_CLIENT_START SND_SEQ_EVENT_CLOCK SND_SEQ_EVENT_CONTINUE SND_SEQ_EVENT_CONTROL14 SND_SEQ_EVENT_CONTROLLER SND_SEQ_EVENT_ECHO SND_SEQ_EVENT_KEYPRESS SND_SEQ_EVENT_KEYSIGN SND_SEQ_EVENT_NONE SND_SEQ_EVENT_NONREGPARAM SND_SEQ_EVENT_NOTE SND_SEQ_EVENT_NOTEOFF SND_SEQ_EVENT_NOTEON SND_SEQ_EVENT_OSS SND_SEQ_EVENT_PGMCHANGE SND_SEQ_EVENT_PITCHBEND SND_SEQ_EVENT_PORT_CHANGE SND_SEQ_EVENT_PORT_EXIT SND_SEQ_EVENT_PORT_START SND_SEQ_EVENT_PORT_SUBSCRIBED SND_SEQ_EVENT_PORT_UNSUBSCRIBED SND_SEQ_EVENT_QFRAME SND_SEQ_EVENT_QUEUE_SKEW SND_SEQ_EVENT_REGPARAM SND_SEQ_EVENT_RESET SND_SEQ_EVENT_RESULT SND_SEQ_EVENT_SENSING SND_SEQ_EVENT_SETPOS_TICK SND_SEQ_EVENT_SETPOS_TIME SND_SEQ_EVENT_SONGPOS SND_SEQ_EVENT_SONGSEL SND_SEQ_EVENT_START SND_SEQ_EVENT_STOP SND_SEQ_EVENT_SYNC_POS SND_SEQ_EVENT_SYSEX SND_SEQ_EVENT_SYSTEM SND_SEQ_EVENT_TEMPO SND_SEQ_EVENT_TICK SND_SEQ_EVENT_TIMESIGN SND_SEQ_EVENT_TUNE_REQUEST SND_SEQ_EVENT_USR0 SND_SEQ_EVENT_USR1 SND_SEQ_EVENT_USR2 SND_SEQ_EVENT_USR3 SND_SEQ_EVENT_USR4 SND_SEQ_EVENT_USR5 SND_SEQ_EVENT_USR6 SND_SEQ_EVENT_USR7 SND_SEQ_EVENT_USR8 SND_SEQ_EVENT_USR9 SND_SEQ_EVENT_USR_VAR0 SND_SEQ_EVENT_USR_VAR1 SND_SEQ_EVENT_USR_VAR2 SND_SEQ_EVENT_USR_VAR3 SND_SEQ_EVENT_USR_VAR4 SND_SEQ_QUEUE_DIRECT SND_SEQ_TIME_STAMP_REAL VERSION
pls pls pls don't hard-code their numerical values into your programs, that's giving chaos. but sometimes you genuinely wanna peek at MIDI-ALSA data, like with Data::Dumper. so here they are, sorted by number as gleaned from the source:
0 SND_SEQ_EVENT_SYSTEM
1 SND_SEQ_EVENT_RESULT
5 SND_SEQ_EVENT_NOTE
6 SND_SEQ_EVENT_NOTEON
7 SND_SEQ_EVENT_NOTEOFF
8 SND_SEQ_EVENT_KEYPRESS
10 SND_SEQ_EVENT_CONTROLLER
11 SND_SEQ_EVENT_PGMCHANGE
12 SND_SEQ_EVENT_CHANPRESS
13 SND_SEQ_EVENT_PITCHBEND
14 SND_SEQ_EVENT_CONTROL14
15 SND_SEQ_EVENT_NONREGPARAM
16 SND_SEQ_EVENT_REGPARAM
20 SND_SEQ_EVENT_SONGPOS
21 SND_SEQ_EVENT_SONGSEL
22 SND_SEQ_EVENT_QFRAME
23 SND_SEQ_EVENT_TIMESIGN
24 SND_SEQ_EVENT_KEYSIGN
30 SND_SEQ_EVENT_START
31 SND_SEQ_EVENT_CONTINUE
32 SND_SEQ_EVENT_STOP
33 SND_SEQ_EVENT_SETPOS_TICK
34 SND_SEQ_EVENT_SETPOS_TIME
35 SND_SEQ_EVENT_TEMPO
36 SND_SEQ_EVENT_CLOCK
37 SND_SEQ_EVENT_TICK
38 SND_SEQ_EVENT_QUEUE_SKEW
39 SND_SEQ_EVENT_SYNC_POS
40 SND_SEQ_EVENT_TUNE_REQUEST
41 SND_SEQ_EVENT_RESET
42 SND_SEQ_EVENT_SENSING
50 SND_SEQ_EVENT_ECHO
51 SND_SEQ_EVENT_OSS
60 SND_SEQ_EVENT_CLIENT_START
61 SND_SEQ_EVENT_CLIENT_EXIT
62 SND_SEQ_EVENT_CLIENT_CHANGE
63 SND_SEQ_EVENT_PORT_START
64 SND_SEQ_EVENT_PORT_EXIT
65 SND_SEQ_EVENT_PORT_CHANGE
66 SND_SEQ_EVENT_PORT_SUBSCRIBED
67 SND_SEQ_EVENT_PORT_UNSUBSCRIBED
90 SND_SEQ_EVENT_USR0
91 SND_SEQ_EVENT_USR1
92 SND_SEQ_EVENT_USR2
93 SND_SEQ_EVENT_USR3
94 SND_SEQ_EVENT_USR4
95 SND_SEQ_EVENT_USR5
96 SND_SEQ_EVENT_USR6
97 SND_SEQ_EVENT_USR7
98 SND_SEQ_EVENT_USR8
99 SND_SEQ_EVENT_USR9
130 SND_SEQ_EVENT_SYSEX
131 SND_SEQ_EVENT_BOUNCE
135 SND_SEQ_EVENT_USR_VAR0
136 SND_SEQ_EVENT_USR_VAR1
137 SND_SEQ_EVENT_USR_VAR2
138 SND_SEQ_EVENT_USR_VAR3
139 SND_SEQ_EVENT_USR_VAR4
255 SND_SEQ_EVENT_NONE
the MIDI standard says a NOTEON event with velocity=0 means the exact same thing as a NOTEOFF event iconic standardization moment. so a lil subroutine like this is lowkey so convenient:
sub is_noteoff { my @alsaevent = @_;
if ($alsaevent[0] == MIDI::ALSA::SND_SEQ_EVENT_NOTEOFF()) {
return 1;
}
if ($alsaevent[0] == MIDI::ALSA::SND_SEQ_EVENT_NOTEON()
and $alsaevent[7][2] == 0) {
return 1;
}
return 0;
}
and now, the lore π
. since Version 1.20, the output-ports are marked as WRITE, which means they can receive SND_SEQ_EVENT_PORT_SUBSCRIBED or SND_SEQ_EVENT_PORT_UNSUBSCRIBED events from System Announce. up until Version 1.19 β and in the original Python module β output-ports created by client() were not so marked. back in those days, if knowing about connections and disconnections to the output-port actually mattered to you, you had to sit there and listen to all the notifications from System Announce:
MIDI::ALSA::connectfrom(0,'System:1')
which was annoying fr fr, because it alerted you unnecessarily to events that didn't even involve your client, and the connection showed up confusingly in the output of aconnect -oil. not the vibe.
π¦ DOWNLOAD π¦
this Perl version lives on CPAN at http://search.cpan.org/perldoc?MIDI::ALSA
the Lua module is available as a LuaRock at http://luarocks.org/repositories/rocks/index.html#midialsa, so you should be able to install it with the command:
# luarocks install midialsa
π TO DO π
so like, maybe there should be a general connect_between() mechanism, allowing the interconnection of two other clients, a bit like aconnect 32 20. just a thought. someone get on that π
also: ALSA does not transmit Meta-Events like text_event, and there's not much can be done about that. that's just how it is bestie, we move.
π AUTHOR π
Peter J Billam, http://www.pjb.com.au/comp/contact.html
πΈ SEE ALSO πΈ
aconnect -oilhttp://pp.com.mx/python/alsaseqhttp://search.cpan.org/perldoc?MIDI::ALSAhttp://www.pjb.com.au/comp/lua/midialsa.htmlhttp://luarocks.org/repositories/rocks/index.html#midialsahttp://www.pjb.com.au/comp/lua/MIDI.htmlhttp://www.pjb.com.au/comp/lua/MIDI.html#eventshttp://alsa-project.org/alsa-doc/alsa-lib/seq.htmlhttp://alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__ev__note.htmlhttp://alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__ev__ctrl.htmlhttp://alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__ev__queue__control.htmlhttp://alsa-project.org/alsa-doc/alsa-lib/group___seq_client.htmlhttp://alsa-utils.sourcearchive.com/documentation/1.0.20/aconnect_8c-source.htmlhttp://alsa-utils.sourcearchive.com/documentation/1.0.8/aplaymidi_8c-source.htmlsnd_seq_client_info_event_filter_clearsnd_seq_get_any_client_infosnd_seq_get_client_infosnd_seq_client_info_thttp://hackage.haskell.org/package/alsa-seqhttp://search.cpan.org/perldoc?AnyEvent
aaand that's a wrap bestie π this whole thing came from perl v5.42.3, dated 2026-08-19, and the page itself is ALSA(3pm) β go forth and sequence some MIDI, you absolute main character β¨π
π¦ where this page comes from
- ubuntu stonking: libmidi-alsa-perl 1.22-2build2 Β· 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