Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = [".", "examples/browser"]
members = [".", "examples/browser/*"]

[package]
name = "midir"
Expand Down Expand Up @@ -54,6 +54,7 @@ parking_lot = { version = "0.12.1" }
[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = "0.3"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [
"Event",
"Navigator",
Expand Down
4 changes: 2 additions & 2 deletions azure-pipelines-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ jobs:
#- bash: cargo build --verbose --target wasm32-unknown-unknown --features "$FEATURES"
# displayName: Build
- bash: |
cd examples/browser
wasm-pack build --target=no-modules --dev
cd examples/browser/read_input_async
wasm-pack build --target=web --dev
displayName: Build WASM example program
- bash: wasm-pack test --chrome
displayName: Run WASM tests
22 changes: 22 additions & 0 deletions examples/browser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# midir browser examples

## Building the examples

From each example directory, use [`wasm-pack`](https://rustwasm.github.io/) to build the WASM artifacts using the [`web`](https://rustwasm.github.io/docs/wasm-bindgen/examples/without-a-bundler.html) target.

```sh
cd read_input_sync
wasm-pack build --target web
```

The output files will placed in a new directory named `./pkg` by default (expected by `index.html`).

## Running the examples

Serve the example directory over HTTP with something like [`serve`](https://github.com/vercel/serve).

```sh
serve .
```

You should now be able to view the example in your browser at `http://localhost:3000`.
18 changes: 0 additions & 18 deletions examples/browser/index.html

This file was deleted.

17 changes: 17 additions & 0 deletions examples/browser/play_async/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "midir_browser_play_async"
version = "0.0.0"
publish = false
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
midir = { "path" = "../../.." }

console_error_panic_hook = "0.1.6"
web-sys = { version = "0.3", features = ["console", "Window"] }
js-sys = "0.3"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
14 changes: 14 additions & 0 deletions examples/browser/play_async/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>midir - examples/browser/play_async</title>
</head>
<body>
<script type="module">
import init from "./pkg/midir_browser_play_async.js";
init();
</script>
</body>
</html>
101 changes: 101 additions & 0 deletions examples/browser/play_async/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use js_sys::{Array, Promise};
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use web_sys::console;

use std::error::Error;

use midir::MidiOutput;

pub fn log(s: String) {
console::log(&Array::of1(&s.into()));
}

macro_rules! println {
() => (log("".to_owned()));
($($arg:tt)*) => (log(format!($($arg)*)));
}

#[wasm_bindgen(start)]
pub async fn start() {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));

run().await.unwrap();
}

async fn run() -> Result<(), Box<dyn Error>> {
let window = web_sys::window().expect("no global `window` exists");

let midi_out = MidiOutput::new_async("midir output").await?;

// Get an input port
let ports = midi_out.ports();
let out_port = match &ports[..] {
[] => {
println!("No ports available yet");
return Ok(());
}
[ref port] => {
println!(
"Choosing the only available output port: {}",
midi_out.port_name(port).unwrap()
);
port
}
_ => {
let mut msg = "Choose an available output port:\n".to_string();
for (i, port) in ports.iter().enumerate() {
msg.push_str(format!("{}: {}\n", i, midi_out.port_name(port).unwrap()).as_str());
}
loop {
if let Ok(Some(port_str)) = window.prompt_with_message_and_default(&msg, "0") {
if let Ok(port_int) = port_str.parse::<usize>() {
if let Some(port) = ports.get(port_int) {
break port;
}
}
}
}
}
};

println!("\nOpening connection");
let mut conn_out = midi_out.connect(out_port, "midir-test")?;
println!("Connection open. Listen!");

const NOTE_ON_MSG: u8 = 0x90;
const NOTE_OFF_MSG: u8 = 0x80;
const VELOCITY: u8 = 0x64;

for (midi_note, duration) in [
(66, 4),
(65, 3),
(63, 1),
(61, 6),
(59, 2),
(58, 4),
(56, 4),
(54, 4),
] {
let _ = conn_out.send(&[NOTE_ON_MSG, midi_note, VELOCITY]);
sleep(duration * 150).await;
let _ = conn_out.send(&[NOTE_OFF_MSG, midi_note, VELOCITY]);
}

println!("\nClosing connection");

// This is optional, the connection would automatically be closed as soon as it goes out of scope
conn_out.close();
println!("Connection closed");
Ok(())
}

async fn sleep(ms: u64) {
let window = web_sys::window().unwrap();
let promise = Promise::new(&mut |resolve, _| {
window
.set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, ms as i32)
.unwrap();
});
JsFuture::from(promise).await.unwrap();
}
17 changes: 17 additions & 0 deletions examples/browser/read_input_async/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "midir_browser_read_input_async"
version = "0.0.0"
publish = false
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
midir = { "path" = "../../.." }

console_error_panic_hook = "0.1.6"
web-sys = { version = "0.3", features = ["console", "Window"] }
js-sys = "0.3"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
14 changes: 14 additions & 0 deletions examples/browser/read_input_async/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>midir - examples/browser/read_input_async</title>
</head>
<body>
<script type="module">
import init from "./pkg/midir_browser_read_input_async.js";
init();
</script>
</body>
</html>
78 changes: 78 additions & 0 deletions examples/browser/read_input_async/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use js_sys::Array;
use wasm_bindgen::prelude::*;
use web_sys::console;

use std::error::Error;

use midir::{Ignore, MidiInput};

pub fn log(s: String) {
console::log(&Array::of1(&s.into()));
}

macro_rules! println {
() => (log("".to_owned()));
($($arg:tt)*) => (log(format!($($arg)*)));
}

#[wasm_bindgen(start)]
pub async fn start() {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));

run().await.unwrap();
}

async fn run() -> Result<(), Box<dyn Error>> {
let window = web_sys::window().expect("no global `window` exists");

let mut midi_in = MidiInput::new_async("midir reading input").await?;
midi_in.ignore(Ignore::None);

// Get an input port
let ports = midi_in.ports();
let in_port = match &ports[..] {
[] => {
println!("No ports available yet");
return Ok(());
}
[ref port] => {
println!(
"Choosing the only available input port: {}",
midi_in.port_name(port).unwrap()
);
port
}
_ => {
let mut msg = "Choose an available input port:\n".to_string();
for (i, port) in ports.iter().enumerate() {
msg.push_str(format!("{}: {}\n", i, midi_in.port_name(port).unwrap()).as_str());
}
loop {
if let Ok(Some(port_str)) = window.prompt_with_message_and_default(&msg, "0") {
if let Ok(port_int) = port_str.parse::<usize>() {
if let Some(port) = ports.get(port_int) {
break port;
}
}
}
}
}
};

println!("Opening connection");
let in_port_name = midi_in.port_name(in_port)?;

// _conn_in needs to be a named parameter, because it needs to be kept alive until the end of the scope
let _conn_in = midi_in.connect(
in_port,
"midir-read-input",
move |stamp, message, _| {
println!("{}: {:?} (len = {})", stamp, message, message.len());
},
(),
)?;

println!("Connection open, reading input from '{}'", in_port_name);
Box::leak(Box::new(_conn_in));
Ok(())
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "midir_browser_example"
name = "midir_browser_read_input_sync"
version = "0.0.0"
publish = false
edition = "2021"
Expand All @@ -8,7 +8,7 @@ edition = "2021"
crate-type = ["cdylib", "rlib"]

[dependencies]
midir = { "path" = "../.." }
midir = { "path" = "../../.." }

console_error_panic_hook = "0.1.6"
web-sys = { version = "0.3", features = ["console", "Window"] }
Expand Down
14 changes: 14 additions & 0 deletions examples/browser/read_input_sync/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>midir - examples/browser/read_input_sync</title>
</head>
<body>
<script type="module">
import init from "./pkg/midir_browser_read_input_sync.js";
init();
</script>
</body>
</html>
8 changes: 8 additions & 0 deletions src/backend/alsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ impl MidiInput {
})
}

pub async fn new_async(client_name: &str) -> Result<Self, InitError> {
Self::new(client_name)
}

pub fn ignore(&mut self, flags: Ignore) {
self.ignore_flags = flags;
}
Expand Down Expand Up @@ -557,6 +561,10 @@ impl MidiOutput {
Ok(MidiOutput { seq: Some(seq) })
}

pub async fn new_async(client_name: &str) -> Result<Self, InitError> {
Self::new(client_name)
}

pub(crate) fn ports_internal(&self) -> Vec<crate::common::MidiOutputPort> {
helpers::get_ports(
self.seq.as_ref().unwrap(),
Expand Down
8 changes: 8 additions & 0 deletions src/backend/coremidi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ impl MidiInput {
}
}

pub async fn new_async(client_name: &str) -> Result<Self, InitError> {
Self::new(client_name)
}

pub(crate) fn ports_internal(&self) -> Vec<crate::common::MidiInputPort> {
Sources
.into_iter()
Expand Down Expand Up @@ -331,6 +335,10 @@ impl MidiOutput {
}
}

pub async fn new_async(client_name: &str) -> Result<Self, InitError> {
Self::new(client_name)
}

pub(crate) fn ports_internal(&self) -> Vec<crate::common::MidiOutputPort> {
Destinations
.into_iter()
Expand Down
Loading