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 = $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 <kolib...@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
signature.asc
Description: Message signed with OpenPGP