use std::{ sync::{Arc, Mutex}, time::Duration, }; use dbus::blocking::Connection; use dbus::message::MatchRule; use dbus::Message; use dbus::{arg::messageitem::MessageItem, channel::MatchingReceiver}; use midi_control::*; use midir::*; fn main() { let midi_device = MidiOutput::new("synth output meow").expect("could not find device"); let ports = midi_device.ports(); // assumes that the device is connected on port 1 // TODO: add port selection let midi_out = midi_device .connect(&ports[1], "meow port") .expect("oh dear"); let midi_out = Arc::new(Mutex::new(midi_out)); // monitor dbus notifications let conn = Connection::new_session().unwrap(); let mut rule = MatchRule::new(); let proxy = conn.with_proxy( "org.freedesktop.DBus", "/org/freedesktop/DBus", Duration::from_millis(5000), ); let result: Result<(), dbus::Error> = proxy.method_call( "org.freedesktop.Notifications", "BecomeMonitor", (vec![rule.match_str()], 0u32), ); if result.is_ok() { // Start matching using new scheme conn.start_receive( rule, Box::new(move |msg, _| { midi_hook(midi_out.clone(), &msg); true }), ); } else { // Start matching using old scheme rule.eavesdrop = true; conn.add_match(rule, move |_: (), _, msg| { midi_hook(midi_out.clone(), &msg); true }) .expect("add_match failed"); } loop { conn.process(Duration::from_millis(1000)).unwrap(); } // TODO: use the ctrlc crate to handle the midi connection closing } fn midi_hook(midi_out: Arc>, msg: &Message) { let args = msg.get_items(); // do nothing if args are empty if args.is_empty() { return; } // only apply to firefox notifications // TODO: make browser name configurable if args[0] == "Nightly".into() { // get the dict of values inside the message's args let dict_values = match &args[6] { MessageItem::Dict(v) => v.clone().into_vec(), _ => Vec::new(), }; { // if the key equals "sender-pid", the message is a duplicate of the last one let (v, _) = &dict_values[&dict_values.len() - 1]; if v == &"sender-pid".into() { return; } } println!("Got a notification from {:?}!", args[0]); // TODO: make note pitch and duration configurable let play_note = midi_control::note_on(Channel::Ch1, 40, 127); let stop_note = midi_control::note_off(Channel::Ch1, 40, 127); let mut midi_out = midi_out.lock().expect("couldn't acquire midi connection"); midi_out .send_message(play_note) .expect("could not play note"); std::thread::sleep(Duration::from_millis(2000)); midi_out .send_message(stop_note) .expect("could not stop play note"); } }