hey, i have released my first c++ ugen. A highly optimized dual wavetable oscillator with dynamic mipmapping and sinc interpolation for bandlimiting and the additional possibility for oversampling (like OscOS). The oscillators are cross modulating each other via PM with a tracking One-Pole filter in the feedback path (via the pmFltRatio you get different PM feedback flavours). You can download the release here. If you want to test it you can use that code snippet here which creates a quick gui (make sure you have NodeProxyGui2). I hope the cross platform builds have been successful ![]()
thanks @Sam_Pluta for the inspiration from OscOS.
Im very happy right now, that im currently extending my possibilities to contribute to this community, watch out for more ![]()
// create a wavetable (or use your own)
(
t = Signal.sineFill(2048, [1], [0]);
u = Signal.sineFill(2048, 1.0/((1..512)**2)*([1,0,-1,0]!128).flatten);
w = Signal.sineFill(2048, 1.0/(1..512)*([1,0]!256).flatten);
x = Signal.sineFill(2048, 1.0/(1..512));
v = t.addAll(u).addAll(w).addAll(x);
b = Buffer.loadCollection(s, v);
)
(
// scale modulation depth of modulators between 0 and 1
var modScaleBipolarUp = { |modulator, value, amount|
value + (modulator * (1 - value) * amount);
};
SynthDef(\dualOscOS, {
var param, calcWavetableData;
var wavetableDataA, wavetableDataB;
var sigs, sig;
// Global parameter function
param = { |chainID, name, default, spec|
var paramName = "%_%".format(chainID, name).asSymbol;
NamedControl.kr(paramName, default, lags: 0.02, fixedLag: true, spec: spec);
};
// Function to calculate wavetable data
calcWavetableData = { |chainID, phaseOffset|
var tableIndexMF, tableIndexMod, tableIndex;
var wavetable, sizeOfTable, pmIndex, pmFltRatio;
var freq, phase;
freq = param.(chainID, \freq, 440, spec: ControlSpec(1, 1000, \exp));
phase = Phasor.ar(DC.ar(0), freq * SampleDur.ir);
/////////////////////////////////////////////////////////////////////////////////
// Create table index modulation
tableIndexMF = param.(chainID, \tableIndexMF, 0.1, ControlSpec(0.1, 1));
tableIndexMod = { |phase|
SinOsc.ar(tableIndexMF, phase + phaseOffset * pi)
};
tableIndex = modScaleBipolarUp.(
modulator: tableIndexMod.(0.5),
value: param.(chainID, \tableIndex, 0, ControlSpec(0, 1)),
amount: param.(chainID, \tableIndexMD, 1, ControlSpec(0, 1))
);
// table params
wavetable = param.(chainID, \sndBuf, 0);
sizeOfTable = BufFrames.kr(wavetable) / 2048;
// Phase modulation params
pmIndex = param.(chainID, \pmIndex, 0, ControlSpec(0, 5));
pmFltRatio = param.(chainID, \pmFltRatio, 1, ControlSpec(1, 5));
/////////////////////////////////////////////////////////////////////////////////
(
phase: phase,
pmIndex: pmIndex,
pmFltRatio: pmFltRatio,
wavetable: wavetable,
sizeOfTable: sizeOfTable,
tableIndex: tableIndex
);
};
wavetableDataA = calcWavetableData.(\one, 0);
wavetableDataB = calcWavetableData.(\two, 1);
sigs = DualOscOS.ar(
bufnumA: wavetableDataA[\wavetable],
phaseA: wavetableDataA[\phase],
numCyclesA: wavetableDataA[\sizeOfTable],
cyclePosA: wavetableDataA[\tableIndex],
bufnumB: wavetableDataB[\wavetable],
phaseB: wavetableDataB[\phase],
numCyclesB: wavetableDataB[\sizeOfTable],
cyclePosB: wavetableDataB[\tableIndex],
pmIndexA: wavetableDataA[\pmIndex],
pmIndexB: wavetableDataB[\pmIndex],
pmFilterRatioA: wavetableDataA[\pmFltRatio],
pmFilterRatioB: wavetableDataB[\pmFltRatio],
oversample: 0
);
sig = XFade2.ar(sigs[0], sigs[1], param.(\all, \chainMix, 0.5, ControlSpec(0, 1)) * 2 - 1);
sig = sig * param.(\all, \amp, -25, ControlSpec(-35, -5)).dbamp;
sig = sig * Env.asr(0.001, 1, 0.001).ar(Done.freeSelf, \gate.kr(1));
sig = LeakDC.ar(sig);
sig = Limiter.ar(sig);
Out.ar(\out.kr(0), sig);
}).add;
)
(
~nodeProxyGui2 = { |synthDefName, initArgs=#[], excludeParams=#[], ignoreParams=#[]|
var synthDef, nodeProxy;
synthDef = try{ SynthDescLib.global[synthDefName].def } ?? {
Error("SynthDef '%' not found".format(synthDefName)).throw
};
nodeProxy = NodeProxy.audio(s, 2);
nodeProxy.prime(synthDef).set(*initArgs);
NodeProxyGui2(nodeProxy, show: true)
.excludeParams_(excludeParams)
.ignoreParams_(ignoreParams)
};
)
(
g = ~nodeProxyGui2.(\dualOscOS,
[\one_sndBuf, b.bufnum, \two_sndBuf, b.bufnum],
[\one_sndBuf, b.bufnum, \two_sndBuf, b.bufnum],
[\all_amp]
);
)
g.randomize;
g.vary;