-
Notifications
You must be signed in to change notification settings - Fork 25
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
Explore promotion mechanism #30
Comments
I think our goals are the same, in the sense that buffers (and streams in my case) should be easy to mix-and-match. As you mention, they might vary in channel count, element type, sample rate or length. I think the main addition for me is that I want it to be pretty easy for other packages to implement their own source and sink types.
One issue with this behavior is if you're multiplying by an envelope that fades out at the ends - with this scheme you end up with a discontinuous jump to
I think it's useful to support fractional sampling rates.
I think this is a really nice idea, and could get the benefits of moving format info into the type without creating a burden for extensibility. Maybe something like: struct SampleFormat{T, SR, CH}
function SampleFormat{T, SR, CH}() where {T, SR, CH}
T isa Type{<:Number} || throw(ArgumentError("T must be a numerical type"))
SR isa Float64 || throw(ArgumentError("SR must be a Float64"))
CH isa Int || throw(ArgumentError("CH must be an Int"))
new{T, SR, CH}()
end
end
function SampleFormat(eltype::Type{<:Number}, samplerate::Real, nchannels::Real)
SampleFormat{eltype, convert(Float64, samplerate), convert(Int, nchannels)}()
end Definitely worth investigating. Another issue to worry about here is how to represent 1-channel buffers. Currently I match the I don't think it's inevitable that all the conversion goes through streams as it does right now in SampledSignals. For channel count and type conversion, there's no state to the conversion so it could be implemented in terms of buffers and then the stream machinery could use those conversions. Where it gets tricky is samplerate conversion, where in the streaming case you need to maintain state from one chunk to the next, so that sort of needs to be implemented with streams in mind. That doesn't necessarily mean that the buffer samplerate conversion needs to invoke the stream machinery though, it could just have a parallel resampling that uses |
Ah, yes. Good point. I like the idea of having it pad out the last value, with a keyword option to change the padding strategy.
Fair enough! Seems reasonable. Those examples help. Thanks. I like your proposed struct SampleFormat{T, SR, CH, MX}
function SampleFormat{T, SR, CH,MX}() where {T, SR, CH,MX}
T isa Type{<:Number} || throw(ArgumentError("T must be a numerical type"))
SR isa Float64 || throw(ArgumentError("SR must be a Float64"))
CH isa Int || throw(ArgumentError("CH must be an Int"))
MX isa Symbol || thorw(ArugumentError("MX must be a Symbol")
new{T, SR, CH}()
end
end
function SampleFormat(eltype::Type{<:Number}, samplerate::Real, nchannels::Real, mixing_strategy::Symbol=:automatic)
SampleFormat{eltype, convert(Float64, samplerate), convert(Int, nchannels),mixing_strategy}()
end
Yes. I ran into this same exact issue, and ended up with the same strategy, specifying the number of channels (C) and the number of dimensions (N) as seperate type parameters (where C must be 1 if N == 1). I see how this might be a bit more tricky to handle if C is part of One very finicky case is the situation where you want a single sample of multiple channels. The default array interface would return a Cx1 array (where C is the number of channels). As I'm handling things now, I always assume the first dimension is time, so I return a 1xC array.
Indeed. It's a bit more work for the streams, but it seems like one should be able to call, e.g. |
Just thinking about where to start, and it seems like a good first step would be working something out for this, and then implementing |
Sounds good. I'm not 100% sold that the mixing strategy needs to be part of the type. It seems like extra complexity and I'm not sure there's a big benefit. I think that a default behavior of:
I agree indexing a single frame ( |
hmm, there's also an argument that the samplerate is pretty meaningless with a single frame of audio, so there might be an argument for duplicating needs a little more thought. |
Sure! That seems like a reasonable behavior. I'm thinking there well be some well defined interface to the format type (e.g. I'll start fleshing this out and make a pull request. |
👍 for a function interface that hides away the type parameter specifics. Looking forward to seeing what you come up with! |
I'm splitting out the various discussions from #29 so they're easier to keep track of separately. Sorry some of the formatting isn't as pretty, I opted for clarify in who said what. Please don't hesitate to let me know if I missed something important.
In an earlier version of SampledSignals I put the sample rate and channel count as type parameters, but after working with it for a while it seemed like more trouble than it was worth. In general with my Julia code I've found that type parameters are great when you need them for type stability, but trying to move too much logic into the type system gets messy and regular run-time branches on field values are simpler.
It's been a while since I made the switch, but some issues I remember were:
That said, there could definitely be a way to improve how I'm doing things right now with doing all my conversion in the source/sink domain. In particular precompile times are pretty long with SampledSignals, which I suspect might have to do with my recursive conversion machinery. Having another set of eyeballs and other use-cases would be helpful in iterating the design.
From @haberdashPI:
followup from @haberdashPI
The text was updated successfully, but these errors were encountered: