Navigator.request_midi_access Example

Does anyone know of an example on how to enumerate MIDI inputs using Navigator.request_midi_access and web_sys::MidiAccess?

Thank you

Is using web_sys a hard requirement? I'm playing around with WebAudio myself, and my current attack is:

(1) get example in html/js
(2) get it working in Rust via stdweb / js! macro
(3) rewrite in Rust if necessary

web_sys is not a requirement. I'm looking at putting as much WebMIDI in Rust as possible, as I want to play midi files back accurately using rust threads to track timers, and rust to parse incoming Web MIDI packets and make those available to Javascript.

I'm only a few days into Rust, but am now really curious to see how Navigator.request_midi_access and web_sys::MidiAccess can be implemented to enumerate input ports. Once I understand how that is done ( or perhaps if it is not possible to due in Rust ) than that will give me enough to get started.

Thanks

In my experience, Rust/wasm32 is single threaded, and you'll end up needing to use js callbacks --- unless you're going WebWorkers (I've never used them) route.

  1. Do you have cargo web --auto-reload working?

  2. Do you have working *.js code for what you want to do?

If you have the above, it's a matter of reformatting the js code into a stdweb/js! macro, and then refactor into Rust from there.

I'll have to look into cargo web --auto-reload but do have working .js code working.

So far, I have the following code but my browser does not prompt me to allow access to MIDI devices which it should if request_midi_access was working below:

    let window = web_sys::window().expect("no global `window` exists");
    let navigator: web_sys::Navigator = window.navigator();
    let _req = navigator.request_midi_access();
    let promise = _req.unwrap();
    let future = wasm_bindgen_futures::JsFuture::from(promise);
    let result = future.await;
    let js_value = result.unwrap();
    let midi_access: Result<MidiAccess,JsValue> = js_value.dyn_into();
    log(&format!("{:?}", midi_access.unwrap()));

Thanks,

I'm not familiar with the futures api, but

let result = future.await;

looks weird. Are you missing a () ?

===

Since you have working *.js code, this is how I would solve it:

  1. look at the stdweb/js! amcro at https://docs.rs/stdweb/0.4.20/stdweb/

  2. note that you pass a rust value to js via @{ name_of_rust_var }

  3. paste all the *.js code into a js! { ... } block in Rust

  4. convert line by line from js to Rust, using the @{ ... } notation above to pass a Rust obj to js

====

The nice thing here is that (1) you start with working code, and (2) you get to debug the conversion line by line.

EDIT: If you are using this together with cargo web auto reload, it should recompile + reload on every save, which makes the whole conversion process rather iterative.

When I add .await()? I get the following build error:

88 | #[wasm_bindgen]
| ^^^^^^^^^^^^^^^ cannot use the ? operator in an async function that returns bool

Here is my full fn:

pub async fn my_test() -> bool {

    let window = web_sys::window().expect("no global `window` exists");
    let navigator: web_sys::Navigator = window.navigator();
    let _req = navigator.request_midi_access();
    let promise = _req.unwrap();
    let future = wasm_bindgen_futures::JsFuture::from(promise);

    let result = future.await()?;
   
true

   }

I'm sorry, this exceeds my knowledge of both futures & web_sys.

No, you don't want () on await. It's not a function call.

Ok thanks. I'm getting this error now on compile:

104 | let result = future.await?;
| ^^^^^^^^^^^^^ cannot use the ? operator in an async function that returns bool

I'm so new at Rust I'm not sure what this means yet but will try to wrap the .await? call in a separate function which returns Result or something like that...

If anyone knows how to enumerate the input MIDI ports, it would be a huge help!

Thanks,

The error comes from the fact that you are not returning a Result from your function. The question mark operator basically works by taking the result of a fallible operation and returning the result, but if it failed, it will return early with that error.

So either do something else with the error or make your function return a result. One option is to unwrap it which aborts the program on error.

Thank you, I will try that

I have the following code compiling successfully, but the browser does NOT prompt me to authorize MIDI port access, and the MidiInputMap array I get is an empty array even though there should be 2 midi inputs detected.

Is it possible my Rust code is not running in the web browser's scope and therefore not prompting for MIDI access authorization? I tried with regular JS and it does work ok.

Any help appreciated.

Here is the code I have so far:

let window = web_sys::window().expect("no global window exists");
let navigator: web_sys::Navigator = window.navigator();
let _req = navigator.request_midi_access().unwrap();
let promise = req;
let future = wasm_bindgen_futures::JsFuture::from(promise);
log(&format!("{:?}", future));
let result = future.await.unwrap();
log(&format!("{:?}", result));
let midi_access: Result<MidiAccess,JsValue> = result.dyn_into();
match &midi_access {
Ok(midi) => {
log(&format!("{:?}", &midi.inputs()));
log(&format!("{:?}", js_sys::Object::values(&midi.inputs())));
},
Err(
) => {
log("err");
}
}
log(&format!("{:?}", midi_access));

Great advice n using stdweb to troubleshoot. I ran my code as JS in rust using stdweb and it DID NOT work, so it seems it is not rust related after all! Thanks again.

If you have JS code that:

(1) works on it's own in a HTML file
(2) does NOT work in stdweb/js! macro

it may be because your JS code is being incorrectly tokenized by the stdweb/js! macro.

One possibly useful test is in your index.html (that loads the rust/wasm32), to add a

<script>
function do_stuff_via_js_side() {
  // your already working js code
}
</script>

then to call it from Rust as

js! { do_stuff_via_js_code(); }

If this test works, it hints that stdweb/js! was incorectly tokenizing your js code. If this test fails, it seems that your js code / wasm32 is interacting badly for some reason (no idea why).

Sorry about that -- honest mistake on my part, not familiar with futures, and didn't realize await was not a function call.

1 Like