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 <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
>

Reply via email to