Closed doums closed 4 years ago
First, thank for your amazing work!
No problem!
Sorry it took a couple days to get back to you; busy, busy.
I have no previous experience with the PulseAudio C library and I'm just trying to use your binding to retrieve the informations of the sink (volume, muted). I started by using the standard loop (I have no clue if it's a good idea or not, for my simple needs). What I have done successfully is to create a context and connect it to the pulseaudio running server. Also I can subscribe to sink events and get the callback fired as expected. Now the problem is I fail to retrieve the sinks info. Either from the subscribe callback or from the context's introspection. In the doc there is no example for that (and I find it pretty complex for a beginner without some previous experience with PulseAudio). Also the relationship between the main loop and the context introspection/subscribed events is pretty vague.
Can you help me ?
Yeah, interacting with PulseAudio is not the easiest thing to get to grips with. Most of the documentation here derives directly from that in the C interface and I'm sure that there might be undesirable deficiencies in explaining how to do things as you've experienced. If I had the time I'd try to take a further look at that but I'm very busy. I happened to be completely new to PulseAudio myself when I started this project and it took a bit of experimenting to get to grips with things sufficiently such that I could have a working threaded mainloop example to test stuff with since there was just no complete example of how on earth to do it. I did update the documentation with such an example to cover that aspect at least.
Do feel free though to create bug reports for areas where the documentation doesn't explain things well or give sufficient examples. It's a very valid area of improvement worth reporting, and although I will try to consider improvements myself if I can find time, it can be very helpful to know of specific issues that users had trouble with. I can't promise to actually get on top of such reports quickly, but they can sit there as a reminder for a little while, and someone else might very well come along, notice, and decide to offer up a patch (which you're welcome to do yourself), and a patch might also take less time to review than doing it myself.
EDIT: After a couple more hours of diving in the doc and the code, I finally got it. So all my question above are not relevant anymore.
Okay, great! :)
I'll skip looking at the code you posted then if you don't mind; too much else to do to spend time looking at it if it's not needed.
Edit: Oh, note that the actual PulseAudio project has a mailing list. I don't know whether anyone actively on it actually has experience with my Rust binding and thus could give specific advice for using it, but the interface this binding provides is not very far removed from the C interface underneath it and so their mailing list offers another good resource in terms of at least being able to get help and advice on concepts with the C interface which could roughly translate to use with this binding.
[Responding to something that was deleted before I had submitted my response]
Hi,
No problem. From a quick look, your problem seems to be because you're
not using the mainloop's iterate()
function within that last loop,
thus you never let the PA mainloop process the messages sent to it from
the PA server to receive the requested data and update the state of
sources
to 'done'.
Take a look at the loop you copied with the "Wait for context to be
ready" comment; notice there that the mainloop's iterate()
function
is being called in each loop (and checked for error) before trying to
read the context state. If you check the example in the documentation
for the standard mainloop you'll also notice that this is done in
multiple places. You simply need to duplicate that bit at the start of
your loop.
Regards, Lyndon
On Sat, 2021-01-16 at 07:48 -0800, Ken wrote:
Hi :) Sorry for bringing back a closed issue, i actually have a similar problem, i can't really understand how to use data from the callbacks, especially for example with Introspector::get_source_info_list, this issue seems to be similar, but @doums didnt post the solution to his problem 😭. This is the example, most of the code comes from the example from the documentation, except the end where i try to list source devices. let spec = sample::Spec { format: sample::Format::S16le, channels: 2, rate: 48000, }; assert!(spec.is_valid());
let mut proplist = Proplist::new().unwrap(); proplist .set_str( libpulse_binding::proplist::properties::APPLICATION_NAME, "FooApp", ) .unwrap();
let mut mainloop = Rc::new(RefCell::new( Mainloop::new().expect("Failed to create mainloop"), ));
let mut context = Rc::new(RefCell::new( Context::new_with_proplist(mainloop.borrow().deref(), "FooAppContext", &proplist) .expect("Failed to create new context"), ));
context .borrow_mut() .connect(None, libpulse_binding::context::flags::NOFLAGS, None) .expect("Failed to connect context");
// Wait for context to be ready loop { match mainloop.borrowmut().iterate(false) { IterateResult::Quit() | IterateResult::Err() => { eprintln!("Iterate state was not success, quitting..."); panic!(); //return } IterateResult::Success() => {} } match context.borrow().get_state() { libpulse_binding::context::State::Ready => { break; } libpulse_binding::context::State::Failed | libpulsebinding::context::State::Terminated => { eprintln!("Context state failed/terminated, quitting..."); panic!(); //return } => {} } }
let introspector = Context::introspect(&*context.borrow()); let sources = Introspector::get_source_infolist(&introspector, || () ); //What do put in this callback ?
loop { match sources.get_state() { libpulse_binding::operation::State::Done => { break; } e => eprintln!("WTF ? {:?}", e), // This is always firing, so the callback never finishes ?? } } Thanks for you time; Cheers, — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.
I just sent you a response covering the main issue - lack of iterating the mainloop in your loop - but then upon taking a last look at your message before closing it I noticed that I'd overlooked aspects of the comments you left, so there's some more that I should say...
Firstly, your last comment ("This is always firing, so the callback
never finishes") demonstrates a misunderstanding connected to the lack
of mainloop iteration just mentioned. Perhaps pointing out the
iteration issue is sufficient, but perhaps not, so I'll just add that
it is not the case that your callback "never finishes", in fact your
callback never executes at all. As explained in the previous response,
by not using the mainloop's iterate()
function within your loop, you
never give the mainloop a chance to process messages from the PA
server, to thus receive the requested data, run the callback and update
the operation state.
You also asked "What do put in this callback". First of all you need to
understand the concept of what happens with "list" type callbacks -
your callback takes a ListResult
parameter, and it will be executed
once per item in the list, and then a final extra time to signal the
end of the list having been reached. So the first thing that your
callback needs to do is check which ListResult
variant the parameter
is, such that it can act appropriately. Once you've added this you
could then add a simple debug-print statement for items such that you
can see a copy of the data your callback is being given.
What to do next, to properly make use of the data, rather depends upon
what you want to do with the data. If simply printing some properties
is all you want then that's simple because you can probably just do
that directly within the callback. If you want to display it in a GUI,
you could make the relevant GUI object available within the callback.
If you want to move/copy some or all of the data out of the callback,
then this is of course possible but not especially easy; you'll need to
make use of Rc
and RefCell
around some type to hold the data in,
create a clone of that Rc
wrapped object, 'move' one instance into
the closure and use it within the closure to capture the data, and then
use the other instance outside of the closure to subsequently make use
of the data. Note that unfortunately no Clone
implementation exists
(currently) for types like SourceInfo
and you're only getting a
reference to one, so you cannot just move or copy the entire object
out, you'll have to copy the specific attributes you're interested in
(I may look at implementing Clone
soon, I'll add it to the to-do
list), and also that attributes like strings only live as long as the
execution of the callback, thus owned copies would need to be made if
moving/copying those.
Regards, Lyndon
On Sat, 2021-01-16 at 07:48 -0800, Ken wrote:
Hi :) Sorry for bringing back a closed issue, i actually have a similar problem, i can't really understand how to use data from the callbacks, especially for example with Introspector::get_source_info_list, this issue seems to be similar, but @doums didnt post the solution to his problem 😭. This is the example, most of the code comes from the example from the documentation, except the end where i try to list source devices. let spec = sample::Spec { format: sample::Format::S16le, channels: 2, rate: 48000, }; assert!(spec.is_valid());
let mut proplist = Proplist::new().unwrap(); proplist .set_str( libpulse_binding::proplist::properties::APPLICATION_NAME, "FooApp", ) .unwrap();
let mut mainloop = Rc::new(RefCell::new( Mainloop::new().expect("Failed to create mainloop"), ));
let mut context = Rc::new(RefCell::new( Context::new_with_proplist(mainloop.borrow().deref(), "FooAppContext", &proplist) .expect("Failed to create new context"), ));
context .borrow_mut() .connect(None, libpulse_binding::context::flags::NOFLAGS, None) .expect("Failed to connect context");
// Wait for context to be ready loop { match mainloop.borrowmut().iterate(false) { IterateResult::Quit() | IterateResult::Err() => { eprintln!("Iterate state was not success, quitting..."); panic!(); //return } IterateResult::Success() => {} } match context.borrow().get_state() { libpulse_binding::context::State::Ready => { break; } libpulse_binding::context::State::Failed | libpulsebinding::context::State::Terminated => { eprintln!("Context state failed/terminated, quitting..."); panic!(); //return } => {} } }
let introspector = Context::introspect(&*context.borrow()); let sources = Introspector::get_source_infolist(&introspector, || () ); //What do put in this callback ?
loop { match sources.get_state() { libpulse_binding::operation::State::Done => { break; } e => eprintln!("WTF ? {:?}", e), // This is always firing, so the callback never finishes ?? } } Thanks for you time; Cheers, — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.
Hi :) Thnks a lot for your answer, in the meantime i had delete the comment because i figured out what you explained about the mainloop. I'm sorry to have bothered you, i'm gratefull that your answered, i understand the problem much better now :) Thanks again :)
Best, Ken
Thanks This discussion was very helpful!
Hi, @Calder-Ty it's been ages since I asked but to let you know I finally ended by implementing that part of the code in C. Not that your binding was wrong, I really think it's nice ;) But I found out it will be better for me to use the C lib straight + FFI. Anyway, thx for your work on this binding and for your time answering my questions!
Hi,
First, thank for your amazing work! I have no previous experience with the PulseAudio C library and I'm just trying to use your binding to retrieve the informations of the sink (volume, muted). I started by using the standard loop (I have no clue if it's a good idea or not, for my simple needs). What I have done successfully is to create a context and connect it to the pulseaudio running server. Also I can subscribe to sink events and get the callback fired as expected. Now the problem is I fail to retrieve the sinks info. Either from the subscribe callback or from the context's introspection. In the doc there is no example for that (and I find it pretty complex for a beginner without some previous experience with PulseAudio). Also the relationship between the main loop and the context introspection/subscribed events is pretty vague.
Can you help me ?
EDIT: After a couple more hours of diving in the doc and the code, I finally got it. So all my question above are not relevant anymore.
Here is my simple code: