summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock828
-rw-r--r--Cargo.toml8
-rw-r--r--rustfmt.toml1
-rw-r--r--src/main.rs94
-rw-r--r--src/midi_input.rs372
6 files changed, 1304 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..e9c5fb2
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,828 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "alsa"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b"
+dependencies = [
+ "alsa-sys",
+ "bitflags",
+ "libc",
+ "nix",
+]
+
+[[package]]
+name = "alsa-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bindgen"
+version = "0.59.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "lazy_static",
+ "lazycell",
+ "peeking_take_while",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bumpalo"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
+
+[[package]]
+name = "bytes"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+
+[[package]]
+name = "cc"
+version = "1.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+dependencies = [
+ "jobserver",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clang-sys"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
+[[package]]
+name = "combine"
+version = "4.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "coreaudio-rs"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88"
+dependencies = [
+ "bitflags",
+ "coreaudio-sys",
+]
+
+[[package]]
+name = "coreaudio-sys"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dff444d80630d7073077d38d40b4501fd518bd2b922c2a55edcc8b0f7be57e6"
+dependencies = [
+ "bindgen",
+]
+
+[[package]]
+name = "cpal"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d466b47cf0ea4100186a7c12d7d0166813dda7cf648553554c9c39c6324841b"
+dependencies = [
+ "alsa",
+ "core-foundation-sys",
+ "coreaudio-rs",
+ "jni",
+ "js-sys",
+ "libc",
+ "mach",
+ "ndk 0.7.0",
+ "ndk-context",
+ "nix",
+ "oboe",
+ "once_cell",
+ "parking_lot",
+ "stdweb",
+ "thiserror",
+ "web-sys",
+ "windows",
+]
+
+[[package]]
+name = "cty"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
+
+[[package]]
+name = "glob"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+
+[[package]]
+name = "jni"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
+dependencies = [
+ "cesu8",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
+[[package]]
+name = "libc"
+version = "0.2.133"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
+
+[[package]]
+name = "libloading"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "mach"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "ndk"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4"
+dependencies = [
+ "bitflags",
+ "jni-sys",
+ "ndk-sys 0.3.0",
+ "num_enum",
+ "thiserror",
+]
+
+[[package]]
+name = "ndk"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"
+dependencies = [
+ "bitflags",
+ "jni-sys",
+ "ndk-sys 0.4.0",
+ "num_enum",
+ "raw-window-handle",
+ "thiserror",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "ndk-sys"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21d83ec9c63ec5bf950200a8e508bdad6659972187b625469f58ef8c08e29046"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "nix"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if",
+ "libc",
+ "memoffset",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "oboe"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27f63c358b4fa0fbcfefd7c8be5cfc39c08ce2389f5325687e7762a48d30a5c1"
+dependencies = [
+ "jni",
+ "ndk 0.6.0",
+ "ndk-context",
+ "num-derive",
+ "num-traits",
+ "oboe-sys",
+]
+
+[[package]]
+name = "oboe-sys"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3370abb7372ed744232c12954d920d1a40f1c4686de9e79e800021ef492294bd"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys",
+]
+
+[[package]]
+name = "peeking_take_while"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
+
+[[package]]
+name = "playmidi"
+version = "0.0.0"
+dependencies = [
+ "cpal",
+ "libc",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
+dependencies = [
+ "once_cell",
+ "thiserror",
+ "toml",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a"
+dependencies = [
+ "cty",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+dependencies = [
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "serde"
+version = "1.0.145"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
+
+[[package]]
+name = "shlex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
+
+[[package]]
+name = "smallvec"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
+
+[[package]]
+name = "stdweb"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
+
+[[package]]
+name = "syn"
+version = "1.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
+
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+
+[[package]]
+name = "web-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647"
+dependencies = [
+ "windows_aarch64_msvc 0.37.0",
+ "windows_i686_gnu 0.37.0",
+ "windows_i686_msvc 0.37.0",
+ "windows_x86_64_gnu 0.37.0",
+ "windows_x86_64_msvc 0.37.0",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+dependencies = [
+ "windows_aarch64_msvc 0.36.1",
+ "windows_i686_gnu 0.36.1",
+ "windows_i686_msvc 0.36.1",
+ "windows_x86_64_gnu 0.36.1",
+ "windows_x86_64_msvc 0.36.1",
+]
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..829d247
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "playmidi"
+version = "0.0.0"
+edition = "2021"
+
+[dependencies]
+cpal = "0.14"
+libc = "0.2"
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644
index 0000000..218e203
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+hard_tabs = true
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..3656f48
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,94 @@
+extern crate cpal;
+
+use std::io::Write;
+
+//use cpal::traits::{HostTrait, DeviceTrait, StreamTrait};
+
+mod midi_input;
+
+fn main() {
+ let mut device_mgr = midi_input::DeviceManager::new().expect("Couldn't create device manager");
+ device_mgr.set_quiet(true);
+ let devices = device_mgr.list().expect("couldn't list MIDI devices");
+ let mut index = 0;
+ for device in &devices {
+ print!("{:3} | ", index + 1);
+ let mut first = true;
+ for line in device.name.lines() {
+ if !first {
+ print!(" | ");
+ }
+ println!("{}", line);
+ first = false;
+ }
+ println!(" -----------------");
+ index += 1;
+ }
+ print!("Select a device (default {}): ", devices.default + 1);
+ match std::io::stdout().flush() {
+ Err(_) => {} // whatever
+ _ => {}
+ }
+
+ let device_id;
+ {
+ let mut buf = String::new();
+ std::io::stdin()
+ .read_line(&mut buf)
+ .expect("error reading stdin");
+ let s = buf.trim();
+ if s.len() == 0 {
+ device_id = &devices[devices.default].id;
+ } else {
+ match s.parse::<usize>() {
+ Ok(idx) if idx >= 1 && idx <= devices.len() => {
+ device_id = &devices[idx - 1].id;
+ }
+ _ => {
+ eprintln!("Bad device ID: {}", s);
+ return;
+ }
+ }
+ }
+ }
+ let mut device = device_mgr
+ .open(&device_id)
+ .expect("error opening MIDI device");
+
+ loop {
+ let byte = device.read_byte();
+ if let Some(x) = byte {
+ println!("{}", x);
+ } else {
+ println!("nothing");
+ std::thread::sleep(std::time::Duration::from_millis(10));
+ }
+ }
+
+ /*
+ let host = cpal::default_host();
+ let device = host.default_output_device().expect("no output device available");
+ let mut supported_configs_range = device.supported_output_configs()
+ .expect("error while querying configs");
+ let config = supported_configs_range.next()
+ .expect("no supported config?!")
+ .with_max_sample_rate()
+ .into();
+ let stream = device.build_output_stream(
+ &config,
+ move |data: &mut [i16], _: &cpal::OutputCallbackInfo| {
+ for sample in data.iter_mut() {
+ *sample = 0;
+ }
+ },
+ move |err| {
+ println!("audio stream error: {}", err);
+ },
+ ).expect("couldn't build output stream");
+ stream.play().expect("couldn't play stream");
+
+ loop {
+ std::thread::sleep(std::time::Duration::from_millis(100));
+ }
+ */
+}
diff --git a/src/midi_input.rs b/src/midi_input.rs
new file mode 100644
index 0000000..cdf5b87
--- /dev/null
+++ b/src/midi_input.rs
@@ -0,0 +1,372 @@
+/// Query and read from MIDI input devices.
+/// Don't assume these functions are thread-safe.
+/// You should only need to call them in the main thread anyways.
+/// Basic usage:
+/// ```
+/// let mut manager = DeviceManager::new().unwrap();
+/// let mut devices = manager.list();
+/// while devices.len() == 0 {
+/// std::thread::sleep(std::time::Duration::from_millis(100));
+/// devices = manager.list();
+/// }
+/// let mut i = devices.default;
+/// for device in devices {
+/// println!("{}",device.name);
+/// }
+/// let device = manager.open(&devices[i].id);
+/// loop {
+/// if let Some(byte) = device.read_byte() {
+/// ...
+/// } else {
+/// std::thread::sleep(std::time::Duration::from_millis(100));
+/// }
+/// }
+/// ```
+use std::ffi::{c_char, c_int, c_void, CStr, CString};
+// so much stuff depends on libc
+// it's not such a big deal to add it as a dependency
+// (we only really need it for size_t, ssize_t)
+extern crate libc;
+use libc::{size_t, ssize_t, free};
+
+
+use std::sync::Mutex;
+
+// (snd_rawmidi_t is an opaque struct)
+type SndRawMidiT = c_void;
+
+/// Opaque device manager type.
+pub struct DeviceManager {}
+
+/// Opaque type for device ID.
+#[derive(Debug, Clone)]
+pub struct DeviceID {
+ name: String,
+}
+
+/// Information about a device.
+pub struct DeviceInfo {
+ /// A human-readable name for the device.
+ /// This may contain newlines. You can just use the first line
+ /// if you want a quick description.
+ pub name: String,
+ /// The id, to be used with `open_device`.
+ pub id: DeviceID,
+}
+
+/// Opaque type for MIDI input device.
+#[derive(Debug)]
+pub struct Device {
+ rawmidi: *mut SndRawMidiT,
+ error: i32
+}
+
+/// An error produced by opening a device.
+#[derive(Debug)]
+pub enum DeviceOpenError {
+ /// the device was not found.
+ /// this can hapen if it was disconnected between calling `list_devices`
+ /// and `open_device`.
+ NotFound(String),
+ /// other error
+ Other(String),
+}
+
+pub struct DeviceList {
+ pub devices: Vec<DeviceInfo>,
+ pub default: usize
+}
+
+
+impl IntoIterator for DeviceList {
+ type Item = DeviceInfo;
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.devices.into_iter()
+ }
+}
+
+impl<'a> IntoIterator for &'a DeviceList {
+ type Item = &'a DeviceInfo;
+ type IntoIter = std::slice::Iter<'a, DeviceInfo>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ (&self.devices).into_iter()
+ }
+}
+
+impl DeviceList {
+ pub fn len(&self) -> usize {
+ self.devices.len()
+ }
+}
+
+
+impl std::ops::Index<usize> for DeviceList {
+ type Output = DeviceInfo;
+
+ fn index(&self, i: usize) -> &Self::Output {
+ &self.devices[i]
+ }
+}
+
+#[link(name = "asound", kind = "dylib")]
+extern "C" {
+ fn snd_device_name_hint(
+ card: c_int,
+ devname: *const c_char,
+ hints: *mut *mut *mut c_void,
+ ) -> c_int;
+ fn snd_device_name_get_hint(hint: *const c_void, id: *const c_char) -> *mut c_char;
+ fn snd_device_name_free_hint(hints: *mut *mut c_void) -> c_int;
+ fn snd_rawmidi_open(
+ inputp: *mut *mut SndRawMidiT,
+ outputp: *mut *mut SndRawMidiT,
+ name: *const c_char,
+ mode: c_int,
+ ) -> c_int;
+ fn snd_rawmidi_nonblock(rawmidi: *mut SndRawMidiT, nonblock: c_int) -> c_int;
+ fn snd_rawmidi_read(rawmidi: *mut SndRawMidiT, buffer: *mut c_void, size: size_t) -> ssize_t;
+ fn snd_rawmidi_close(rawmidi: *mut SndRawMidiT) -> c_int;
+ fn snd_lib_error_set_handler(handler: *mut c_void) -> c_int;
+}
+
+impl Into<String> for DeviceOpenError {
+ fn into(self) -> String {
+ use DeviceOpenError::*;
+ match self {
+ NotFound(s) => format!("device not found: {}", s),
+ Other(s) => s.clone(),
+ }
+ }
+}
+
+// technically there should be varargs here but oh well
+pub unsafe extern "C" fn snd_lib_error_handler_quiet(
+ _file: *const c_char,
+ _line: i32,
+ _function: *const c_char,
+ _err: i32,
+) {
+}
+
+// convert cstr to Option<String>. you'll get back None if
+// either cstr is null, or invalid UTF-8.
+unsafe fn cstr_to_option_string(cstr: *const c_char) -> Option<String> {
+ if cstr == 0 as _ {
+ return None;
+ }
+
+ let result = CStr::from_ptr(cstr).to_str();
+ match result {
+ Ok(s) => Some(s.to_string()),
+ Err(_) => None,
+ }
+}
+
+static DEVICE_MANAGER_EXISTS: Mutex<bool> = Mutex::new(false);
+
+impl DeviceManager {
+ /// Create a new manager for MIDI input devices.
+ /// Only ONE device manager can exist at any point in time.
+ pub fn new() -> Result<DeviceManager, String> {
+ let mut r = DEVICE_MANAGER_EXISTS
+ .lock()
+ .map_err(|_| "Failed to lock mutex".to_string())?;
+ if *r {
+ return Err("A device manager already exists.".to_string());
+ }
+ *r = true;
+ Ok(DeviceManager {})
+ }
+
+ /// Pass `true` to stop ALSA from outputting errors to `stderr`
+ /// (no effect on Windows).
+ pub fn set_quiet(&mut self, quiet: bool) {
+ let mut callback: *mut c_void = 0 as _;
+
+ if quiet {
+ callback = snd_lib_error_handler_quiet as _;
+ }
+ unsafe {
+ snd_lib_error_set_handler(callback);
+ }
+ }
+
+ /// Returns a `DeviceList` containing descriptions for all MIDI input devices.
+ pub fn list(&self) -> Result<DeviceList, String> {
+ let mut hints: *mut *mut c_void = 0 as _;
+ let err: c_int;
+ unsafe {
+ err =
+ snd_device_name_hint(-1, "rawmidi\0".as_ptr() as *const c_char, (&mut hints) as _);
+ }
+
+ // in theory hints should never be null if err != 0.
+ // but you can never be sure.
+ if err != 0 || hints == 0 as _ {
+ return Err(format!("failed to get device hints (error code {})", err));
+ }
+
+ let mut idx: usize = 0;
+ let mut devices = vec![];
+ let mut default = None;
+
+ loop {
+ let hint;
+ unsafe {
+ hint = *hints.offset(idx as isize);
+ }
+ if hint.is_null() {
+ break;
+ }
+
+ let name;
+ let desc;
+
+ unsafe {
+ let name_cstr = snd_device_name_get_hint(hint, "NAME\0".as_ptr() as _);
+ let desc_cstr = snd_device_name_get_hint(hint, "DESC\0".as_ptr() as _);
+ //let ioid_cstr = snd_device_name_get_hint(hint, "IOID\0".as_ptr() as _);
+
+ name = cstr_to_option_string(name_cstr);
+ desc = cstr_to_option_string(desc_cstr);
+
+ free(name_cstr as _);
+ free(desc_cstr as _);
+ }
+
+ // we need the name to be able to do anything with the device
+ if let Some(name_unwrapped) = name {
+ let has_desc = desc.is_some();
+ let desc_unwrapped = desc.unwrap_or("(no description)".to_string());
+ let desc_str = format!("{}\n{}", name_unwrapped, desc_unwrapped);
+ let info = DeviceInfo {
+ id: DeviceID {
+ name: name_unwrapped,
+ },
+ name: desc_str,
+ };
+
+ if has_desc && default.is_none() {
+ default = Some(idx);
+ }
+
+ devices.push(info);
+ }
+
+ idx += 1;
+ }
+
+ unsafe {
+ snd_device_name_free_hint(hints);
+ }
+
+ Ok(DeviceList {
+ devices,
+ default: default.unwrap_or(0)
+ })
+ }
+
+ /// Open a device.
+ pub fn open(&self, id: &DeviceID) -> Result<Device, DeviceOpenError> {
+ let name_cstr = CString::new(&id.name[..]).map_err(|_| {
+ // (name has null bytes)
+ // this should never happen since `name` should
+ // have been constructed from a cstr.
+ // but it could happen if someone uses unsafe or something.
+ DeviceOpenError::Other("invalid device ID".to_string())
+ })?;
+ let mut input: *mut SndRawMidiT = 0 as _;
+ let mut err;
+ unsafe {
+ err = snd_rawmidi_open((&mut input) as _, 0 as _, name_cstr.as_ptr(), 0);
+ if err == 0 && !input.is_null() {
+ err = snd_rawmidi_nonblock(input, 1);
+ }
+ }
+ if err != 0 || input.is_null() {
+ if err == -2 {
+ return Err(DeviceOpenError::NotFound(id.name.clone()));
+ }
+ return Err(DeviceOpenError::Other(format!(
+ "other error (code {})",
+ err
+ )));
+ }
+ Ok(Device { rawmidi: input, error: 0 })
+ }
+}
+
+
+impl Device {
+ fn read_raw(&mut self, buffer: &mut [u8]) -> usize {
+ let n;
+ unsafe {
+ let pbuffer = &mut buffer[0] as *mut u8 as *mut c_void;
+ n = snd_rawmidi_read(self.rawmidi, pbuffer, buffer.len() as _);
+ }
+ if n < 0 {
+ self.error = n as _;
+ 0
+ } else if (n as usize) <= buffer.len() {
+ n as usize
+ } else {
+ // this shouldn't happen
+ // something messed up
+ self.error = -1000;
+ 0
+ }
+ }
+
+ /// Read a single byte of MIDI input, if there is any.
+ /// It's probably fine to use this instead of `read()` in most cases,
+ /// since you probably aren't getting megabytes of MIDI data per second or anything.
+ /// For simplicity, this function just returns None if a read error occurs.
+ /// Check `get_error()` if you really care.
+ #[allow(dead_code)]
+ pub fn read_byte(&mut self) -> Option<u8> {
+ let mut buffer = [0u8; 1];
+ let n = self.read_raw(&mut buffer);
+ if n == 1 {
+ Some(buffer[0])
+ } else {
+ None
+ }
+ }
+
+ /// get the device error if there is one
+ #[allow(dead_code)]
+ pub fn get_error(&self) -> Option<String> {
+ if self.error == 0 {
+ return None;
+ }
+ Some(format!("ALSA error code {}", self.error))
+ }
+
+ /// read as many bytes of MIDI input as are available,
+ /// up to a reasonable limit.
+ #[allow(dead_code)]
+ pub fn read(&mut self) -> Vec<u8> {
+ let mut buffer = [0u8; 1024];
+ let n = self.read_raw(&mut buffer);
+ buffer[..n].to_vec()
+
+ }
+}
+
+impl Drop for DeviceManager {
+ fn drop(&mut self) {
+ *DEVICE_MANAGER_EXISTS.lock().unwrap() = false;
+ }
+}
+
+impl Drop for Device {
+ fn drop(&mut self) {
+ unsafe {
+ // there's not much we can do if this fails.
+ snd_rawmidi_close(self.rawmidi);
+ }
+ }
+}