-
Notifications
You must be signed in to change notification settings - Fork 7
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
Migrating to RingBuffers.jl #7
Comments
When I just did a Pkg.clone(...) of
After a little digging, this makes sense given that this commit is labeled "rename SingleAsyncWork to AsyncCondition, JuliaLang/julia@eaf1ba5 So the requirements are pretty sensible. If I get this working on 0.6.1, I'll submit a PR. |
IIRC The real solution I think is to port over JACKAudio to use RingBuffers.jl, which provides a thread-safe lockfree ringbuffer that can be called from C or Julia. I use it in PortAudio.jl and it has cleaned up things pretty substantially. |
Yeah, I think the overall architecture would be very similar to how PortAudio.jl is set up. There'd be a That's definitely one likely point of confusion - RingBuffers.jl is independent of PortAudio, even though it uses the PortAudio ringbuffer implementation (which is shipped as a separate library). So a hypothetical |
Thanks for the explanation. That makes sense I'm taking a shot at this here but it's no where near ready. |
That's awesome, thanks! Let me know if you have any questions or want me to check anything out. |
So, jack typically has a "channel buffer" model, where each input and output channel has its own buffer. portaudio has a "multichannel interleaved buffer" model, where all input (and output) channels are transported as a single, interleaved buffer. Given that, it makes sense that For The downside seems like RingBuffer.jl wouldn't have the right convenience functions, but maybe they could be added. Thoughts? |
I think in general a ringbuf-per-channel approach would work well, and then you don't have to worry about interleaving/deinterleaving on both sides of the ring buffer. One potential gotcha would be making sure that the channels are always in sync, particularly after an overflow or underflow. The issue could pop up if the JACK callback gets called while the Julia side is in the middle of processing and has maybe processed some channels but not others. You could get into a state where the channels processed before the JACK callback underflowed/overflowed but the channels processed afterwards were fine, so the channels are now de-synced. Doing an initial pass to determine worst case readability/writability should mitigate the problem. I think whenever reading or writing to the collection of ringbuffers you'd want to first go through the list of them and find the worst-case data/space availability, then go through and read/write that number of samples. |
@ssfrr Great points. I think
|
Looking good so far, thanks for taking this on! It looks like you've got the gist of That's also related to your question about whether it's too spammy to notify on each over/underflow - it might be a good enough heuristic that if Your question about when to notify the Julia side is a good one, and I'm not 100% sure of the right answer right now. I'd think you'd only want to notify after reading/writing all of the channels, but that might mess up some of the task blocking logic on the Julia side. On the other hand, maybe if you process the channels in the same order on the Julia and C sides, than the task wake-up might actually work as expected. Not sure if it would cause more context-switches than necessary though... Hopefully the answer will present itself. 😄 |
I'm a bit naive about REPL use cases, but would like to know more. My intended application for some of these changes is to allow for easier coding of "long running" clients that will run for hours or days without interruption. For that kind of use case, I added an Right now, I either make "long running clients" in C or Python. Both have their benefits, but I think JACK clients written in Julia could be the best of both worlds. I'm reading through
|
Rather than structuring the application around a processing callback function, the JuliaAudio packages are stream-oriented, so a long-running process would look like: # set up for stereo IO, also promise that we'll make sure to read the same number
# of frames as we write, and in exchange JACKAudio will make sure the ringbuffer
# latency is deterministic
client = JACKClient("Processor", 2, 2, synced=true)
# start by reading a block of audio, so the samplerate, element type, etc. all match
# the source this will be a `SampleBuf`, which is basically a samplerate-aware
# wrapper around an len x chan array
buf = read(client, 4096)
while true
super_advanced_dsp_process!(buf) # this is where you do your stuff
write(client, buf)
read!(client, buf)
end If you want separate input and output buffers to work with you could pre-allocate an output buffer as well (creating the The idea with REPL usage is just that I want people to be able to do things like open a |
@ssfrr That makes sense. I'll work towards that type of interface. Out of curiosity, I'm looking at the code, and I see that
But I don't see where |
UPDATE: Nevermind, I just wasn't linking jack_shim.o against libjack. Now I just get a segfault. Everything is going according to plan. :-) Original post:
And ultimately Julia quits with the following information:
Which is odd, given that in Julia, I have the following behavior...
@ssfrr Have you ever seen something like this where the .so files get "mixed up"? I get this behavior as of this commit |
Ah, fantastic, glad you're back to the expected segfaults. 😄 Regarding your previous question, there's an abstract implemention in SampledSignals.jl that handles reading/writing from sources/sinks (respectively). The call works its way through the samplerate/channelcount conversion machinery (if needed) and ends up in calls to |
This might be intended, but I just noticed this behavior:
The text was updated successfully, but these errors were encountered: