From 922fa6b649fac4e2f983186a1a2dbd77847b938c Mon Sep 17 00:00:00 2001 From: pommicket Date: Tue, 27 Sep 2022 16:40:34 -0400 Subject: midi input --- .gitignore | 1 + Cargo.lock | 828 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 8 + rustfmt.toml | 1 + src/main.rs | 94 +++++++ src/midi_input.rs | 372 ++++++++++++++++++++++++ 6 files changed, 1304 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 rustfmt.toml create mode 100644 src/main.rs create mode 100644 src/midi_input.rs 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::() { + 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, + pub default: usize +} + + +impl IntoIterator for DeviceList { + type Item = DeviceInfo; + type IntoIter = std::vec::IntoIter; + + 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 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 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. 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 { + 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 = 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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); + } + } +} -- cgit v1.2.3