-
Notifications
You must be signed in to change notification settings - Fork 16
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
Q/FR: how to do mix-down with an array of panning signals, instead of a spread? (ChannelPanner to generic mixer) #127
Comments
Hi @balintlaczko, Nice use of the multichannel functionality!
Other than that, I think that the code is nice and concise, not sure if it needs much further improvement... Upgrading This also makes me notice that, in general, SignalFlow needs better documentation of which inputs can (or must) be mono, multichannel, or scalar... |
Thanks, sum works! Now that that loop is out, it looks much nicer! class Mixer(sf.Patch):
def __init__(self, input_sig, pan_sig, num_channels=2):
super().__init__()
assert input_sig.num_output_channels == pan_sig.num_output_channels
n = input_sig.num_output_channels
panner = [sf.ChannelPanner(num_channels, input_sig[i] / n, pan_sig[i]) for i in range(n)]
_sum = sf.Sum(panner)
self.set_output(_sum) This is more than enough for my purpose now.
I was now working on an "upmixer" that would linearly interpolate the input channels to the target number of channels. So if the input is a 2-ch signal (let's say with the constants [0, 1]), then the samples would be linearly interpolated in a 5-channel output (e.g. [0, 0.25, 0.5, 0.75, 1]). But I must be doing something wrong, because it crashes the ipython kernel every time I'm trying to use it. class UpMixer(sf.Patch):
def __init__(self, input_sig, num_channels=5):
super().__init__()
n = input_sig.num_output_channels # e.g. 2
output_x = np.linspace(0, n-1, num_channels) # e.g. [0, 0.25, 0.5, 0.75, 1]
upmixed_list = []
for i in range(num_channels):
output_i = output_x[i]
a = input_sig[int(output_i)]
b = input_sig[int(output_i) + 1]
frac = float(output_i - int(output_i))
interp = sf.WetDry(a, b, sf.Constant(frac)) # linear interpolation
upmixed_list.append(interp)
out = sf.ChannelArray(upmixed_list)
self.set_output(out) Whoops, update, I found the error, went too far with the index. Here is the correct one: class UpMixer(sf.Patch):
def __init__(self, input_sig, num_channels=5):
super().__init__()
n = input_sig.num_output_channels # e.g. 2
output_x = np.linspace(0, n-1, num_channels) # e.g. [0, 0.25, 0.5, 0.75, 1]
upmixed_list = []
for i in range(num_channels - 1):
output_i = output_x[i]
a = input_sig[int(output_i)]
b = input_sig[int(output_i) + 1]
frac = float(output_i - int(output_i))
interp = sf.WetDry(a, b, sf.Constant(frac))
upmixed_list.append(interp)
# add the last channel
upmixed_list.append(input_sig[n-1])
out = sf.ChannelArray(upmixed_list)
self.set_output(out) Update again, it only seems to work if I sum together a list of ChannelPanners instead. Not sure why. class UpMixer(sf.Patch):
def __init__(self, input_sig, out_channels=5):
super().__init__()
n = input_sig.num_output_channels # e.g. 2
output_x = np.linspace(0, n-1, out_channels) # e.g. [0, 0.25, 0.5, 0.75, 1]
output_y = output_x * (out_channels - 1) # e.g. [0, 1, 2, 3, 4]
upmixed_list = [sf.WetDry(input_sig[int(output_i)], input_sig[int(output_i) + 1], float(output_i - int(output_i))) for output_i in output_x[:-1]]
upmixed_list.append(input_sig[n-1])
expanded_list = [sf.ChannelPanner(out_channels, upmixed_list[i], float(output_y[i])) for i in range(out_channels)]
_out = sf.Sum(expanded_list)
self.set_output(_out) |
Diagnosing crashes is annoyingly difficult right now :-( It is possible by making a debug build, but painful... |
Although it's not well-documented (and possibly not fully tested), |
Thanks for the tip! I checked it now, it seems to fill the new in-between channels with zeros, but it correctly places the input values across the new channel-dim: b = sf.Buffer(3, 1)
b.data[:, :] = np.ones_like(b.data) * np.linspace(0, 1, 3).reshape(3, 1) # [0, 0.5, 1]
b_player = sf.BufferPlayer(b, loop=True)
# (Q: could this instead just be [sf.Constant(num) for num in np.linspace(0, 1, 3)] ?)
b_upmixed = sf.ChannelMixer(5, b_player, True)
mixdown = sf.ChannelMixer(2, b_upmixed)
graph.play(mixdown)
print(b_upmixed.output_buffer[:, -1]) # array([0. , 0. , 0.5, 0. , 1. ], dtype=float32) |
Hi!
I am trying to wrap my head around the awesome multichannel support in signalflow. Thanks to that, I realize I don't have to make many changes to an existing synth patch, passing lists as params generally works very smoothly.
I am now wondering what is the best method to mix N input channels to M output channels, given a panning signal that is also N channels (it could either go between 0...M-1, or -1...1).
I hacked together something that seems to work:
This can be generalized by using ChannelPanner instead of StereoPanner, and scaling multipansig to fall between 0 and M-1 (where M is the ChannelPanner's output channels).
To me it feels like generating the list of panner objects and summing them in a bus is somewhat against the mindset of multichannel audio in signalflow. Is there currently a different way of achieving this? I am also wondering if perhaps
ChannelPanner
could be upgraded to support multichannel signals as input and pan?UPDATE: for convenience, here is the same thing as a patch:
The text was updated successfully, but these errors were encountered: