Closed LFSaw closed 7 years ago
what happens if you crossfade into a new set of synths while you are in the process of a crossfade already?
ah I see now how the multiple crossfade works.
every synth has its own, and cleans up after itself .
OK, I've had a closer look. I wonder: why is the writing to the fadeBus
done in the method that constructs the filter input? Shouldn't it be the method that constructs the filter output?
I understand that we need a place to have the original input signal for all versions of one single cross fading synth.
why is the writing to the fadeBus done in the method that constructs the filter input? Shouldn't it be the method that constructs the filter output?
because it writes the unaltered signal rather than the altered signal. AFAI remember (late atm.), the filter output method does not have a link to that signal anymore.
ok, makes sense. After some thinking I also see that other way round would be more complicated.
Although, just trying to reconsider carefully:
The current problem is only that filters overwrite their input signal (this is only true for sequential filters, not for parallel ones).
So if every synth would write its output also to the fadeBus
, it would be available, wouldn't it?
all sequential filters would always read from the fadeBus
.
a synth that is in the process of fading out could immediately stop writing to the fadeBus
as to let the original input through.
a synth that is fading in would overwrite the fadeBus
signal, so that subsequent unrelated synths would in turn read the correct signal.
I think if you reconstruct the fadeBus
to write / read the other way round, the bus logic should be done here:
getBusArgs { |readIndex, writeIndex, dryReadIndex, through, argumentIndex|
var readBus = busIndices.clipAt(readIndex);
var writeBus = busIndices.clipAt(writeIndex + (argumentIndex ? 0));
var dryReadBus = busIndices.clipAt(dryReadIndex);
^[\in, readBus, \out, writeBus, \dryIn, dryReadBus, \through, through]
}
something like this:
getBusArgs { |readIndex, writeIndex, dryReadIndex, through, argumentIndex|
var readBus = busIndices.clipAt(readIndex);
var writeBus = busIndices.clipAt(writeIndex + (argumentIndex ? 0));
var dryReadBus = busIndices.clipAt(dryReadIndex);
var fadeBus = fadeBusses.clipAt(argumentIndex); // <----- maybe like this?
^[\in, readBus, \out, writeBus, \dryIn, dryReadBus, \through, through]
}
or maybe we really only need a single fadeBus, then (having added fadeBus
to the instance variables of StenoStack
):
getBusArgs { |readIndex, writeIndex, dryReadIndex, through, argumentIndex|
var readBus = busIndices.clipAt(readIndex);
var writeBus = busIndices.clipAt(writeIndex + (argumentIndex ? 0));
var dryReadBus = busIndices.clipAt(dryReadIndex);
^[\in, readBus, \out, writeBus, \dryIn, dryReadBus, \through, through, \fadeBus, fadeBus]
}
So, I did a drawing that, I hope, reflects the intended behaviour of fading in steno.
[c] shows the current implementation, where fadeBus
keeps the original, unprocessed data.
I'll now try to mimic your suggestion...
I think the right diagram isn't correct yet. Dependent on different situations, read and write (you marked as inBus and outBus) are the following:
| | read | write | dry-read | fadeBus |
| serial | 0 | 0 | 0 | ? |
| parallel | -1 | 0 | -1 | ? |
But I suppose the situation we need to cover is only the serial one.
hmm... I was referring to the actual implementation and the names of the buses in the code... I also don't understand what you mean by 0/-1/?
ah sorry, also now I understand [c] is the picture on the right.
0
means the current bus offset
-1
means the previous one.
But never mind, it'll be easier with the alternate solution anyhow.
OK, here is a formula. Let's suppose that we have a tailBus
for all the filter tails, and normally write and read from outBus
. All use ReplaceOut
.
(
var outbus, tailBus;
// normal = lastSynth, doesn't matter. New synths are always added after, as "normal".
// this works, because it also always writes zero to the tailBus to clean up.
var normal = { |oldSignal, filteredSignal, allTails|
outbus = [oldSignal * 0, filteredSignal * 1, allTails * 1].sum; // assuming that tails are cleaned up by last synth.
tailBus = [oldSignal * 0, filteredSignal * 0, allTails * 0].sum
};
// just fading out differs.
var fadingOutReplacement = { |oldSignal, filteredSignal, allTails|
outbus = [oldSignal * 1, filteredSignal * 0, allTails * 0].sum;
tailBus = [oldSignal * 0, filteredSignal * 1, allTails * 1].sum
};
var fadingOut = { |oldSignal, filteredSignal, allTails|
outbus = [oldSignal * 0.5, filteredSignal * 1, allTails * 0].sum; // 0.5 means a fade-in
tailBus = [oldSignal * 0, filteredSignal * 0, allTails * 0].sum
};
)
Note that when fading out in a filter the input signal is x-faded out (not featured here) so that automatically the filteredSignal
should reach silence, even if it is filteredSignal * 1
here.
One problem is: how do we know what the last synth is? There is no notification about that a synth has indeed ended.
That is when to use fadingOutReplacement
and when fadingOut
?
I am thinking that this is what we're intending to implement. first row is a "replacement-group" for Filter, 2nd row is a replacement group for Quellen.
This means that all synths are instantiated in the form of the rightmost appearance (called A
) and, when being replaced, get a "replace" message which switches them to the layout in the left versions (called A'
resp. A''
).
seems like the most uniform solution between quelle and filter, yes. Also good that the available input for quelle is the same.
it would be optimal to reformulate the whole diagram in a way that all outs are ReplaceOut
. Then it will be easier to implement in the current context.
I started implementing the collectTails
scheme in this branch:
https://github.com/tai-studio/Steno/tree/topic/fix-fading-collectTails
with https://github.com/tai-studio/Steno/commit/4be731f10d9b2152a1cedb4a2064ff57b407d97f I implemented the fading mechanism for filters (quellen do not work yet).
(
s.waitForBoot{
t = t ?? {Steno(2, true).push};
// non-standard filter (not using incoming signal)
t.filter(\f, { |in, controls| SinOsc.ar(ExpRand(1000, 2000), 0, Decay.ar(Impulse.ar(10), 0.01)) * controls[\env] });
t.filter(\g, { |in, controls| SinOsc.ar({ExpRand(400, 800)}!2) * controls[\env] });
// standard filter (use incoming signal)
t.filter(\h, { |in, controls| RHPF.ar(in, Rand(1000, 2000), 0.1) });
t.filter(\i, { |in, controls| RLPF.ar(in, Rand(4000, 8000), 0.1) });
t.setGlobal(\attack, 5, \fadeTime, 5); // set fadeTime and attack to 5 seconds
t.quant_(0.5); // set quant to synchronise pulses
s.dumpOSC(1) // see messages that are sent
}
)
// single filter
-- f
-- g
-- f
// smooth transition
-- !f
s.queryAllNodes
// >> wrong order
// with "normal" filter (h and i)
-- fh
-- fi
-- fh
-- !fh
s.queryAllNodes
// >> wrong order
// since f and g swallow their input, only g is heard
-- fg
// set mix
t.set(\g, \mix, 0.9) // both
t.set(\g, \mix, 0.3) // both
t.set(\g, \mix, 0) // only f
-- fh
it would be optimal to reformulate the whole diagram in a way that all outs are ReplaceOut. Then it will be easier to implement in the current context.
They are, or?
writeToBus now has an Xout. My formulas above just do all the work outside this method. But never mind for now.
I am sorry, I just do not understand them... and where they should go...
I like the idea to "just" have one signal passed to writeToBus
which is treated differently, depending on where it goes...
added implementation for quellen in https://github.com/tai-studio/Steno/tree/topic/fix-fading-collectTails
I created a separate PR ( #18 ) for the alternative scheme.
This branch is obsolete and superseded by #18
My test file is here: https://gist.github.com/LFSaw/aa60234ddb04ae4c5348112ecab170d7
This PR is big and by far not ready to merge (I think). It basically are two commits, the rest are merge artefacts from branches I already proposed in other PRs (#14 #15 #16).
52aaf68
To fix fading behaviour to sound reasonable, I realised that an additional
numChannels
-sized bus is needed: I called itSteno:fadeBus
(that's the index, no bus object used, I'm open for naming ideas, but I;d like to keep it reasonably short for the sake of OSC communication).When a filter is replaced (
DiffString:swapFunc
), it sends an arg\replacement, 1
which indicates that this filter is a replacement for an old one. This triggers that it reads its signal fromfadeBus
during the fade and switches later to normal behaviour. All this is defined in `StenoSignal:filterInput.Accrodingly, all Synths need to write (i.e.
ReplaceOut
) to that bus (compare lower third of each,StenoSignal:filterInput
andStenoSignal:quelleInput
).4fb2e1c
Since also the Synths for
[]{}()
need to write there, I made them use the StenoSignal class.missing