Conversation
|
|
||
| /// A buffer of samples treated as a source. | ||
| #[derive(Debug, Clone)] | ||
| pub struct SamplesBuffer<const SR: u32, const CH: u16> { |
There was a problem hiding this comment.
Can we use the SampleRate and ChannelCount type aliases?
There was a problem hiding this comment.
Sadly not, const params are limited to primitive types atm :(
| @@ -0,0 +1,20 @@ | |||
| //! Code shared between source types. | |||
| //! | |||
| //! Since we have three source types there is a lot of code duplication. We | |||
There was a problem hiding this comment.
I thought this shouldn't be user-facing Rustdoc when I saw it probably isn't, because the module isn't imported as public. Still, may want to consider the Rustdoc level.
There was a problem hiding this comment.
It's still useful to use doc comments instead of simple comments as rust-analyzer makes hover doc available for this when working on Rodio.
| impl<const SR: u32, const CH: u16> SamplesBuffer<SR, CH> { | ||
| /// Builds a new `SamplesBuffer`. | ||
| /// | ||
| /// Note any call to total_duration will panic if the buffer is larger then |
There was a problem hiding this comment.
I see you moved that code from new to total_duration which probably makes sense. But I'd say it doesn't need docs here then.
| where | ||
| D: Into<Vec<Sample>>, | ||
| { | ||
| const { assert!(SR > 0) }; |
There was a problem hiding this comment.
Not too familiar with const generics; I guess we can't use NonZero as we wanted?
There was a problem hiding this comment.
yes, pretty sad. One day we'll get rustc there. But thanks to the const block that assert will fire compile time.
| .map_err(|e| SeekError::Other(e)) | ||
| } else { | ||
| self.second | ||
| .try_seek(pos) |
There was a problem hiding this comment.
I think this needs to be pos - first. As-is, chaining two 1-second sources and seeking to 1.5s puts the second source at 1.5s, which is past its own end.
There was a problem hiding this comment.
This still needs try_seek, size_hint and ExactSizeIterator implemented.
|
|
||
| #[derive(Debug, thiserror::Error)] | ||
| pub enum ChainSeekError { | ||
| #[error("Could not get duration of first source ({ty}")] |
| use crate::source::SeekError; | ||
|
|
||
| #[derive(Debug, thiserror::Error)] | ||
| pub enum ChainSeekError { |
There was a problem hiding this comment.
Should this be exported publicly so users can downcast to them from a SeekError::Other(Arc<dyn Error>)?
There was a problem hiding this comment.
It should be re-exported in the source implementation as wel. See the: pub use crate::common::source::chain::ChainSeekError; in const_source/chain.rs
| pub enum ChainSeekError { | ||
| #[error("Could not get duration of first source ({ty}")] | ||
| NoTotalDurationForFirst { ty: &'static str }, | ||
| #[error("Could not seek in first source")] |
There was a problem hiding this comment.
Add ({ty}) here too? And below.
| type Item = crate::Sample; | ||
|
|
||
| fn next(&mut self) -> Option<Self::Item> { | ||
| Some(0.0) |
|
Thanks for the review! Should have addressed all of the feedback. I'll see if I can fix the Phase1b tonight. |
|
OK, I see channel and seeking precision bugs I saw yesterday, are already fixed :) |
|
First of all thanks for finding the time for the review! Secondly, I did not have the time to write a short answer so you get a very long one, apologies.
There are going to be even more macro calls when we get to implementing all the effects... Lets see what we can do: TraitsI've tried doing something with traits, separating out the repeated parts: trait FixedSource {
fn sample_rate(&self) -> SampleRate;
fn channel_count(&self) -> ChannelCount;
fn total_duration(&self) -> Duration;
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError>;
}
trait ConstSource {
fn sample_rate(&self) -> SampleRate;
fn channel_count(&self) -> ChannelCount;
fn total_duration(&self) -> Duration;
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError>;
}We would have: trait FixedSource: TotalDuration + TrySeek {
fn sample_rate(&self) -> SampleRate;
fn channel_count(&self) -> ChannelCount;
}
trait ConstSource: TotalDuration + TrySeek {
fn sample_rate(&self) -> SampleRate;
fn channel_count(&self) -> ChannelCount;
}
trait TotalDuration {
fn total_duration(&self) -> Duration;
}
trait TrySeek {
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError>;
}Now we can implement TotalDuration and TrySeek separately. This does not win us anything unfortunately, we still need a separate object for Marker traitWhy is You'd do: impl FixedSource for Amplify {
fn sample_rate() -> SampleRate {
...
}
...
}
impl<SR, CH> ConstSource for Amplify<SR, CH>;Well the reason for that is we do not want any type to implement both Wrappingstruct Amplify(fixed_source::Amplify);
impl<SR, CH> ConstSource<SR, CH> for Amplify {
fn total_duration(&self) -> Duration {
self.0.total_duration()
}
...
}That seems nice, no more code duplication and no more macro's. But then we realize that S will probably wrap some existing source (like chain does or a future amplify (in phase 3a). But fixed_source::S can only be generic over a // Error: argument T to fixed_source::Amplify must implement FixedSource
struct Amplify<T: ConstSource>(fixed_source::Amplify<T>); Well for that we have the IntoFixedSource wrapper! So we get: struct Amplify<T>(fixed_source::Amplify<IntoFixedSource<T>);
impl<SR, CH> ConstSource<SR, CH> for Amplify {
fn total_duration(&self) -> Duration {
self.0.total_duration()
}
...
}I don't like that this leads to a lot of IntoFixedSource's in there but those should compile out... I have to think about it. What do you all think? |
supersedes #902
Tracking issue: #901
Very minimal still, to get an idea of what this will all look like see: rodio-experiments.
Implementation notes:
include_str!those to prevent duplication. We'll be using that even more for the effects.