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 | + |
| A | Cargo.lock | | | 508 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | Cargo.toml | | | 8 | ++++++++ |
| A | src/data.rs | | | 38 | ++++++++++++++++++++++++++++++++++++++ |
| A | src/generator.rs | | | 114 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/main.rs | | | 103 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/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(¬e_on_c4)?;
+
+ std::thread::sleep(std::time::Duration::from_millis(500));
+
+ println!("Sending C4 vel 100 off channel");
+ transmitter.send_message(¬e_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()
+ }
+}