develooper Front page | perl.perl6.users | Postings from January 2021

concurrency of asynchronous events - input from multiple keyboards

Thread Next
From:
Nathan Gray
Date:
January 1, 2021 18:31
Subject:
concurrency of asynchronous events - input from multiple keyboards
Message ID:
20210101182354.GA31651@vs1.graystudios.org
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

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About