-
Notifications
You must be signed in to change notification settings - Fork 64
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
Monitoring Signal
s from different threads of varying rates.
#88
Comments
I can think of a couple different behaviours that one might want when sharing the output of a signal across multiple threads:
|
Requirements
I imagine the signal chain might look something like this across the threads:
The API might look similar to the way that let signal = signal.monitor(source_rate);
let output = signal.send(rate_interpolator, initial_target_rate, channel_buffer_size);
// Requesting frames on audio thread
signal.next()
// Other thread
output.next() where:
The target sample rate could be dynamically calculated by counting the number of frames that are requested from SIGNAL_A for each frame requested by SIGNAL_B and then dividing the known sample rate of SIGNAL_A by the result. This frame "count" should probably be averaged over some window size before dividing the original sample rate in order to avoid fluctuations in the case that either SIGNAL_A or B are buffered and occasionally request many frames at once. Questions
|
This paper seems to solve this problem but w.r.t. mapping time from "sample time" to "system time". |
Well I can't answer all your questions right now, but at least I can say that you can implement a very simple This forces you to think more about issues of synchrony when combining signals, but I think this is a good discipline. |
Thanks for sharing! This sounds incredibly similar to the 'Signal::fork' PR
I opened yesterday, except that it allows for sharing a bounded ring buffer
between the two nodes (which may be stack or heap allocated) and both
branches behave the same. It offers sharing the
RefCell<ring_buffer::Bounded> via either Rc or reference (not sure how
ergonomic the latter will be in practise though yet due to the lifetime).
I'd link but am replying via email on phone, will try to remember to later
:+1:
…On Wed, 22 Nov 2017 at 1:23 am, alcofribas ***@***.***> wrote:
Well I can't answer all your questions right now, but at least I can say
that you can implement a very simple bus-like method that inputs a Signal
S and produces a pair (Sampled(S), Aux) of Signals, where Aux contains an
Rc<RefCell<S::Frame>>, and Sampled's next() method just writes S.next()
to that RefCell and then outputs it. Hmm, maybe you don't even need to wrap
it in an Rc, I'm not sure. That is, you have created a buffer of length
one; then you can read Aux at any rate you want; it's guaranteed to give
the "current", or "last" value of S::Frame. This is what I call an
Auxiliary signal in my framework.
This forces you to think more about issues of synchrony when combining
signals, but I think this is a good discipline.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#88 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AEX_bWpWhRehNA-imu0SIlvn_E3LTUuQks5s4ty8gaJpZM4Qee6j>
.
|
Well I have a lot of Signal structs that have &'d structs inside of them, and the only bother is that your refs always have to come "from outside". This forces you to use macros, for example if you want to construct a complex UGen by assembling several smaller ones, or if you want to feed a bundle of several refs to a complex Signal struct, which is something you want to do in one fell swoop, not all by hand. |
Hey, chiming in way late -- what I usually do is almost exactly what @quatrezoneilles described, except I just use Anyway -- I'm a little sketchy on whether you're looking to send all of the frames from one thread to another, or if you'd rather just poll the latest frame whenever. My preference would be to have something like |
Thanks for the input @andrewcsmith! I've also been doing something similar for GUI monitoring but using a crossbeam queue instead with a bit of wrapper code that recycles the buffers between sender and receiver. I guess the thing I'd really like to solve is how to read from a signal from two different threads, where both signals do not drop any data. E.g. the two threads involved might be two different audio devices where samples are requested from two separate callbacks. Although we might be able to set the two devices to the same sample rate, I'd prefer not to rely on this as the physical clocks will likely drift from one another over time. We can't drop frames in this case as this will cause glitching in the output of the late device. As an alternative to dropping frames, I'd like to work out some nice way of using adaptive sample rate interpolation to synchronise the rate at which samples are requested from the signal, while still ensuring that the two (or more) outputs receive as many samples as they request. Further, I was thinking that perhaps if we could solve this in a robust, "generalised" manner (e.g. supporting widely varying sample rates) we might also be able to use this solution as an alternative way of monitoring the audio thread from the GUI thread. |
Ah, okay, so just so I understand, the sample rate difference might not even matter. We could be talking about the same sample rate with a different block size, correct? I wonder if you could make a ring buffer-like queue (spsc) but with more than one I'll phone a friend and get back to you... |
I often come across the desire to monitor a
Signal
that is running on the audio thread, from the GUI thread. Normally the kinds of signals I want to monitor are control signals (likePeak
orRms
) generated by an adaptor around some signal.I normally end up designing a custom
Signal
adaptor to do this depending on the task.I often want to sample the signal I'm monitoring at a much lower rate than the audio sample rate. E.g. if I want to monitor a 44100hz audio signal in a GUI that is running at 60hz, I only want a "snapshot" of the audio signal every 735 audio frames as I can't physically see the monitored values faster than this anyway.
This rate issue is a large contributor to the awkwardness involved with these monitoring implementations and I think could possibly be addressed at a lower level. That is, it would be nice to have abstractions for safely and predictably pulling values from a
Signal
at varying rates.Signal::bus
could be considered a step in this direction, but has caveats. Firstly,Bus
is designed for use on a single thread (it usesRc
andRefCell
internally for sharing the ring buffer between output nodes). Secondly, when requesting from aBus
'sOutput
nodes at different rates, the internal ring buffer will grow infinitely large with values that have not yet been collected by the slowerOutput
.One could work around the second issue by reducing the sample rate (e.g. calling
Signal::from_hz_to_hz
) on the slowerOutput
node to the rate at which it should be called. This would go a long way to fixing the issue, but the same problem will occur if the rate is inconsistent/unreliable or if some drift occurs (which will almost always be the case if frames are being requested from different threads). This same problem applies when a user requires outputting a single audio signal to more than one audio hardware device.The text was updated successfully, but these errors were encountered: