Refactored to have apply_volume in a specifix mixer#148
Refactored to have apply_volume in a specifix mixer#148plietar merged 18 commits intoplietar:masterfrom romerod:feature/mixer
Conversation
|
Thanks for working on this, this is an often requested feature. I don't think the API as it is now would allow for hardware mixers. The The best is probably to merge AudioBackend and Mixer. They are closely linked. AudioBackend's which don't support hardware mixing can just fallback to software as it is now. |
|
I know that apply_volume doesn't make sense in any but the SoftwareMixer case, that's way the default implementation just returns the unaltered data. I don't like the idea of putting the audio backend and the mixer together. I've the same use case as @herrernst in #43 where volume will be controlled on an amplifier which I'll remote control with CEC. Having them separated makes it possible to run different mixers with different backend. Having the possibility to delegate the "mixing" to an external process would make it possible to use amixer for example without the need to manually implement that. |
|
Right, I hadn't thought of that use case and it makes more sense now. Thanks for clarifying. I think the API is reasonable like that. It may make sense the split the
I think this should be left for other PRs. I also have a refactor coming up which would impact the way these are done. I don't have a candidate for IPC. Is there a precedent for volume control ? Is writing the volume to the process' stdin enough ? |
src/mixer/mod.rs
Outdated
src/mixer/mod.rs
Outdated
There was a problem hiding this comment.
Is this necessary ? Initialisation can happen when constructing the Mixer object
src/mixer/mod.rs
Outdated
There was a problem hiding this comment.
start/stop seem like better names
|
I've started splitting it up a bit differently. I introduced the StreamEditor trait which can be optionally passed to the Player. The current volume is owned by the Mixer. Are you ok with this? Currently the state of the device isn't read anymore by the SpircManager [WIP]. |
There was a problem hiding this comment.
Overall looks good, couple of nits.
AudioFilter sounds like a better name than StreamEditor. Maybe one day we can have other type of filters.
I'm confused about the changes in src/spirc.rs and why they're needed, beyond just adding a mixer: Box<Mixer + Send> to the SpircManager and calling it when appropriate.
Finally, a device can disable volume changing (probably by not advertising the kVolumeSteps in spirc). This could be done if none is used as a mixer.
That can be left for another PR though.
src/main.rs
Outdated
There was a problem hiding this comment.
mixer::find should take an Option<&str>, and take care of choosing a default rather than picking it here, similar to how the audio backend is done.
src/main.rs
Outdated
There was a problem hiding this comment.
Use .expect("Invalid mixer") so it is more obvious in case of an error
src/main.rs
Outdated
There was a problem hiding this comment.
Move mixer.get_stream_editor() to a variable
src/mixer/mod.rs
Outdated
There was a problem hiding this comment.
Brace should go at the end of the line.
If the line is too long, use
fn get_stream_editor(&self)
-> Option<Box<StreamEditor + Send>>
{
src/mixer/mod.rs
Outdated
src/mixer/softmixer.rs
Outdated
There was a problem hiding this comment.
Just store the Arc<RwLock<u16>> in there, no need for a closure
There was a problem hiding this comment.
There is no reason to allow the SoftVolumeApplier to modify the value, i was looking to be able to get a ReadLock from the RwLock but didn't find one. That's why i decided to use the closure
There was a problem hiding this comment.
The privacy boundary is usually the module. As long as it isn't possible to modify the volume unexpectedly from outside the module, that's fine.
I find the closure a lot more confusing than storing the Arc<RwLock<u16>>
src/mixer/softmixer.rs
Outdated
src/player.rs
Outdated
There was a problem hiding this comment.
It's tied to the changes i made in spirc, there is a new struct "State" which contains the whole system state (Player and Mixer) so I thought the update_time belongs to the State not the PlayerState
src/player.rs
Outdated
There was a problem hiding this comment.
This could just be editor.modify_stream(&mut packet.data) to modify in place.
(See my comment above)
src/spirc.rs
Outdated
There was a problem hiding this comment.
see my previous answer about why I removed the update_time, I think when I continue with the changes it will become clear
plietar
left a comment
There was a problem hiding this comment.
Why do you need a channel to send volume change updates back to the SpircManager ?
Volume change is only done by spirc anyway, so you can just do self.notify(false, None); when a kMessageTypeVolume message has been received.
There shouldn't be any changes needed to mercury, and it should definitely not depend on the spirc module.
I'm also not sure what the implement_sender macro does and why it's needed.
I'm also still not convinced about why the State struct in player is needed, and why this existing code wasn't sufficient.
Sorry for requesting all these changes, but I found the implementation very confusing and overly complicated.
src/player.rs
Outdated
There was a problem hiding this comment.
Hmm, using Some(OK(mut packet)) should be enough to avoid the copy.
There was a problem hiding this comment.
I will try that, thanks
|
There are multiple reasons I used a channel to send volume change notifications:
I know I modified quite a lot of things, but I didn't want to introduce additional threads and weak pointers. The dependencies between spirc and mercury were less than ideal, I know, i'd suggest something like the last commit, what do you think? It might be cleaner to move the SpircMessage enum back to spirc, but then the macro would need to create a trait which would need to get boxed to be passed arround etc. I thought it is simpler like that. Don't worry about asking to much changes, the idea is to get something good out of it, right? The state part is not finished, TODO on line 435 of spirc. |
…ge types to one channel
…Sender implemented by macro
|
Ok the changes to channel stuff make more sense. It's a shame the I suggest leaving out the volume updates from this PR, especially since it is not needed for software volume. It can be implemented in a much cleaner way once I'm done with the refactor. |
|
Hmm, looked at the select! macro, but I don't think it serializes incoming messages as the solution does so you would still have to use locks, which make it much more difficult to know at what point who has to wait on what. Do you see the possible simplification of passing a PlayerStateSender to the instead of the observable stuff and removing the Arc<Mutex<>>? |
|
I removed that part of the code (it hurts a bit ;-) ). As long as the player is playing changes in the mixer will still be reflected as updates are sent to the server, so I can live with that. |
|
Is there something missing? |
|
@plietar pinging incase of forgotten PR. |
plietar
left a comment
There was a problem hiding this comment.
Sorry, I thought this was still in WIP.
I still don't understand why you need an extra State type ?
|
|
||
| pub fn find<T: AsRef<str>>(name: Option<T>) -> Option<Box<Mixer + Send>> { | ||
| match name { | ||
| _ => Some(Box::new(SoftMixer::new())), |
There was a problem hiding this comment.
Only return SoftMixer if soft was given as an argument (or None for default).
|
|
||
| impl Player { | ||
| pub fn new<F>(session: Session, sink_builder: F) -> Player | ||
| pub fn new<F>(session: Session, stream_editor: Option<Box<AudioFilter + Send>>, sink_builder: F) -> Player |
There was a problem hiding this comment.
Rename the variable to audio_filter
|
|
||
| impl PlayerInternal { | ||
| fn run(self, mut sink: Box<Sink>) { | ||
| fn run(self, mut sink: Box<Sink>, stream_editor: Option<Box<AudioFilter + Send>>) { |
|
I've made a few fixes and merged. Thanks @romerod |
|
Cool, thank you |
core API review changes.
This is still work in progress, are you interested in having multiple mixer support?
I started by refactoring the code to have a Mixer trait which is passed to Player.
I verified the code on rpi2 and it works, i'm a complete rust newb so don't hang me for weird code.
TODO: