auto-musik

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | LICENSE

commit 5473b2d9a09952a3c2d05e93433322ece555c157
Author: minerva-jupiter <ryouturn@gmail.com>
Date:   Fri,  7 Nov 2025 21:59:41 +0900

init commit

Diffstat:
A.env | 1+
A.gitignore | 1+
ACargo.lock | 508+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ACargo.toml | 8++++++++
Asrc/data.rs | 38++++++++++++++++++++++++++++++++++++++
Asrc/generator.rs | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main.rs | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/midi_io.rs | 45+++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 818 insertions(+), 0 deletions(-)

diff --git a/.env b/.env @@ -0,0 +1 @@ +GEMINI_API_KEY=AIzaSyDiW3UJYq8zgpzJxzOPQ35hXoO3RRfrj8s diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock @@ -0,0 +1,508 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "alsa" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" +dependencies = [ + "alsa-sys", + "bitflags 2.10.0", + "cfg-if", + "libc", +] + +[[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 = "auto-musik" +version = "0.1.0" +dependencies = [ + "midir", + "rand", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "coremidi" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964eb3e10ea8b0d29c797086aab3ca730f75e06dced0cb980642fd274a5cca30" +dependencies = [ + "block", + "core-foundation", + "core-foundation-sys", + "coremidi-sys", +] + +[[package]] +name = "coremidi-sys" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9504310988d938e49fff1b5f1e56e3dafe39bb1bae580c19660b58b83a191e" +dependencies = [ + "core-foundation-sys", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "js-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "midir" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73f8737248ad37b88291a2108d9df5f991dc8555103597d586b5a29d4d703c0" +dependencies = [ + "alsa", + "bitflags 1.3.2", + "coremidi", + "js-sys", + "libc", + "parking_lot", + "wasm-bindgen", + "web-sys", + "windows", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "auto-musik" +version = "0.1.0" +edition = "2024" + +[dependencies] +midir = "0.10.3" +rand = "0.9.2" diff --git a/src/data.rs b/src/data.rs @@ -0,0 +1,38 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum NoteEvent { + // Note On: + NoteOn { note: u8, velocity: u8 }, + // Note Off: + NoteOff { note: u8 }, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Bar { + pub beat: Beat, + pub tonality: Tonality, + pub chord: Chord, + pub events: Vec<(u64, NoteEvent)>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Beat { + TwoFourth, + ThreeFourth, + FourFourth, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Tonality { + CM, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Chord { + First, + Second, + Third, + Fourth, + Fifth, + Sixth, + Seventh, +} diff --git a/src/generator.rs b/src/generator.rs @@ -0,0 +1,114 @@ +use crate::data::{Bar, Beat, Chord, NoteEvent, Tonality}; +use rand::Rng; +use std::collections::HashMap; + +pub struct MusicGenerator { + previous_state: Chord, + transition_model: HashMap<Chord, Vec<(Chord, u32)>>, +} + +impl MusicGenerator { + pub fn new() -> Self { + use Chord::*; + let mut model = HashMap::new(); + + model.insert( + First, + vec![ + (Fourth, 5), // I -> IV (F) に行きやすい + (Fifth, 3), // I -> V (G) もあり + (Sixth, 2), // I -> VI (Am) もあり + (First, 1), // I のまま留まる + ], + ); + + model.insert( + Fourth, + vec![ + (Fifth, 8), // IV -> V (G) に行きやすい + (First, 2), // IV -> I (C) に戻る + ], + ); + + model.insert( + Fifth, + vec![ + (First, 10), // V -> I (C) に戻るのが基本 + ], + ); + + model.insert(Sixth, vec![(Fourth, 5), (First, 5)]); // Am -> F or C + model.insert(Second, vec![(Fifth, 10)]); // Dm -> G + + MusicGenerator { + previous_state: First, // 初期状態は I (C) + transition_model: model, + } + } + + pub fn generate_next_bar(&mut self) -> Bar { + let next_chord = self.choose_next_state(); + + let next_bar = self.generate_events_for_chord(next_chord); + + self.previous_state = next_chord; + next_bar + } + + fn choose_next_state(&self) -> Chord { + let options = self + .transition_model + .get(&self.previous_state) + .unwrap_or_else(|| panic!("there no code {:?} on this model", self.previous_state)); + + let total_weight: u32 = options.iter().map(|(_, w)| w).sum(); + let mut rng = rand::rng(); + let mut target = rng.random_range(0..total_weight); + + for (state, weight) in options { + if target < *weight { + return *state; + } + target -= weight; + } + options[0].0 + } + + fn generate_events_for_chord(&self, chord: Chord) -> Bar { + let root_note = match chord { + Chord::First => 60, // C4 + Chord::Second => 62, // D4 + Chord::Third => 64, // E4 + Chord::Fourth => 65, // F4 + Chord::Fifth => 67, // G4 + Chord::Sixth => 69, // A4 + Chord::Seventh => 71, // B4 + }; + + let mut events = Vec::new(); + const NOTE_DURATION: u64 = 480; // 500ms - 20ms の隙間 + + for i in 0..4 { + let time_ms = (i as u64) * 500; + + events.push(( + time_ms, + NoteEvent::NoteOn { + note: root_note, + velocity: 90, + }, + )); + events.push(( + time_ms + NOTE_DURATION, + NoteEvent::NoteOff { note: root_note }, + )); + } + + Bar { + beat: Beat::FourFourth, + tonality: Tonality::CM, + chord, + events, + } + } +} diff --git a/src/main.rs b/src/main.rs @@ -0,0 +1,103 @@ +mod data; +mod generator; +mod midi_io; + +use data::{Bar, NoteEvent}; +use generator::MusicGenerator; +use midi_io::MidiTransmitter; + +fn main() -> Result<(), Box<dyn std::error::Error>> { + let target_port_name = "Microsoft GS Wavetable Synth"; + let mut transmitter = MidiTransmitter::new(target_port_name)?; + + let mut generator = MusicGenerator::new(); + + // 1分(60,000ms) / 120 = 500ms が四分音符の長さ + const QUARTER_NOTE_DURATION_MS: u64 = 500; + + let start_time = std::time::Instant::now(); + let mut current_time_ms: u64 = 0; + + loop { + let next_bar: Bar = generator.generate_next_bar(); + println!("Generated Chord is: {:?}", next_bar.chord); // どのコードが選ばれたか表示 + + let bar_duration_ms = 4 * QUARTER_NOTE_DURATION_MS; + + for (event_time_offset, event) in next_bar.events.iter() { + let target_absolute_time_ms = current_time_ms + event_time_offset; + + let elapsed_time = start_time.elapsed().as_millis() as u64; + + if target_absolute_time_ms > elapsed_time { + let wait_time_ms = target_absolute_time_ms - elapsed_time; + std::thread::sleep(std::time::Duration::from_millis(wait_time_ms)); + } + + if let Some(message) = note_event_to_midi_message(*event) + && let Err(e) = transmitter.send_message(&message) + { + eprintln!("Midi sending error: {}", e); + } + } + + current_time_ms += bar_duration_ms; + + let elapsed_time = start_time.elapsed().as_millis() as u64; + if current_time_ms > elapsed_time { + let wait_time_ms = current_time_ms - elapsed_time; + std::thread::sleep(std::time::Duration::from_millis(wait_time_ms)); + } + } +} + +fn note_event_to_midi_message(event: NoteEvent) -> Option<[u8; 3]> { + match event { + NoteEvent::NoteOn { note, velocity } => Some([0x90, note, velocity]), + NoteEvent::NoteOff { note } => Some([0x80, note, 0]), + } +} + +// ---------------------------------------------------- +// 3. generator.rs の調整(data.rs に合わせて) +// ---------------------------------------------------- +// generator.rs の MusicGenerator::new() で、HashMapのキーとして +// data::Chord を使うように修正します。 +// 例: model.insert(Chord::First, vec![(Chord::Fourth, 7), ...]); +// また、generate_events_for_state() も Chord に対応させてください。 + +/* this is test for midi_io.ts +fn main() -> Result<(), Box<dyn std::error::Error>> { + println!("avalable Midi ports"); + if let Ok(midi_out) = MidiOutput::new("temp_lister") { + for name in MidiTransmitter::get_port_names(&midi_out) { + println!(" - {}", name); + } + } else { + println!("Failed to initialize Midi output"); + } + println!("---------------------------------"); + + let target_port_name = "Synth"; + + let mut transmitter = MidiTransmitter::new(target_port_name)?; + + // Note On: 0x9n (n=チャンネル), ノート番号(60=C4), ベロシティ(100) + let note_on_c4: [u8; 3] = [0x90, 60, 100]; + // Note Off: 0x8n (n=チャンネル), ノート番号(60=C4), ベロシティ(0) + let note_off_c4: [u8; 3] = [0x80, 60, 0]; + + println!("Sending C4 vel 100 on channel"); + transmitter.send_message(&note_on_c4)?; + + std::thread::sleep(std::time::Duration::from_millis(500)); + + println!("Sending C4 vel 100 off channel"); + transmitter.send_message(&note_off_c4)?; + + std::thread::sleep(std::time::Duration::from_millis(100)); + + println!("Done"); + Ok(()) +} +*/ diff --git a/src/midi_io.rs b/src/midi_io.rs @@ -0,0 +1,45 @@ +use midir::{MidiOutput, MidiOutputConnection, MidiOutputPort}; + +pub struct MidiTransmitter { + conn: MidiOutputConnection, +} + +type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>; + +impl MidiTransmitter { + pub fn new(port_name: &str) -> Result<Self> { + let midi_out = MidiOutput::new("Rust Midi Generator")?; + + let out_port = Self::find_port(&midi_out, port_name)?; + let actual_port_name = midi_out.port_name(&out_port)?; + + println!("We use to connect port: {}", actual_port_name); + + let conn = midi_out.connect(&out_port, "midir-output-port")?; + + Ok(MidiTransmitter { conn }) + } + + fn find_port(midi_out: &MidiOutput, search_name: &str) -> Result<MidiOutputPort> { + for port in midi_out.ports().iter() { + let name = midi_out.port_name(port)?; + if name.contains(search_name) { + return Ok(port.clone()); + } + } + + Err(format!("Midi port named '{}' was not found ", search_name).into()) + } + + pub fn send_message(&mut self, message: &[u8]) -> std::result::Result<(), midir::SendError> { + self.conn.send(message) + } + + pub fn get_port_names(midi_out: &MidiOutput) -> Vec<String> { + midi_out + .ports() + .iter() + .filter_map(|p| midi_out.port_name(p).ok()) + .collect() + } +}