diff --git a/Cargo.lock b/Cargo.lock index f872b21e..238a65a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,6 +163,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "bumpalo" version = "3.12.1" @@ -347,11 +353,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[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.4" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "coreaudio-rs" @@ -373,6 +389,27 @@ dependencies = [ "bindgen", ] +[[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.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709d142e542467e028d5dc5f0374392339ab7dead0c48c129504de2ccd667e1b" +dependencies = [ + "core-foundation-sys", +] + [[package]] name = "cpal" version = "0.15.3" @@ -393,7 +430,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows", + "windows 0.54.0", ] [[package]] @@ -496,23 +533,23 @@ checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" [[package]] name = "directories" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -765,7 +802,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.63", "walkdir", "windows-sys 0.45.0", ] @@ -834,7 +871,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.72", + "syn 2.0.87", ] [[package]] @@ -932,7 +969,7 @@ dependencies = [ "midi-toolkit-rs-derive", "num-traits", "rayon", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -949,6 +986,23 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "midir" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e77a0c42a20fdcb979064b996b7ba8a0030a866832d6ea5ab689a0688ef3a9" +dependencies = [ + "alsa", + "bitflags 1.3.2", + "coremidi", + "js-sys", + "libc", + "parking_lot", + "wasm-bindgen", + "web-sys", + "windows 0.56.0", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -978,7 +1032,7 @@ dependencies = [ "log", "ndk-sys", "num_enum", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1055,7 +1109,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.87", ] [[package]] @@ -1094,7 +1148,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.87", ] [[package]] @@ -1384,13 +1438,13 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 2.0.12", ] [[package]] @@ -1424,7 +1478,7 @@ checksum = "ccd8d4222c7fe394fb5ae81c08515107316596c8d4594f1a0d636919350c7eff" dependencies = [ "regex", "regex-bnf-macro", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1535,7 +1589,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.87", ] [[package]] @@ -1751,9 +1805,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -1794,7 +1848,16 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -1805,7 +1868,18 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -1915,7 +1989,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -1949,7 +2023,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2016,7 +2090,17 @@ version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" dependencies = [ - "windows-core", + "windows-core 0.54.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" +dependencies = [ + "windows-core 0.56.0", "windows-targets 0.52.6", ] @@ -2030,6 +2114,40 @@ dependencies = [ "windows-targets 0.52.6", ] +[[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 0.52.6", +] + +[[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 2.0.87", +] + +[[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 2.0.87", +] + [[package]] name = "windows-result" version = "0.1.2" @@ -2290,11 +2408,25 @@ dependencies = [ "simdeez", "spin_sleep", "symphonia", - "thiserror", + "thiserror 1.0.63", "to_vec", "xsynth-soundfonts", ] +[[package]] +name = "xsynth-interface" +version = "0.3.2" +dependencies = [ + "cfg-if", + "directories", + "hotwatch", + "midir", + "serde", + "serde_json", + "xsynth-core", + "xsynth-realtime", +] + [[package]] name = "xsynth-kdmapi" version = "0.3.2" @@ -2340,7 +2472,7 @@ dependencies = [ "midi-toolkit-rs", "rayon", "spin_sleep", - "thiserror", + "thiserror 1.0.63", "xsynth-core", ] @@ -2355,5 +2487,5 @@ dependencies = [ "rubato", "simdeez", "soundfont", - "thiserror", + "thiserror 1.0.63", ] diff --git a/Cargo.toml b/Cargo.toml index 4565f675..874d6dd1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["core", "clib", "soundfonts", "realtime", "render", "kdmapi"] +members = ["core", "clib", "soundfonts", "realtime", "render", "kdmapi", "interface"] [workspace.package] version = "0.3.2" diff --git a/README.md b/README.md index 1476dbf1..27477108 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,13 @@ ## Modules -- [`core`](https://github.com/BlackMIDIDevs/xsynth/tree/master/core): Handles the core audio rendering functionality. -- [`clib`](https://github.com/BlackMIDIDevs/xsynth/tree/master/clib): C/C++ bindings for XSynth. -- [`soundfonts`](https://github.com/BlackMIDIDevs/xsynth/tree/master/soundfonts): A module to parse soundfonts to be used in XSynth. -- [`realtime`](https://github.com/BlackMIDIDevs/xsynth/tree/master/realtime): The real-time rendering module within XSynth. -- [`render`](https://github.com/BlackMIDIDevs/xsynth/tree/master/render): A command line utility for rendering MIDIs to audio using XSynth. -- [`kdmapi`](https://github.com/BlackMIDIDevs/xsynth/tree/master/render): A cdylib wrapper around XSynth to act as a drop in replacement for OmniMIDI/KDMAPI. +- [`core`](/core): Handles the core audio rendering functionality. +- [`clib`](/clib): C/C++ bindings for XSynth. +- [`soundfonts`](/soundfonts): A module to parse soundfonts to be used in XSynth. +- [`realtime`](/realtime): The real-time rendering module within XSynth. +- [`render`](/render): A command line utility for rendering MIDIs to audio using XSynth. +- [`kdmapi`](/kdmapi): A cdylib wrapper around XSynth to act as a drop in replacement for OmniMIDI/KDMAPI. +- [`interface`](/interface): A MIDI interface for XSynth. ## Demos diff --git a/interface/Cargo.toml b/interface/Cargo.toml new file mode 100644 index 00000000..61b3647d --- /dev/null +++ b/interface/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "xsynth-interface" +version.workspace = true +license.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true + +[dependencies] +xsynth-core = { workspace = true, features = ["serde"] } +xsynth-realtime = { workspace = true, features = ["serde"] } +midir = { version = "0.10.1" } +cfg-if = "1.0.0" +serde_json = "1.0.122" +serde = { version = "1.0.206", features = ["derive"] } +hotwatch = "0.5.0" +directories = "6.0.0" diff --git a/interface/README.md b/interface/README.md new file mode 100644 index 00000000..66c50f78 --- /dev/null +++ b/interface/README.md @@ -0,0 +1,3 @@ +# xsynth-interface + +A MIDI interface for XSynth. By using midir, it can be used to send MIDI events to XSynth without the need of KDMAPI or direct integration with XSynth. diff --git a/interface/src/main.rs b/interface/src/main.rs new file mode 100644 index 00000000..2dec37d6 --- /dev/null +++ b/interface/src/main.rs @@ -0,0 +1,102 @@ +use std::error::Error; +use std::io::stdin; + +#[cfg(not(target_os = "windows"))] +use midir::os::unix::VirtualInput; + +use hotwatch::{Event, EventKind, Hotwatch}; +use midir::{Ignore, MidiInput}; +use std::{thread, time::Duration}; +use xsynth_core::channel::{ChannelConfigEvent, ChannelEvent}; +use xsynth_realtime::{RealtimeSynth, SynthEvent}; + +mod parsers; +use parsers::*; + +fn main() { + match run() { + Ok(_) => (), + Err(err) => println!("Error: {}", err), + } +} + +#[cfg(not(target_os = "windows"))] +fn run() -> Result<(), Box> { + let config = Config::::new().load().unwrap(); + let sflist = Config::::new().load().unwrap(); + + let realtime_synth = RealtimeSynth::open_with_default_output(config.get_synth_config()); + let mut sender = realtime_synth.get_sender_ref().clone(); + let params = realtime_synth.stream_params(); + + sender.send_event(SynthEvent::AllChannels(ChannelEvent::Config( + ChannelConfigEvent::SetLayerCount(config.get_layers()), + ))); + sender.send_event(SynthEvent::AllChannels(ChannelEvent::Config( + ChannelConfigEvent::SetSoundfonts(sflist.create_sfbase_vector(params)), + ))); + + let mut midi_in = MidiInput::new("XSynth")?; + midi_in.ignore(Ignore::None); + + // _conn_in needs to be a named parameter, because it needs to be kept alive until the end of the scope + let mut sender_thread = sender.clone(); + let _conn_in = midi_in.create_virtual( + "XSynth MIDI In", + move |_, message, _| { + //println!("{}: {:?} (len = {})", stamp, message, message.len()); + //Send the MIDI message to the synth as raw bytes + sender_thread.send_event_u32( + message.get(0).copied().unwrap_or(0) as u32 + | (message.get(1).copied().unwrap_or(0) as u32) << 8 + | (message.get(2).copied().unwrap_or(0) as u32) << 16, + ); + }, + (), + )?; + + let mut hotwatch = Hotwatch::new_with_custom_delay(Duration::from_millis(500)).unwrap(); + + // Watch for config changes and apply them + let mut sender_thread = sender.clone(); + hotwatch + .watch(Config::::path(), move |event: Event| { + if let EventKind::Modify(_) = event.kind { + thread::sleep(Duration::from_millis(10)); + let layers = Config::::new().load().unwrap().get_layers(); + sender_thread.send_event(SynthEvent::AllChannels(ChannelEvent::Config( + ChannelConfigEvent::SetLayerCount(layers), + ))); + } + }) + .unwrap(); + + // Watch for soundfont list changes and apply them + let mut sender_thread = sender.clone(); + hotwatch + .watch(Config::::path(), move |event: Event| { + if let EventKind::Modify(_) = event.kind { + thread::sleep(Duration::from_millis(10)); + let sfs = Config::::new() + .load() + .unwrap() + .create_sfbase_vector(params); + sender_thread.send_event(SynthEvent::AllChannels(ChannelEvent::Config( + ChannelConfigEvent::SetSoundfonts(sfs), + ))); + } + }) + .unwrap(); + + println!("Press any key to exit..."); + let mut input = String::new(); + stdin().read_line(&mut input)?; + println!("Shutting down..."); + Ok(()) +} + +#[cfg(target_os = "windows")] +fn run() -> Result<(), Box> { + println!("xsynth-interface is not supported on Windows."); + Ok(()) +} diff --git a/interface/src/parsers.rs b/interface/src/parsers.rs new file mode 100644 index 00000000..9cd548d9 --- /dev/null +++ b/interface/src/parsers.rs @@ -0,0 +1,81 @@ +use serde::{Deserialize, Serialize}; +use std::{fs::File, io::prelude::*, marker::PhantomData, path::PathBuf}; + +mod soundfonts; +pub use soundfonts::SFList; + +mod settings; +pub use settings::Settings; + +const CONFIG_DIR: &str = "xsynth"; + +pub trait ConfigPath { + fn filename() -> PathBuf; +} + +pub struct Config +where + T: Default + Serialize + for<'a> Deserialize<'a> + ConfigPath, +{ + path: PathBuf, + _config: PhantomData, +} + +impl Config +where + T: Default + Serialize + for<'a> Deserialize<'a> + ConfigPath, +{ + pub fn path() -> PathBuf { + match directories::BaseDirs::new() { + Some(dirs) => { + let mut path = dirs.config_dir().to_path_buf(); + path.push(CONFIG_DIR); + std::fs::create_dir_all(&path).unwrap(); + path.push(T::filename()); + path + } + None => PathBuf::from("./"), + } + } + + pub fn new() -> Self { + Self { + path: Config::::path(), + _config: PhantomData, + } + } + + fn load_from_file(&self) -> Result { + let mut file = File::open(&self.path).map_err(|e| format!("IO error: {e}"))?; + let mut contents = String::new(); + file.read_to_string(&mut contents) + .map_err(|e| format!("Loading error: {e}"))?; + serde_json::from_str(&contents).map_err(|e| format!("Parsing error: {e}")) + } + + fn save(&self, config: &T) -> Result<(), String> { + let contents = + serde_json::to_string_pretty(config).map_err(|e| format!("Parsing error: {e}"))?; + let mut file = File::create(&self.path).map_err(|e| format!("IO error: {e}"))?; + file.write_all(contents.as_bytes()) + .map_err(|e| format!("Saving error: {e}"))?; + + Ok(()) + } + + fn create_empty(&self) -> Result<(), String> { + self.save(&T::default()) + } + + pub fn load(&self) -> Result { + let path = &self.path; + if !path.exists() { + self.create_empty()?; + } + self.load_from_file() + } + + pub fn repair(&self) -> Result<(), String> { + self.save(&self.load()?) + } +} diff --git a/interface/src/parsers/settings.rs b/interface/src/parsers/settings.rs new file mode 100644 index 00000000..6f7607b1 --- /dev/null +++ b/interface/src/parsers/settings.rs @@ -0,0 +1,56 @@ +use super::ConfigPath; +use serde::{Deserialize, Serialize}; +use std::{ops::RangeInclusive, path::PathBuf}; +use xsynth_core::channel::ChannelInitOptions; +use xsynth_realtime::{SynthFormat, ThreadCount, XSynthRealtimeConfig}; + +#[derive(Clone, Serialize, Deserialize)] +#[serde(default)] +pub struct Settings { + // Channel options + layers: Option, + fade_out_killing: bool, + + // Realtime synth options + render_window_ms: f64, + multithreading: ThreadCount, + ignore_range: RangeInclusive, +} + +impl Default for Settings { + fn default() -> Self { + let chandef = ChannelInitOptions::default(); + + Self { + layers: Some(4), + fade_out_killing: chandef.fade_out_killing, + render_window_ms: 10.0, + multithreading: ThreadCount::None, + ignore_range: 0..=0, + } + } +} + +impl Settings { + pub fn get_layers(&self) -> Option { + self.layers + } + + pub fn get_synth_config(&self) -> XSynthRealtimeConfig { + XSynthRealtimeConfig { + channel_init_options: ChannelInitOptions { + fade_out_killing: self.fade_out_killing, + }, + render_window_ms: self.render_window_ms, + format: SynthFormat::Midi, + multithreading: self.multithreading, + ignore_range: self.ignore_range.clone(), + } + } +} + +impl ConfigPath for Settings { + fn filename() -> PathBuf { + "settings.json".into() + } +} diff --git a/interface/src/parsers/soundfonts.rs b/interface/src/parsers/soundfonts.rs new file mode 100644 index 00000000..db4acf0a --- /dev/null +++ b/interface/src/parsers/soundfonts.rs @@ -0,0 +1,74 @@ +use super::ConfigPath; +use serde::{Deserialize, Serialize}; +use std::{path::PathBuf, sync::Arc}; +use xsynth_core::{ + soundfont::{SampleSoundfont, SoundfontBase, SoundfontInitOptions}, + AudioStreamParams, +}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(default)] +pub struct SFDescriptor { + pub path: PathBuf, + pub enabled: bool, + pub options: SoundfontInitOptions, +} + +impl Default for SFDescriptor { + fn default() -> Self { + Self { + path: PathBuf::new(), + enabled: true, + options: Default::default(), + } + } +} + +impl SFDescriptor { + pub fn path(&self) -> Option { + let path = PathBuf::from(&self.path); + if path.exists() && self.enabled { + Some(path) + } else { + None + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(default)] +pub struct SFList { + soundfonts: Vec, +} + +impl Default for SFList { + fn default() -> Self { + Self { + soundfonts: vec![SFDescriptor::default()], + } + } +} + +impl SFList { + pub fn create_sfbase_vector( + self, + stream_params: AudioStreamParams, + ) -> Vec> { + let mut out: Vec> = Vec::new(); + for sf in self.soundfonts { + if let Some(path) = sf.path() { + match SampleSoundfont::new(path, stream_params, sf.options) { + Ok(sf) => out.push(Arc::new(sf)), + Err(e) => println!("Error loading soundfont: {e}"), + } + } + } + out + } +} + +impl ConfigPath for SFList { + fn filename() -> PathBuf { + "soundfonts.json".into() + } +} diff --git a/kdmapi/Cargo.toml b/kdmapi/Cargo.toml index 386aea6c..e9750faa 100644 --- a/kdmapi/Cargo.toml +++ b/kdmapi/Cargo.toml @@ -25,4 +25,4 @@ cfg-if = "1.0.0" serde_json = "1.0.122" serde = { version = "1.0.206", features = ["derive"] } hotwatch = "0.5.0" -directories = "5.0.1" \ No newline at end of file +directories = "6.0.0" diff --git a/kdmapi/README.md b/kdmapi/README.md index e2b6942a..d44055c0 100644 --- a/kdmapi/README.md +++ b/kdmapi/README.md @@ -6,7 +6,7 @@ A cdylib wrapper around XSynth to act as a drop in replacement for OmniMIDI/KDMA 1) Alternatively you can build the library yourself by cloning the repo and compiling with Cargo. 2) Place it in the same directory as your KDMAPI aware software of choice. -Upon loading the library, the following two files will be generated under `%userprofile%/AppData/Roaming/xsynth-kdmapi` (on Windows): +Upon loading the library, the following two files will be generated under `%userprofile%/AppData/Roaming/xsynth` (on Windows): ### `settings.json` The synthesizer settings. Fields: diff --git a/kdmapi/src/parsers.rs b/kdmapi/src/parsers.rs index 21592d8f..9cd548d9 100644 --- a/kdmapi/src/parsers.rs +++ b/kdmapi/src/parsers.rs @@ -7,7 +7,7 @@ pub use soundfonts::SFList; mod settings; pub use settings::Settings; -const CONFIG_DIR: &str = "xsynth-kdmapi"; +const CONFIG_DIR: &str = "xsynth"; pub trait ConfigPath { fn filename() -> PathBuf;