Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable buffer size #512

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

PetrGlad
Copy link

@PetrGlad PetrGlad commented Oct 1, 2023

I needed to configure smaller buffer size to reduce playback delays (for a MIDI/VST application), and could not find a better way than to patch rodio. Notes:

  • This is a breaking change, I needed to pass the buffer size to new_output_stream_with_format, it is likely possible to rewrite this, keeping that function intact if necessary.
  • Using references to pass parameters is not strictly required, but the stream constructor does not need to own them, and it made it a bit easier to required some parameters after calling
  • SampleFormat probably should be a part of StreamConfig. This will streamline the construction parameters. It is not clear to me what this affects, though.

The version from this patch works for me. I am willing to rework the request if there is a better idea how to do this.

@dvdsk dvdsk added the breaking Proposed change that would break the public API label Mar 27, 2024
@vyfor vyfor mentioned this pull request Jul 4, 2024
sample_format: &SampleFormat,
) -> Result<(Self, OutputStreamHandle), StreamError> {
let (mixer, _stream) = device.try_new_output_stream(&config, &sample_format)?;
_stream.play()?;
Copy link

@AsPulse AsPulse Sep 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not a maintainer, but one of the users of rodio, though, what a wonderful pull request!!!
I had the opposite of your case, a problem with regeneration due to lack of buffer, but thanks to you I was able to solve it.
One thing I noticed is that this _stream.play()? is necessary?

Suggested change
_stream.play()?;

I removed it and tried it (in this fork), but it works without any problems and the time it takes has become shorter.

Correction, it worked fine on macos and Linux, but on Windows it would not play without this code. Sorry for the accusation.
If you don't mind, I would be glad to know why this line is necessary.

Copy link
Author

@PetrGlad PetrGlad Sep 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried to follow existing patterns, and make minimal changes to pass the new parameter to the point where the stream is initialized. For example, I do not like underscored names there, since normally that indicates unused variable, but kept it to mimic existing style :) Other similar functions also use open() after initialization. See for example stream::try_from_device_config(...).
It looks like what play() does depends on the platform/back-end implementation. The trait containing it is part of cpal library. The declaration doc says

Note: Not all platforms automatically run the stream upon creation, so it is important to call play after creation if it is expected that the stream should run immediately.

@dvdsk
Copy link
Collaborator

dvdsk commented Sep 15, 2024

Just to give you all an update, configurable buffer size will be merged one day. Its just that I have my plate full atm and want to do as many breaking changes at once, such that I can write a porting/update guide and do an announcement on the various platforms. Before I do that I want to overhaul

  • The stream being returned (needing to keep a variable you do not use is annoying and a rodio footgun).
  • The Sink only processing changes every 5ms
  • The compile time features are confusing (no clear distinction between containers & codecs)

And that will take a while once I have the time or someone else pitches in. Therefore thanks @PetrGlad for this PR, I suggest anyone who urgently needs this change to rely on their fork or this PR for now.

@dvdsk
Copy link
Collaborator

dvdsk commented Sep 16, 2024

@PetrGlad I've been listing all the breaking changes planned. What is your opinion on replacing all the try_from factory functions with a single builder?

@PetrGlad
Copy link
Author

@dvdsk Yes, this looks to me like a good idea, especially if the builder instance can be copied/passed around. In my case I was happy with the defaults and only wanted to change the buffer size. In that case a builder can help setting the defaults. Maybe the builder should have methods that can provide replacements for OutputStream::try_from_device (something like ConfigBuilder::from(conf: SupportedStreamConfig)):

let conf_builder = rodio::OutputConfigBuilder::from(cpal_suported_config)
    .buffer_size(13);
let output = rodio::OutputStream::open(conf_builder); 

or

let conf = rodio::OutputConfigBuilder::from(cpal_suported_config)
    .buffer_size(13)
    .build();
let output = rodio::OutputStream::open(conf); 

Another option could be to add all possible options to some config struct in rodio and implement Default, From<cpal::SupportedStreamConfig>, and From<cpal::StreamConfig> for it to fill defaults, and let users to adjust this struct's fields before they pass it to the stream initialization function. I think of the necessary parameters only stream's sample format is not part of cpal::StreamConfig. If there are not too many parameters this could be simpler than builder.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Proposed change that would break the public API
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants