concurrency of asynchronous events - input from multiple keyboards

--5mCyUwZo2JvN/JJP
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

I am working on a small virtual organ program, where I have
multiple MIDI controller keyboards which can be connected to one
or more synthesizer channels to emit various sounds
simultaneously.

At this point, I am able to read events from a single MIDI
controller and send the events to the correct synthesizer channel
(I'm using Timidity at the moment).

I would like to read keypresses from the computer keyboard and
use those to adjust which synthesizers receive which MIDI events.
In other words, I press a note on my MIDI controller and the note
plays a sound on the synthesizer I have set up.  When I press the
letter 'a' on my computer keyboard, I would like to add another
synthesizer, so that subsequent notes played on the MIDI
controller send events to the original synthesizer and to a new
synthesizer.

I was able to build a script to read in events from the computer
keyboard (via the module Term::ReadKey).  I read these keypresses
in a react block with a whenever:

    react {
        whenever key-pressed(:!echo) {
            # Eventually will connect or disconnect a synthesizer for the relevant MIDI controller.
            # Currently just prints out the key that was pressed.
            given .fc {
                when 'q' { done }
                default { .raku.say }
            }
        }
    }

The MIDI events are being read via the module Audio::PortMIDI in
a loop block:

    my $pm = Audio::PortMIDI.new;
    my $stream = $pm.open-input($input-device-number, 32);
    my $voice = $pm.open-output($output-device-number, 32);
    # Read events from the MIDI controller and write to the synthesizer.
    loop {
        if $stream.poll {
            my @notes = $stream.read(1);
            $voice.write(@notes);
        }
    }

I'm struggling to figure out how to combine the react block for
the computer keyboard events and the loop block for the MIDI
events.  I would like to be able to accept events from both
devices concurrently.

It seems like I might need to turn the loop into a Supply of some
kind, but I'm not sure how I would go about doing that.  If I
understand correctly, once it is a supply, I could add it to the
react block as another whenever event.

I have found examples of how to create a Supply using a loop and
a Promise that is kept after a specific amount of time
(https://stackoverflow.com/questions/57486372/concurrency-react-ing-to-more-than-one-supply-at-a-time),
but I have not found anything that is polling from a data stream.

My attempt of polling the stream in a whenever block errors out,
without me ever pressing a key, which makes it seem like it is
trying to read when there are no keypress events available.

    whenever so $stream.poll {
        my @notes = $stream.read(1);
        ...
    }

Type check failed for return value; expected Str but got Any (Any)
  in sub with-termios at /home/kolibrie/.raku/sources/C758559420AEADF99B8D412BDFADA739CAC14C2A (Term::ReadKey) line 20
  in block  at /home/kolibrie/.raku/sources/C758559420AEADF99B8D412BDFADA739CAC14C2A (Term::ReadKey) line 51

Any insights would be greatly appreciated.

-kolibrie

--5mCyUwZo2JvN/JJP
Content-Type: application/pgp-signature; name="signature.asc"

-----BEGIN PGP SIGNATURE-----

iF0EABECAB0WIQQcdVBq2runIfiYui1iVlJyleoqBAUCX+9ouAAKCRBiVlJyleoq
BMv7AKCQEwkstfaxCaYZNb/ZAZASxMx4DACdEdssvoXJ6tuNWPcsEhi/s/KaQHM=
=8jW8
-----END PGP SIGNATURE-----

--5mCyUwZo2JvN/JJP--
0
kolibrie
1/1/2021 6:23:54 PM
perl.perl6.users 1545 articles. 0 followers. Follow

3 Replies
22 Views

Similar Articles

[PageSpeed] 50

--Apple-Mail=_76CE5E84-B7F5-400A-BB93-DC60A2CFD513
Content-Type: multipart/alternative;
	boundary="Apple-Mail=_CFC66AC3-4456-42B1-A6D5-93D0326864C5"


--Apple-Mail=_CFC66AC3-4456-42B1-A6D5-93D0326864C5
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
	charset=us-ascii

As it seems that Audio::PortMIDI lacks non-blocking interface, I think a =
solution would be to read events in a dedicated thread and re-submit =
them into a Supplier. Something like:

my Supplier $midi-events;

start {
    loop {
        my $ev =3D $midi.read;
        $midi-events.emit: $ev;
    }
}


$midi-events can then be used in your react block. BTW, I think calling =
method 'poll' must not be needed because `read` should block until =
actual event is available. At least this is how I understand the normal =
order of things.

Best regards,
Vadim Belman

> On Jan 1, 2021, at 1:23 PM, Nathan Gray <kolibrie@graystudios.org> =
wrote:
>=20
> I am working on a small virtual organ program, where I have
> multiple MIDI controller keyboards which can be connected to one
> or more synthesizer channels to emit various sounds
> simultaneously.
>=20
> At this point, I am able to read events from a single MIDI
> controller and send the events to the correct synthesizer channel
> (I'm using Timidity at the moment).
>=20
> I would like to read keypresses from the computer keyboard and
> use those to adjust which synthesizers receive which MIDI events.
> In other words, I press a note on my MIDI controller and the note
> plays a sound on the synthesizer I have set up.  When I press the
> letter 'a' on my computer keyboard, I would like to add another
> synthesizer, so that subsequent notes played on the MIDI
> controller send events to the original synthesizer and to a new
> synthesizer.
>=20
> I was able to build a script to read in events from the computer
> keyboard (via the module Term::ReadKey).  I read these keypresses
> in a react block with a whenever:
>=20
>    react {
>        whenever key-pressed(:!echo) {
>            # Eventually will connect or disconnect a synthesizer for =
the relevant MIDI controller.
>            # Currently just prints out the key that was pressed.
>            given .fc {
>                when 'q' { done }
>                default { .raku.say }
>            }
>        }
>    }
>=20
> The MIDI events are being read via the module Audio::PortMIDI in
> a loop block:
>=20
>    my $pm =3D Audio::PortMIDI.new;
>    my $stream =3D $pm.open-input($input-device-number, 32);
>    my $voice =3D $pm.open-output($output-device-number, 32);
>    # Read events from the MIDI controller and write to the =
synthesizer.
>    loop {
>        if $stream.poll {
>            my @notes =3D $stream.read(1);
>            $voice.write(@notes);
>        }
>    }
>=20
> I'm struggling to figure out how to combine the react block for
> the computer keyboard events and the loop block for the MIDI
> events.  I would like to be able to accept events from both
> devices concurrently.
>=20
> It seems like I might need to turn the loop into a Supply of some
> kind, but I'm not sure how I would go about doing that.  If I
> understand correctly, once it is a supply, I could add it to the
> react block as another whenever event.
>=20
> I have found examples of how to create a Supply using a loop and
> a Promise that is kept after a specific amount of time
> =
(https://stackoverflow.com/questions/57486372/concurrency-react-ing-to-mor=
e-than-one-supply-at-a-time),
> but I have not found anything that is polling from a data stream.
>=20
> My attempt of polling the stream in a whenever block errors out,
> without me ever pressing a key, which makes it seem like it is
> trying to read when there are no keypress events available.
>=20
>    whenever so $stream.poll {
>        my @notes =3D $stream.read(1);
>        ...
>    }
>=20
> Type check failed for return value; expected Str but got Any (Any)
>  in sub with-termios at =
/home/kolibrie/.raku/sources/C758559420AEADF99B8D412BDFADA739CAC14C2A =
(Term::ReadKey) line 20
>  in block  at =
/home/kolibrie/.raku/sources/C758559420AEADF99B8D412BDFADA739CAC14C2A =
(Term::ReadKey) line 51
>=20
> Any insights would be greatly appreciated.
>=20
> -kolibrie


--Apple-Mail=_CFC66AC3-4456-42B1-A6D5-93D0326864C5
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html;
	charset=us-ascii

<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; =
charset=3Dus-ascii"></head><body style=3D"word-wrap: break-word; =
-webkit-nbsp-mode: space; line-break: after-white-space;" class=3D"">As =
it seems that Audio::PortMIDI lacks non-blocking interface, I think a =
solution would be to read events in a dedicated thread and re-submit =
them into a Supplier. Something like:<div class=3D""><br =
class=3D""></div><div class=3D""><div class=3D""><font face=3D"Andale =
Mono" class=3D""><span style=3D"font-style: normal;" class=3D"">my =
Supplier $midi-events;</span></font></div><div class=3D""><font =
face=3D"Andale Mono" class=3D""><span style=3D"font-style: normal;" =
class=3D""><br class=3D""></span></font></div><div class=3D""><font =
face=3D"Andale Mono" class=3D""><span style=3D"font-style: normal;" =
class=3D"">start {</span></font></div><div class=3D""><font face=3D"Andale=
 Mono" class=3D""><span style=3D"font-style: normal;" class=3D"">&nbsp; =
&nbsp; loop {</span></font></div><div class=3D""><font face=3D"Andale =
Mono" class=3D""><span style=3D"font-style: normal;" class=3D"">&nbsp; =
&nbsp; &nbsp; &nbsp; my $ev =3D $midi.read;</span></font></div><div =
class=3D""><font face=3D"Andale Mono" class=3D""><span =
style=3D"font-style: normal;" class=3D"">&nbsp; &nbsp; &nbsp; &nbsp; =
$midi-events.emit: $ev;</span></font></div><div class=3D""><font =
face=3D"Andale Mono" class=3D""><span style=3D"font-style: normal;" =
class=3D"">&nbsp; &nbsp; }</span></font></div><div class=3D""><font =
face=3D"Andale Mono" class=3D""><span style=3D"font-style: normal;" =
class=3D"">}</span></font></div><div class=3D""><br class=3D""></div><div =
class=3D""><br class=3D""></div><div class=3D"">$midi-events can then be =
used in your react block. BTW, I think calling method 'poll' must not be =
needed because `read` should block until actual event is available. At =
least this is how I understand the normal order of things.</div><div =
class=3D"">&nbsp;</div><div class=3D"">
<div class=3D"">Best regards,</div>Vadim Belman

</div>
<div><br class=3D""><blockquote type=3D"cite" class=3D""><div =
class=3D"">On Jan 1, 2021, at 1:23 PM, Nathan Gray &lt;<a =
href=3D"mailto:kolibrie@graystudios.org" =
class=3D"">kolibrie@graystudios.org</a>&gt; wrote:</div><br =
class=3D"Apple-interchange-newline"><div class=3D""><div class=3D""><div =
class=3D"content-isolator__container">I am working on a small virtual =
organ program, where I have<br class=3D"">multiple MIDI controller =
keyboards which can be connected to one<br class=3D"">or more =
synthesizer channels to emit various sounds<br =
class=3D"">simultaneously.<br class=3D""><br class=3D"">At this point, I =
am able to read events from a single MIDI<br class=3D"">controller and =
send the events to the correct synthesizer channel<br class=3D"">(I'm =
using Timidity at the moment).<br class=3D""><br class=3D"">I would like =
to read keypresses from the computer keyboard and<br class=3D"">use =
those to adjust which synthesizers receive which MIDI events.<br =
class=3D"">In other words, I press a note on my MIDI controller and the =
note<br class=3D"">plays a sound on the synthesizer I have set up. =
&nbsp;When I press the<br class=3D"">letter 'a' on my computer keyboard, =
I would like to add another<br class=3D"">synthesizer, so that =
subsequent notes played on the MIDI<br class=3D"">controller send events =
to the original synthesizer and to a new<br class=3D"">synthesizer.<br =
class=3D""><br class=3D"">I was able to build a script to read in events =
from the computer<br class=3D"">keyboard (via the module Term::ReadKey). =
&nbsp;I read these keypresses<br class=3D"">in a react block with a =
whenever:<br class=3D""><br class=3D""> &nbsp;&nbsp;&nbsp;react {<br =
class=3D""> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;whenever =
key-pressed(:!echo) {<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# =
Eventually will connect or disconnect a synthesizer for the relevant =
MIDI controller.<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# =
Currently just prints out the key that was pressed.<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;given =
..fc {<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n=
bsp;&nbsp;&nbsp;when 'q' { done }<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n=
bsp;&nbsp;&nbsp;default { .raku.say }<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br =
class=3D""> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br class=3D""> =
&nbsp;&nbsp;&nbsp;}<br class=3D""><br class=3D"">The MIDI events are =
being read via the module Audio::PortMIDI in<br class=3D"">a loop =
block:<br class=3D""><br class=3D""> &nbsp;&nbsp;&nbsp;my $pm =3D =
Audio::PortMIDI.new;<br class=3D""> &nbsp;&nbsp;&nbsp;my $stream =3D =
$pm.open-input($input-device-number, 32);<br class=3D""> =
&nbsp;&nbsp;&nbsp;my $voice =3D $pm.open-output($output-device-number, =
32);<br class=3D""> &nbsp;&nbsp;&nbsp;# Read events from the MIDI =
controller and write to the synthesizer.<br class=3D""> =
&nbsp;&nbsp;&nbsp;loop {<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if $stream.poll {<br class=3D"">=
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;my =
@notes =3D $stream.read(1);<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$voice.w=
rite(@notes);<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br class=3D""> =
&nbsp;&nbsp;&nbsp;}<br class=3D""><br class=3D"">I'm struggling to =
figure out how to combine the react block for<br class=3D"">the computer =
keyboard events and the loop block for the MIDI<br class=3D"">events. =
&nbsp;I would like to be able to accept events from both<br =
class=3D"">devices concurrently.<br class=3D""><br class=3D"">It seems =
like I might need to turn the loop into a Supply of some<br =
class=3D"">kind, but I'm not sure how I would go about doing that. =
&nbsp;If I<br class=3D"">understand correctly, once it is a supply, I =
could add it to the<br class=3D"">react block as another whenever =
event.<br class=3D""><br class=3D"">I have found examples of how to =
create a Supply using a loop and<br class=3D"">a Promise that is kept =
after a specific amount of time<br class=3D"">(<a =
href=3D"https://stackoverflow.com/questions/57486372/concurrency-react-ing=
-to-more-than-one-supply-at-a-time" =
class=3D"">https://stackoverflow.com/questions/57486372/concurrency-react-=
ing-to-more-than-one-supply-at-a-time</a>),<br class=3D"">but I have not =
found anything that is polling from a data stream.<br class=3D""><br =
class=3D"">My attempt of polling the stream in a whenever block errors =
out,<br class=3D"">without me ever pressing a key, which makes it seem =
like it is<br class=3D"">trying to read when there are no keypress =
events available.<br class=3D""><br class=3D""> =
&nbsp;&nbsp;&nbsp;whenever so $stream.poll {<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;my @notes =3D =
$stream.read(1);<br class=3D""> =
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br class=3D""> =
&nbsp;&nbsp;&nbsp;}<br class=3D""><br class=3D"">Type check failed for =
return value; expected Str but got Any (Any)<br class=3D""> &nbsp;in sub =
with-termios at =
/home/kolibrie/.raku/sources/C758559420AEADF99B8D412BDFADA739CAC14C2A =
(Term::ReadKey) line 20<br class=3D""> &nbsp;in block &nbsp;at =
/home/kolibrie/.raku/sources/C758559420AEADF99B8D412BDFADA739CAC14C2A =
(Term::ReadKey) line 51<br class=3D""><br class=3D"">Any insights would =
be greatly appreciated.<br class=3D""><br class=3D"">-kolibrie<br =
class=3D""></div></div></div></blockquote></div><br =
class=3D""></div></body></html>=

--Apple-Mail=_CFC66AC3-4456-42B1-A6D5-93D0326864C5--

--Apple-Mail=_76CE5E84-B7F5-400A-BB93-DC60A2CFD513
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename=signature.asc
Content-Type: application/pgp-signature;
	name=signature.asc
Content-Description: Message signed with OpenPGP

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEF1hitHHOemrwyOqx5FPkstg07jMFAl/vg/kACgkQ5FPkstg0
7jN4nA//YksetDhGJ1DbXearfvqU5KFfDh/QVAH3tGSqO7l1VzZcoAObAj+iqcyX
rsP1x172T3DgBoJGJhXCj1QYECnVPDLSIEC33tTAsURS94A0/7+ekk8JGMj6Ka4k
zXzMsbWVZg1UgbPrUZm3pwjDLr1px+QdGpPwMx31dEggG88ZRX6KkQo+LThr9Gqd
zfDG8u2r9se9oEseTZw7aRxvw4n5fLztxocMZ8qUJpX4qGG/sKuu5BT9UznGBcVL
Ihvs1v0vZX3xH5FIxp0oOJImu0wCjFD1xeNJ3ruZxCW2k5hBsuC8DUW6XKioB8Dn
tGa2aoH+LpI7Ie5Gv4QwaCe7TMAxP4pQz2iLv+37spV4+SjVo3ShjlMbi0i2kVnh
Oqe5r3TxBUO/iwfJIdu3ttverE40A/IGczo6lNq6b+IyyX+MKfGcjGFTj1JQqKWj
IuwtSI5zghtKirtuhC5akyEgYl+Pl1FzITXvGqVdP28ndOO5qQuTmKLOq2XEMH5m
BDKtQ/xga6LZF76eYPjt+LJVlqzIPDJp3IAP0q/OlXqhpolATnMHbTFA1MvYAr98
Ai9wj826UdCKiklCrWO2GI6Q8FZo4TTV3PfaR/V2Eu2bh+1xULvDCaCVcNm1ptBI
SRvNuQGWjXba+mdR6e42pGA07ojKkkBrpNzKhyBn5lhMCCN3/Ck=
=GtEW
-----END PGP SIGNATURE-----

--Apple-Mail=_76CE5E84-B7F5-400A-BB93-DC60A2CFD513--
0
vrurg
1/1/2021 8:20:09 PM
--000000000000ee65bf05b7dce956
Content-Type: text/plain; charset="UTF-8"

I think the simplest way to turn that into a Supply is to use the `supply`
keyword

    my $pm = Audio::PortMIDI.new;


    my $input = supply {
        my $stream = $pm.open-input($input-device-number, 32);

        DONE {
            $stream.close;
        }

        loop {
                emit $stream.read(1);
        }
    }


    react {
        whenever key-pressed(:!echo) {
            given .fc {
                when 'q' { done }
                default { .raku.say }
            }
        }

        my $voice = $pm.open-output($output-device-number, 32);
        whenever $input {
            $voice.write(|$_);
        }
    }

On Fri, Jan 1, 2021 at 12:31 PM Nathan Gray <kolibrie@graystudios.org>
wrote:

> I am working on a small virtual organ program, where I have
> multiple MIDI controller keyboards which can be connected to one
> or more synthesizer channels to emit various sounds
> simultaneously.
>
> At this point, I am able to read events from a single MIDI
> controller and send the events to the correct synthesizer channel
> (I'm using Timidity at the moment).
>
> I would like to read keypresses from the computer keyboard and
> use those to adjust which synthesizers receive which MIDI events.
> In other words, I press a note on my MIDI controller and the note
> plays a sound on the synthesizer I have set up.  When I press the
> letter 'a' on my computer keyboard, I would like to add another
> synthesizer, so that subsequent notes played on the MIDI
> controller send events to the original synthesizer and to a new
> synthesizer.
>
> I was able to build a script to read in events from the computer
> keyboard (via the module Term::ReadKey).  I read these keypresses
> in a react block with a whenever:
>
>     react {
>         whenever key-pressed(:!echo) {
>             # Eventually will connect or disconnect a synthesizer for the
> relevant MIDI controller.
>             # Currently just prints out the key that was pressed.
>             given .fc {
>                 when 'q' { done }
>                 default { .raku.say }
>             }
>         }
>     }
>
> The MIDI events are being read via the module Audio::PortMIDI in
> a loop block:
>
>     my $pm = Audio::PortMIDI.new;
>     my $stream = $pm.open-input($input-device-number, 32);
>     my $voice = $pm.open-output($output-device-number, 32);
>     # Read events from the MIDI controller and write to the synthesizer.
>     loop {
>         if $stream.poll {
>             my @notes = $stream.read(1);
>             $voice.write(@notes);
>         }
>     }
>
> I'm struggling to figure out how to combine the react block for
> the computer keyboard events and the loop block for the MIDI
> events.  I would like to be able to accept events from both
> devices concurrently.
>
> It seems like I might need to turn the loop into a Supply of some
> kind, but I'm not sure how I would go about doing that.  If I
> understand correctly, once it is a supply, I could add it to the
> react block as another whenever event.
>
> I have found examples of how to create a Supply using a loop and
> a Promise that is kept after a specific amount of time
> (
> https://stackoverflow.com/questions/57486372/concurrency-react-ing-to-more-than-one-supply-at-a-time
> ),
> but I have not found anything that is polling from a data stream.
>
> My attempt of polling the stream in a whenever block errors out,
> without me ever pressing a key, which makes it seem like it is
> trying to read when there are no keypress events available.
>
>     whenever so $stream.poll {
>         my @notes = $stream.read(1);
>         ...
>     }
>
> Type check failed for return value; expected Str but got Any (Any)
>   in sub with-termios at
> /home/kolibrie/.raku/sources/C758559420AEADF99B8D412BDFADA739CAC14C2A
> (Term::ReadKey) line 20
>   in block  at
> /home/kolibrie/.raku/sources/C758559420AEADF99B8D412BDFADA739CAC14C2A
> (Term::ReadKey) line 51
>
> Any insights would be greatly appreciated.
>
> -kolibrie
>

--000000000000ee65bf05b7dce956
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">I think the simplest way to turn that into a Supply is to =
use the `supply` keyword<div><br>=C2=A0 =C2=A0 my $pm =3D Audio::PortMIDI.n=
ew;<br></div><div><br></div><div><br></div><div>=C2=A0 =C2=A0 my $input =3D=
 supply {<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 my $stream =3D $pm.open-input($inp=
ut-device-number, 32);</div><div><br></div><div>=C2=A0 =C2=A0 =C2=A0 =C2=A0=
 DONE {</div><div>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 $stream.close;<=
/div><div>=C2=A0 =C2=A0 =C2=A0 =C2=A0 }</div><div><br></div><div>=C2=A0 =C2=
=A0 =C2=A0 =C2=A0 loop {<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 emit $stream.read(1);<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 }</div><div=
>=C2=A0 =C2=A0 }</div><div><br></div><div><br>=C2=A0 =C2=A0 react {<br>=C2=
=A0 =C2=A0 =C2=A0 =C2=A0 whenever key-pressed(:!echo) {<br>=C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 given .fc {<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 =C2=A0 when &#39;q&#39; { done }<br>=C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 default { .raku.say }<br>=C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 }<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 }</div><br>=C2=
=A0 =C2=A0 =C2=A0 =C2=A0 my $voice =3D $pm.open-output($output-device-numbe=
r, 32);<div>=C2=A0 =C2=A0 =C2=A0 =C2=A0 whenever $input {</div><div>=C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=C2=A0$voice.write(|$_);</div><div>=C2=A0=
 =C2=A0 =C2=A0 =C2=A0 }<br>=C2=A0 =C2=A0 }<br></div></div><br><div class=3D=
"gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr">On Fri, Jan 1, 2021 at =
12:31 PM Nathan Gray &lt;<a href=3D"mailto:kolibrie@graystudios.org">kolibr=
ie@graystudios.org</a>&gt; wrote:<br></div><blockquote class=3D"gmail_quote=
" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);=
padding-left:1ex">I am working on a small virtual organ program, where I ha=
ve<br>
multiple MIDI controller keyboards which can be connected to one<br>
or more synthesizer channels to emit various sounds<br>
simultaneously.<br>
<br>
At this point, I am able to read events from a single MIDI<br>
controller and send the events to the correct synthesizer channel<br>
(I&#39;m using Timidity at the moment).<br>
<br>
I would like to read keypresses from the computer keyboard and<br>
use those to adjust which synthesizers receive which MIDI events.<br>
In other words, I press a note on my MIDI controller and the note<br>
plays a sound on the synthesizer I have set up.=C2=A0 When I press the<br>
letter &#39;a&#39; on my computer keyboard, I would like to add another<br>
synthesizer, so that subsequent notes played on the MIDI<br>
controller send events to the original synthesizer and to a new<br>
synthesizer.<br>
<br>
I was able to build a script to read in events from the computer<br>
keyboard (via the module Term::ReadKey).=C2=A0 I read these keypresses<br>
in a react block with a whenever:<br>
<br>
=C2=A0 =C2=A0 react {<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 whenever key-pressed(:!echo) {<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 # Eventually will connect or disc=
onnect a synthesizer for the relevant MIDI controller.<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 # Currently just prints out the k=
ey that was pressed.<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 given .fc {<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 when &#39;q&#39; { =
done }<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 default { .raku.say=
 }<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 }<br>
=C2=A0 =C2=A0 }<br>
<br>
The MIDI events are being read via the module Audio::PortMIDI in<br>
a loop block:<br>
<br>
=C2=A0 =C2=A0 my $pm =3D Audio::PortMIDI.new;<br>
=C2=A0 =C2=A0 my $stream =3D $pm.open-input($input-device-number, 32);<br>
=C2=A0 =C2=A0 my $voice =3D $pm.open-output($output-device-number, 32);<br>
=C2=A0 =C2=A0 # Read events from the MIDI controller and write to the synth=
esizer.<br>
=C2=A0 =C2=A0 loop {<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 if $stream.poll {<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 my @notes =3D $stream.read(1);<br=
>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 $voice.write(@notes);<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 }<br>
=C2=A0 =C2=A0 }<br>
<br>
I&#39;m struggling to figure out how to combine the react block for<br>
the computer keyboard events and the loop block for the MIDI<br>
events.=C2=A0 I would like to be able to accept events from both<br>
devices concurrently.<br>
<br>
It seems like I might need to turn the loop into a Supply of some<br>
kind, but I&#39;m not sure how I would go about doing that.=C2=A0 If I<br>
understand correctly, once it is a supply, I could add it to the<br>
react block as another whenever event.<br>
<br>
I have found examples of how to create a Supply using a loop and<br>
a Promise that is kept after a specific amount of time<br>
(<a href=3D"https://stackoverflow.com/questions/57486372/concurrency-react-=
ing-to-more-than-one-supply-at-a-time" rel=3D"noreferrer" target=3D"_blank"=
>https://stackoverflow.com/questions/57486372/concurrency-react-ing-to-more=
-than-one-supply-at-a-time</a>),<br>
but I have not found anything that is polling from a data stream.<br>
<br>
My attempt of polling the stream in a whenever block errors out,<br>
without me ever pressing a key, which makes it seem like it is<br>
trying to read when there are no keypress events available.<br>
<br>
=C2=A0 =C2=A0 whenever so $stream.poll {<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 my @notes =3D $stream.read(1);<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 ...<br>
=C2=A0 =C2=A0 }<br>
<br>
Type check failed for return value; expected Str but got Any (Any)<br>
=C2=A0 in sub with-termios at /home/kolibrie/.raku/sources/C758559420AEADF9=
9B8D412BDFADA739CAC14C2A (Term::ReadKey) line 20<br>
=C2=A0 in block=C2=A0 at /home/kolibrie/.raku/sources/C758559420AEADF99B8D4=
12BDFADA739CAC14C2A (Term::ReadKey) line 51<br>
<br>
Any insights would be greatly appreciated.<br>
<br>
-kolibrie<br>
</blockquote></div>

--000000000000ee65bf05b7dce956--
0
b2gills
1/1/2021 8:51:57 PM
--EuxKj2iCbKjpUGkD
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

On Fri, Jan 01, 2021 at 03:20:09PM -0500, Vadim Belman wrote:
> As it seems that Audio::PortMIDI lacks non-blocking interface, I think a =
solution would be to read events in a dedicated thread and re-submit them i=
nto a Supplier. Something like:
>=20
> my Supplier $midi-events;
>=20
> start {
>     loop {
>         my $ev =3D $midi.read;
>         $midi-events.emit: $ev;
>     }
> }
>=20
>=20
> $midi-events can then be used in your react block. BTW, I think calling m=
ethod 'poll' must not be needed because `read` should block until actual ev=
ent is available. At least this is how I understand the normal order of thi=
ngs.

Vadim,

Thank you for your reply.  I tried out not calling $midi.poll,
but found that $midi.read would return undef events (rather than
blocking until it received an event).  So I added it back in.
Thank you for the example of how to set up a supply.


On Fri, Jan 01, 2021 at 02:51:57PM -0600, Brad Gilbert wrote:
> I think the simplest way to turn that into a Supply is to use the `supply`
> keyword
>=20
>     my $pm =3D Audio::PortMIDI.new;
>=20
>=20
>     my $input =3D supply {
>         my $stream =3D $pm.open-input($input-device-number, 32);
>=20
>         DONE {
>             $stream.close;
>         }
>=20
>         loop {
>                 emit $stream.read(1);
>         }
>     }
>=20
>=20
>     react {
>         whenever key-pressed(:!echo) {
>             given .fc {
>                 when 'q' { done }
>                 default { .raku.say }
>             }
>         }
>=20
>         my $voice =3D $pm.open-output($output-device-number, 32);
>         whenever $input {
>             $voice.write(|$_);
>         }
>     }

Brad,

Thank you for your reply.  I was able to get my code working
thanks to the examples from you and Vadim.

Here is what I ended up with:

    my $pm =3D Audio::PortMIDI.new;

    # Set up supply to read MIDI events.
    my $input =3D supply {
        my $stream =3D $pm.open-input($input-device-number, 32);

        LAST {
            $stream.close;
        }

        loop {
            emit $stream.read(1) if $stream.poll;
        }
    }

    # Set up handle to send MIDI events to the synthesizer.
    my $voice =3D $pm.open-output($output-device-number, 32);

    react {
        # Read key presses from the computer keyboard.
        # TODO: change $voice as needed based on computer keyboard input.
        whenever key-pressed(:!echo) {
            given .fc {
                when 'q' { done }
                default { .raku.say }
            }
        }

        # Read MIDI events and send to the synthesizer.
        whenever $input {
            $voice.write(|$_);
        }
    }

-kolibrie

--EuxKj2iCbKjpUGkD
Content-Type: application/pgp-signature; name="signature.asc"

-----BEGIN PGP SIGNATURE-----

iF0EABECAB0WIQQcdVBq2runIfiYui1iVlJyleoqBAUCX/C+qQAKCRBiVlJyleoq
BDE3AKCAX9jytqo8FJw2p49Uo9Q5v4ic/gCeLHwk9i1iK00CoLEbh2jSizqA0fo=
=hQdM
-----END PGP SIGNATURE-----

--EuxKj2iCbKjpUGkD--
0
kolibrie
1/2/2021 6:42:52 PM
Reply: