diff --git a/Cargo.lock b/Cargo.lock index 3c74e1c..fa63e02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "aho-corasick" -version = "0.7.15" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] @@ -17,7 +17,7 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "baru" -version = "0.2.7" +version = "0.2.8" dependencies = [ "chrono", "cmake", @@ -56,15 +56,15 @@ dependencies = [ [[package]] name = "dtoa" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" [[package]] name = "libc" -version = "0.2.91" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" [[package]] name = "linked-hash-map" @@ -74,9 +74,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "memchr" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "num-integer" @@ -99,9 +99,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] @@ -117,9 +117,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.4.5" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", "memchr", @@ -128,24 +128,24 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.23" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "serde" -version = "1.0.125" +version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.125" +version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ "proc-macro2", "quote", @@ -166,9 +166,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.65" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663" +checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" dependencies = [ "proc-macro2", "quote", @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "wasi" diff --git a/Cargo.toml b/Cargo.toml index 861f333..633649f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "baru" -version = "0.2.7" +version = "0.2.8" authors = ["pierre "] edition = "2018" links = "netlink,audio" diff --git a/baru.yaml b/baru.yaml index 4da11fc..f37bb9a 100644 --- a/baru.yaml +++ b/baru.yaml @@ -377,11 +377,12 @@ mic: # placeholder: '-' - # index: u32, default: 0 + # source_name: String, default: None # - # The source index. + # If not provided, the default source will be used automatically. + # You can get it with `pactl list sources` command. # - index: 1 + source_name: "some_name" # label: String, default: mic # @@ -421,11 +422,12 @@ sound: # placeholder: '-' - # index: u32, default: 0 + # sink_name: String, default: None # - # The sink index. + # If not provided, the default sink will be used automatically. + # You can get it with `pactl list sinks` command. # - index: 1 + sink_name: "some_name" # label: String, default: sou # diff --git a/lib/audio/include/audio.h b/lib/audio/include/audio.h index 7177d81..55952e1 100644 --- a/lib/audio/include/audio.h +++ b/lib/audio/include/audio.h @@ -32,34 +32,41 @@ static bool alive = true; typedef struct timespec - t_timespec; + t_timespec; -typedef struct volume { - uint32_t volume; - bool mute; -} t_volume; +typedef struct volume { + uint32_t volume; + bool mute; +} t_volume; -typedef void(* send_sink_cb)(void *, uint32_t, bool); -typedef void(* send_source_cb)(void *, uint32_t, bool); +typedef void(*send_cb)(void *, uint32_t, bool); -typedef struct data { - uint32_t tick; - uint32_t sink_index; - uint32_t source_index; - bool connected; - pa_context *context; - pa_mainloop *mainloop; - pa_mainloop_api *api; - t_volume sink_volume; - t_volume source_volume; - void *cb_context; - send_sink_cb sink_cb; - send_source_cb source_cb; - t_timespec start; - pa_operation *sink_op; - pa_operation *source_op; -} t_data; +typedef struct data { + const char *name; + bool use_default; + t_volume volume; + send_cb cb; + pa_operation *op; +} t_data; -void run(uint32_t tick, uint32_t sink_index, uint32_t source_index, void *, send_sink_cb, send_source_cb); +typedef struct main { + uint32_t tick; + bool connected; + pa_context *context; + pa_mainloop *mainloop; + pa_mainloop_api *api; + void *cb_context; + t_timespec start; + pa_operation *server_op; + t_data *sink; + t_data *source; +} t_main; + +void run(uint32_t tick, + const char *sink_name, + const char *source_name, + void *cb_context, + send_cb, + send_cb); #endif //AUDIO_H diff --git a/lib/audio/src/audio.c b/lib/audio/src/audio.c index 4740d21..fe432f2 100644 --- a/lib/audio/src/audio.c +++ b/lib/audio/src/audio.c @@ -9,12 +9,19 @@ void printe(char *err) { exit(EXIT_FAILURE); } -void context_state_cb(pa_context *context, void *data) { +void init_data(t_data *data, const char *name, send_cb cb) { + data->name = name; + data->cb = cb; + data->op = NULL; + data->use_default = name == NULL ? true : false; +} + +void context_state_cb(pa_context *context, void *main) { pa_context_state_t state; state = pa_context_get_state(context); if (state == PA_CONTEXT_READY) { - ((t_data *) data)->connected = true; + ((t_main *) main)->connected = true; } else if (state == PA_CONTEXT_FAILED) { printe("context connection failed"); } @@ -27,48 +34,92 @@ void try_free_op(pa_operation **operation) { } } -void sink_info_cb(pa_context *context, const pa_sink_info *info, int eol, void *data) { - t_data *d; +void sink_info_cb(pa_context *context, const pa_sink_info *info, int eol, void *main) { + t_main *m; (void) context; - d = data; + m = main; if (info != NULL && eol == 0) { - d->sink_volume.mute = info->mute; - d->sink_volume.volume = VOLUME(pa_cvolume_avg(&info->volume)); - (*d->sink_cb)(d->cb_context, d->sink_volume.volume, d->sink_volume.mute); + m->sink->volume.mute = info->mute; + m->sink->volume.volume = VOLUME(pa_cvolume_avg(&info->volume)); + (*m->sink->cb)(m->cb_context, m->sink->volume.volume, m->sink->volume.mute); } if (eol != 0) { - try_free_op(&d->sink_op); + try_free_op(&m->sink->op); } } -void source_info_cb(pa_context *context, const pa_source_info *info, int eol, void *data) { - t_data *d; +void source_info_cb(pa_context *context, const pa_source_info *info, int eol, + void *main) { + t_main *m; (void) context; - d = data; + m = main; if (info != NULL && eol == 0) { - d->source_volume.mute = info->mute; - d->source_volume.volume = VOLUME(pa_cvolume_avg(&info->volume)); - (*d->source_cb)(d->cb_context, d->source_volume.volume, d->source_volume.mute); + m->source->volume.mute = info->mute; + m->source->volume.volume = VOLUME(pa_cvolume_avg(&info->volume)); + (*m->source->cb)(m->cb_context, m->source->volume.volume, m->source->volume.mute); } if (eol != 0) { - try_free_op(&d->source_op); + try_free_op(&m->source->op); + } +} + +const char *name_switch(const char *old_name, const char *new_name) { + if (old_name != NULL) { + free((char *) old_name); } + if ((old_name = malloc(sizeof(char) * (strlen(new_name) + 1))) == NULL) { + printe("malloc failed"); + } + return strcpy((char *) old_name, new_name); } -void subscription_cb(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *data) { - t_data *d; +void +server_info_cb(pa_context *context, const pa_server_info *info, void *main) { + t_main *m; + + (void) context; + m = main; + if (info != NULL) { + if (m->sink->use_default && (m->sink->name == NULL || strcmp(info->default_sink_name, m->sink->name) != 0)) { + m->sink->name = name_switch(m->sink->name, info->default_sink_name); + try_free_op(&m->sink->op); + m->sink->op = pa_context_get_sink_info_by_name(m->context, m->sink->name, sink_info_cb, main); + } + if (m->source->use_default && + (m->source->name == NULL || strcmp(info->default_source_name, m->source->name) != 0)) { + m->source->name = name_switch(m->source->name, info->default_source_name); + try_free_op(&m->source->op); + m->source->op = pa_context_get_source_info_by_name(m->context, m->source->name, source_info_cb, main); + } + } + try_free_op(&m->server_op); +} + +void subscription_cb(pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *main) { + t_main *m; (void) context; (void) idx; - d = data; - if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { - try_free_op(&d->sink_op); - d->sink_op = pa_context_get_sink_info_by_index(d->context, d->sink_index, sink_info_cb, data); - } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) { - try_free_op(&d->source_op); - d->source_op = pa_context_get_source_info_by_index(d->context, d->source_index, source_info_cb, data); + m = main; + switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { + case PA_SUBSCRIPTION_EVENT_SINK: + try_free_op(&m->sink->op); + if (m->sink->name != NULL) { + m->sink->op = pa_context_get_sink_info_by_name(m->context, m->sink->name, sink_info_cb, main); + } + break; + case PA_SUBSCRIPTION_EVENT_SOURCE: + try_free_op(&m->source->op); + if (m->source->name != NULL) { + m->source->op = pa_context_get_source_info_by_name(m->context, m->source->name, source_info_cb, main); + } + break; + case PA_SUBSCRIPTION_EVENT_SERVER: + try_free_op(&m->server_op); + m->server_op = pa_context_get_server_info(m->context, server_info_cb, main); + break; } } @@ -87,79 +138,94 @@ void abs_time_tick(t_timespec *start, t_timespec *end, uint32_t tick) { } } -void iterate(t_data *data) { +void iterate(t_main *main) { t_timespec tick; // get the time at the start of an iteration - if (clock_gettime(CLOCK_REALTIME, &data->start) == -1) { + if (clock_gettime(CLOCK_REALTIME, &main->start) == -1) { printe("clock_gettime failed"); } // get the absolute time of the next tick (start time + tick value) - abs_time_tick(&data->start, &tick, data->tick); + abs_time_tick(&main->start, &tick, main->tick); // iterate the main loop - if (pa_mainloop_iterate(data->mainloop, 0, NULL) < 0) { + if (pa_mainloop_iterate(main->mainloop, 0, NULL) < 0) { printe("pa_mainloop_iterate failed"); } // free pa_operation objects - try_free_op(&data->sink_op); - try_free_op(&data->source_op); + try_free_op(&main->sink->op); + try_free_op(&main->source->op); // wait for the remaining time of the tick value clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tick, NULL); } -void run(uint32_t tick, uint32_t sink_index, uint32_t source_index, void *cb_context, send_sink_cb sink_cb, - send_source_cb source_cb) { +void run(uint32_t tick, const char *sink_name, const char *source_name, void *cb_context, send_cb sink_cb, + send_cb source_cb) { pa_proplist *proplist; - t_data data; + t_main main; + t_data sink; + t_data source; pa_operation *context_subscription; - data.tick = tick; - data.sink_index = sink_index; - data.source_index = source_index; - data.connected = false; - data.cb_context = cb_context; - data.sink_cb = sink_cb; - data.source_cb = source_cb; - data.mainloop = pa_mainloop_new(); - data.api = pa_mainloop_get_api(data.mainloop); + init_data(&sink, sink_name, sink_cb); + init_data(&source, source_name, source_cb); + + main.tick = tick; + main.connected = false; + main.cb_context = cb_context; + main.mainloop = pa_mainloop_new(); + main.api = pa_mainloop_get_api(main.mainloop); + main.server_op = NULL; + main.sink = &sink; + main.source = &source; + proplist = pa_proplist_new(); // context creation if (pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, APPLICATION_NAME) != 0) { printe("pa_proplist_sets failed"); } - data.context = pa_context_new_with_proplist(data.api, APPLICATION_NAME, proplist); + main.context = pa_context_new_with_proplist(main.api, APPLICATION_NAME, proplist); // context connection to the sever - pa_context_set_state_callback(data.context, context_state_cb, &data); - if (pa_context_connect(data.context, NULL, PA_CONTEXT_NOFAIL, NULL) < 0) { + pa_context_set_state_callback(main.context, context_state_cb, &main); + if (pa_context_connect(main.context, NULL, PA_CONTEXT_NOFAIL, NULL) < 0) { printe("pa_context_connect failed"); } - while (data.connected == false) { - if (pa_mainloop_iterate(data.mainloop, 0, NULL) < 0) { + while (main.connected == false) { + if (pa_mainloop_iterate(main.mainloop, 0, NULL) < 0) { printe("pa_mainloop_iterate failed"); } } // initial introspection - data.sink_op = pa_context_get_sink_info_by_index(data.context, data.sink_index, sink_info_cb, &data); - data.source_op = pa_context_get_source_info_by_index(data.context, data.source_index, source_info_cb, &data); + if (sink.use_default || source.use_default) { + main.server_op = pa_context_get_server_info(main.context, server_info_cb, &main); + } + if (!sink.use_default) { + main.sink->op = pa_context_get_sink_info_by_name(main.context, sink.name, sink_info_cb, &main); + } + if (!source.use_default) { + main.source->op = pa_context_get_source_info_by_name(main.context, source.name, source_info_cb, &main); + } // subscription introspection - context_subscription = - pa_context_subscribe(data.context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, NULL, NULL); - pa_context_set_subscribe_callback(data.context, subscription_cb, &data); + int subscription_mask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE; + if (sink.use_default || source.use_default) { + subscription_mask |= PA_SUBSCRIPTION_MASK_SERVER; + } + context_subscription = pa_context_subscribe(main.context, subscription_mask, NULL, NULL); + pa_context_set_subscribe_callback(main.context, subscription_cb, &main); // iterate main loop while (alive) { - iterate(&data); + iterate(&main); } // close connection and free pa_operation_unref(context_subscription); - pa_context_disconnect(data.context); - pa_mainloop_free(data.mainloop); + pa_context_disconnect(main.context); + pa_mainloop_free(main.mainloop); } diff --git a/src/mic.rs b/src/mic.rs index 47d9620..237efb4 100644 --- a/src/mic.rs +++ b/src/mic.rs @@ -20,7 +20,7 @@ const FORMAT: &str = "%l:%v"; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Config { - pub index: Option, + pub source_name: Option, tick: Option, placeholder: Option, label: Option, diff --git a/src/pulse.rs b/src/pulse.rs index 4f43c73..44f8cd0 100644 --- a/src/pulse.rs +++ b/src/pulse.rs @@ -4,12 +4,12 @@ use crate::error::Error; use crate::Config; +use std::os::raw::c_char; use std::sync::mpsc::{self, Receiver, Sender}; use std::thread::{self, JoinHandle}; +use std::{ffi::CString, ptr}; const PULSE_RATE: u32 = 50_000_000; // in nanosecond -const SINK_INDEX: u32 = 0; -const SOURCE_INDEX: u32 = 0; pub type Callback = extern "C" fn(*const CallbackContext, u32, bool); @@ -34,25 +34,21 @@ impl Pulse { Some(val) => val * 1e6 as u32, None => PULSE_RATE, }; - let mut sink_index = SINK_INDEX; - let mut source_index = SOURCE_INDEX; + let mut sink_name = None; + let mut source_name = None; if let Some(c) = &config.sound { - if let Some(v) = c.index { - sink_index = v; - } + sink_name = c.sink_name.clone(); } if let Some(c) = &config.mic { - if let Some(v) = c.index { - source_index = v; - } + source_name = c.source_name.clone(); } let builder = thread::Builder::new().name("pulse_mod".into()); let handle = builder.spawn(move || -> Result<(), Error> { let cb_context = CallbackContext(sink_tx, source_tx); pulse_run( tick, - sink_index, - source_index, + sink_name, + source_name, &cb_context, sink_cb, source_cb, @@ -93,8 +89,8 @@ extern "C" fn source_cb(context: *const CallbackContext, volume: u32, mute: bool extern "C" { fn run( tick: u32, - sink_index: u32, - source_index: u32, + sink_name: *const c_char, + source_name: *const c_char, cb_context: *const CallbackContext, sink_cb: Callback, source_cb: Callback, @@ -103,21 +99,26 @@ extern "C" { pub fn pulse_run( tick: u32, - sink_index: u32, - source_index: u32, + sink_name: Option, + source_name: Option, callback_context: &CallbackContext, sink_cb: Callback, source_cb: Callback, ) { let context_ptr: *const CallbackContext = callback_context; + let mut ptr_sink = ptr::null(); + let mut ptr_source = ptr::null(); + let c_string_sink; + let c_string_source; + if let Some(s) = sink_name { + c_string_sink = CString::new(s).expect("CString::new failed"); + ptr_sink = c_string_sink.as_ptr(); + }; + if let Some(s) = source_name { + c_string_source = CString::new(s).expect("CString::new failed"); + ptr_source = c_string_source.as_ptr(); + }; unsafe { - run( - tick, - sink_index, - source_index, - context_ptr, - sink_cb, - source_cb, - ); + run(tick, ptr_sink, ptr_source, context_ptr, sink_cb, source_cb); } } diff --git a/src/sound.rs b/src/sound.rs index ff4a326..624e0cf 100644 --- a/src/sound.rs +++ b/src/sound.rs @@ -20,7 +20,7 @@ const FORMAT: &str = "%l:%v"; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Config { - pub index: Option, + pub sink_name: Option, tick: Option, placeholder: Option, label: Option,