Re: concurrency of asynchronous events - input from multiple keyboards
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 > 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. 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 > > 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(|$_); > } > } 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 = Audio::PortMIDI.new; # Set up supply to read MIDI events. my $input = supply { my $stream = $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 = $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 signature.asc Description: PGP signature
Re: concurrency of asynchronous events - input from multiple keyboards
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 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 >
Re: concurrency of asynchronous events - input from multiple keyboards
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 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
concurrency of asynchronous events - input from multiple keyboards
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: PGP signature