-
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
Port over Sounds.jl API functions #32
Comments
I kind of like being able specify the destination directly rather than needing a loadstreaming("dest.wav") do dest
[...] |> dest
end but that would require something like I went through the Sounds.jl docs to start thinking through the translation. Most of them seem pretty straightforward, I have questions on a couple. Sources
Processers
I think that when streams are composed with Of course that brings up the trickier question of what I'll have to spend some more time thinking about use-cases. |
Agreed. If that regression ever gets fixed, it would make sense to add a callable interface for the abstract type
Sure! I like that name.
I like
If I recall I think I miswrote the documentation in an earlier version, it's possible that is currently showing as the stable docs (that's embarrassing <_< ...). It's definitely implemented as
I could also imagine some approximations to this that wouldn't require the whole signal: e.g. the normalize the power over some window of time, or compute the power from 0 to time t (which you could compute incrementally at each frame) and normalize by that.
Ah, yes, I like that. I used to call it
It also occurs to me that this is really the same as
Yeah, that seems reasonable!
Wouldn't this just be |
Another little bit of brainstorming: The high level methods for filtering ( |
I am a little late to this conversation, so apologies if this feature is already available or discussed elsewhere. I was thinking it would be nice if the processing (including parameters) that was applied to a signal could be stored somewhere in the type. So you could query the signal to know how it has been treated. This can be useful when:
An example might be... Load signals and save to variables loadstreaming("source.wav") do src
src |> signal_unprocessed
src |> resample(8kHz) |> lowpass(2kHz) |> rampon(500ms) |> signal_processed
end You can currently prove the data like this samplerate(signal_unprocessed) # = 48kHz
samplerate(signal_processed) # = 8kHz It would be great if you could do something like processing(signal_unprocessed)
# Empty or None or [] or Dict{Any,Any} with 0 entries etc processing(signal_processed)
# Dict{String,Dict{String,String}} with 2 entries:
# "rampon" => Dict("RampLength"=>"500ms","RampFunction"=>"Linear")
# "lowpass" => Dict("CuttOff"=>"2kHz","Filter"=>"DSP.FilterObject")
# "resample" => Dict("SampleRate"=>"8kHz","Filter"=>"DSP.FilterObject") or maybe signal_processed.processing{1}
# "resample" => Dict("SampleRate"=>"8kHz","Filter"=>"DSP.FilterObject")
signal_processed.processing{2}
# "lowpass" => Dict("CuttOff"=>"2kHz","Filter"=>"DSP.FilterObject") And you could check if required processing has been completed as contains(processing(signal_unprocessed), "lowpass")
# false
contains(processing(signal_processed), "lowpass")
# true |
@codles: That's an interesting idea. What purpose would you imagine putting this to? I'm trying to think about what happens when you mix two signals in this setup. You can't just merge the steps applied into a one data structure, because they're only applied to part of the signal. So somehow you have to have the metadata apply to only part of the resulting signal (and how or whether that's worth expressing seems like it would depend on the intended purpose). A small technical point; I don't think this would work: loadstreaming("source.wav") do src
src |> signal_unprocessed
src |> resample(8kHz) |> lowpass(2kHz) |> rampon(500ms) |> signal_processed
end Because loadstreaming("source.wav") do src
signal_unprocessed = src
signal_processed = src |> resample(8kHz) |> lowpass(2kHz) |> rampon(500ms)
# do stuff with signal_unprocessed and signal_processed here...
end |
Nice, this is coming together!
What about having a 1-arg method that applies to both start and stop, and a 2-arg method where you specify both separately? So then if you want to apply a fade to one or the other you can just do something like
I think if we have some kind of windowing and time-varying gain then we're getting into more complex compression / loudness normalization that I think we can defer to the future. It's useful to have, but there's a lot of history and specification around loudness (e.g. the broadcast world has a bunch of specs) that doesn't need to be coupled to these changes. I think for now just having
Ah yes, you are totally right here. 🤦♂️.
Yeah, definitely (though generic filtering support could probably be pushed out to the future). There have been one or two times in the past where I've needed to submit PRs to DSP.jl to make sure that the functions use @codles I think that storing up metadata on the processing graph is out-of-scope for this refactor. I wouldn't want to complicate the API to support it, so if we do it I'd rather find a way to add it on top of the simple system. It also seems like there are a bunch of open questions about exactly how it would work. Can you propose it in a separate issue? |
Ah yes, that seems simpler, and still quite clear.
Absolutely. Seems reasonable as a first pass.
Ah yes: it may be better to submit a PR there. I think there are still some cases that don't work, but maybe that's changed. |
Note that #44 is the first step to addressing this issue. |
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.
Sounds.jl has a nice API for building and manipulating sounds, which is currently focused on buffers, but seems like it would also be useful for streaming, e.g. one could put together a quick on-line filtering pipeline:
Or if you have a gigantic (larger than memory) audio file that you wanted to filter you could do:
Hopefully pretty soon
loadsteaming
andsavestreaming
into FileIO.jl. We'd need to define a method to make dest a callable to enable this API, or else use a different operator than|>
. doingrampoff
streaming would have to add latency as long as the fade time (so it could start applying the fade when its input stream ends), but that's not a deal-breaker and is a really nice API. AudioIO.jl might be the right place for most of the Sound.jl functions to live (most of them don't seem particularly psychoacoustic-specific, and are nice general purpose audio processing)from @haberdashPI
followup from @haberdashPI
The text was updated successfully, but these errors were encountered: