New Object Early Community Build #1: FluidVoiceAllocator (NOW WITH SC TOO)

I thought this was a thread of me largely asking excited question about how it’s working, trying to understand. Not sure where the idea of demands comes from.

I specifically mentioned that interface choice (needing to generate spoof data as part of the usage of the object) as it (to my understanding (which may be incorrect)) also appears in the other object you’ve shown so far. Hence me bringing it up.

it probably is. academia must kill my soul. keep on trippin’ and askin’ and I’ll reply with my rose-tinted glasses I will keep for the good stuf :smiley:

Ah I see! That’s dope! I slowly realize how versatile this could be. It can be good for data compression, but it can also be a good tracker.

Yeah, totally. If there is another external or an abstraction to cover the offline scenario, then I guess there is no need to put it anywhere else.

The only parameter I still don’t really get is @trackprob. What I see is when trackprob=1.0 then the tracking is a lot more jumpy, as when trackprob=0.0.

…after I spaced out with the demo patch a bit I found myself aligning some cheap beats to the vibrato of the cello sample by finding the track where it was jumping between two overtones. This is fun!

<pre><code>
----------begin_max5_patcher----------
3591.3oc6cs0qaaiD94y4WAgeXQWfSL3EQcIXen.Kvhcwh8oVzWZJBjknsYi
rjWI5ykVjyu8cHojs7wR1x1TNocSPiqkDMGNeyvgyLbnxue+cSlU7rnZB58n
eFc2c+982cm4V5abW802MYU7yIYwUllMIW7TwrecxC1GoDOqL2VgHM2qXiJS
nTurVX62IxbnM+R8Skol1C8w6BXM+j7Mqj4vOxPBxtaZ6p8t65XUxRY9hOVJ
RTVB3w3S4Of7wdSoLpONzOh6yozvnGPTxT7CHRzTL5Wzcvmu+d8GOLPtc1Fk
pHuWNaVb9htYMuIaGukwqDJQ4GE4wyxL+RrKYad.SyhTulOuHFEn5LQYuL5j
GNBy5G3HlkdDl02vrdA78jwj.MBfuJYbeZzEKDoKDGW1eTXIbhaX8Z4rkM8B
GClMSVollqVhXGUEna9LpS9jdg7oWT3ghXunwfqqDYHuSJf6dBN1oLMm5qYP
F8pXyEwx7W6kepjKxiy1miVsISISVFmmKxdLtTFmqZOmssT1e7mjGY0v880P
BodZcvEBFqyheQqVuCPJkKj.BjIxWnVZongDS.H3SU6.kifbc8M8eSkIJYQd
b4Ks5lZ5oDqVWXvCplb1GNKtB9IaxkZiGuG440EjyGBjuVB3XRQoFFggP6Gs
pHUXWFSSslNadQVVwSKxJl0ZrcTAnRtRToJE.gpgssL47hxUfRS2z++tINSp
d4vgv9ZD7SpQTuJtUifgmti9owp3ZkfZsf6ljjIWu0mlVO.dT7rphrMJAPJC
qLAyP+yXEY5SwOVO3LbkDTR.D+DM4SxbqzJdSprPemVMnVPtgD5iwdgg9sdV
VQw5V.kYPWjqD4pOVohUhFNxL1aZxma91mu2Ngn9NeMYjfcCLRvmpWQfEhao
SbwVIbn6r9D23NKiaVKXLbm8OA1DweylX8D.O5g5GihIRB5eCJCG0DYuMYPl
HYXvDIMhf+JwD40DLD+FDLDKzXg3FFLTp.rb7JhBKjcpkN5DT3mg2xGwznOk
aTu8M+O+nuJi6i64l39pWG3q139XAtIDnZ9bLh66RSeC6Fj9lFwqaReiyCvk
D4ToqaBv03IZUlLcm04JgBFdPqZ47Bwq85emPid1hjhrhxleN8g89XW2jDmC
dzDmK7aZZ.au+XZNiSi7Hj.eu.LOReqfvPZq6BitVcpkW1a.foAdbbTHlEQI
7.cWz5Z+PeCg3Xd69c+dsswnK2ib.YqTuXan24J1C4F6UTH.NJGC+gg8XdAg
vbZpefQHQIeSKnWsf8aNwLR7XDV.k6EgIQ9laQreGGDZdV+ZAzuoE7GQs.Gz
qszB7B9lVfK0B5Pfweqn.lI2g871Sjwg8Kv9lw6wVf40rppVJYro52V.h8M8
PaIneT+xquYlcrkWsr8AS07Lyl1J+zxHysB10DsvreAF9aBrwdBVKOWo5P5.
ijskWL6RX66hauBLVze9DX8D61l7esPliBOdf4G6ucBfmylRGcZ.wG3ZR.Kh
TCHT8N2gYiQtJlqJV0OXzIyxcSF4aXVVvTM+wv+gMH1VfC8OgSkRJVsRjqNP
04u8NTZAJuPglWTtPnPpBzbYFvxH0RApTjtIOMNGdrFIpPem3QQ4KnpGqzOu
TfjUn3bDvfq2nPwUn0vuz1l+Q1l+dw+IFkJpRJkqUEk+0FxmIyEIEar6VFsi
TrbN0+DteDkGZgM69lypSnTnam88aYH8V.djDi1uMG2jIIN0ZVIvrGX1zJcw
SB6SS46drPlHhyxJRhAYIxbY0VQZaomuajdDXhf1nIMxrsNDR3Usgl8xXqKx
d40iwOb2vO9dsYmfwga9QXVKEYGknh4n4YajoSeirS77ZXTAsSmkaSyD5ceC
Y2rypoHcujIlqrcjdRNXd.rA.MSjmHEUO.y5SslHjKVtsElc1qZcQdJv5nUw
KxkpMfAfoCclOMvUXs0VJlbXpyuV6.8A8+.XgDEWtXi9oUZigVvuB34pWAKi
weBLFpr67IJNaQQoTsb0fMJRcCzvXDsdHXyv3Rhe3XXTTglgxtvBH6nr4RYZ
pH+x11HpY0.ObGamBLojOBFM+QXcPXvgRkymCqVBqi9T7iBjFJpzK0VIDnkE
OUuVaRQdkpbShRjhp.Mgz5EV5Y5Cqih.vMpHFmW0JE7oAuEoHbyrGli8gcco
Xs.3XynQTZ1.8yxk1.5Uo2PNodCKH3P8FB65VsUaqXyQKnlCiYQIVUWgBSzK
c8Qi8koP2BiccSeP+g4IpRY2OnJ9odd.rNP4tdqSfl3ns.jcREPFi0AjyIih
CN507fAn0O.TrB.uYaTBjt3JJKxLN7hpj+lPudo960N8ZVC8AjTgRhy0dROC
7JNOoTDWIz5yUJXgvm21y1UNaeG8uCUjm8h9WBQP.zFA.sdEZcunWsnaC.dc
j2FOG46E13rByy3qBkXlAvwNdO1KhSgfJpNVn7c51LdzlpGZVZz2rzX.dTzy
9Wc4TVo17uP9nvDNkLuQwBsPqAXhFCtuVqKYSoYgjNTUe.Z.3HlzppttTpcv
3En2POsTlrzb2leCD5FzioYhzonePtRlEWpWPxnWWBAf2ZBfw0usdxLGFOaJ
EuNTMRpiVSpNECDe7ThumOyiRCB3TSNwfucU5m8Ip9IMXgLUUUELhQLzeA4Y
vNyzUcbtvDVPXTjCsqNz2bEXL.5uTI7yfVMGDwZugAzECXK3ghwBfdROLeei
1HA3pXLBf6EKDkCdpN0QAI6GFZqLNutr0FNJ.6O1nI1.t.l99OjiQuSGjg3C
4D3afFXbxm9PNE9d0Fngx7OjyfKJEYZiqeH2S+DkHNqSLyuCLi3HLCaV1m1k
KATOici.5HfYLT83qYwGXtsc557xhU1P1.8LSLYue+301ELlU+qM7O7vyvtE
+7H8heWbDIheKMN40Nhi2mdzzsnsCmcYoYxl+Ecw3eXcZYbVw6Rya2rZ+gaF
4OJEO8nrRNS1TqrMi1h4yAeGZklz1Y2D3sOIRSKiWTk.9xj0lgRxjIeRsrrX
yhksuu0mtkG9CrO3wCefkJZWjZe2YKZpr3s2wnx19NmrjI2VPsohUElXi159
5gAJ2oXFet9BzjUMSEURs0WYD+BkiU5DtTVTkTrV7544sCIzMkHZ.OXG+vvV
mav3u.LTfaXHtsLW6bZ2UyemxkUUrIc43yj0wN5f8RMoFsy3ihBtt8X5JJw6
naQEdaNUfdFmAGmR51lO3cgVeI00MqamyXWnQnNy8Pznv8clv3ueu3Uob+i5
vvw2P1N2xsyvhf2IQqNy5KgZNOoL7X.WsCLBnTnAvlOWYRWf9rAf9d8U.n.C
3J6cdGQ+eeu08MxEBncBlmy9UvN8BfznNbRqVu7RQSczkajCv3otgM1krpec
sh+MnpzIvTPyV3GYiC0y6pl8c9.PubO+FTR9jur7dctLjPvx8CCQ2.bvlDBs
2f8kDhaHpnJg3i0A4U1d6mamMvagpAyv15ynh21ZcALVDr6HndiQDHR2dAD+
aFfzoYyuPHx5xhYcgFg2NzvGOEVyXW0PEQ9RfF.mX.jLQdW3QzMCO7qyl4MG
AlIKUKyJdBB3WTsrHKsKKo3amdQD3pFQO6nV23KGnrTmzrigJjaFpPozoTre
KXwAVSWGmK1lnTaJX1+3U8PqO5oJSogNZClsNPX2cIFlY7tfwaQzc4Hh9lym
dS2Bl3sPOMn0Ikefk6GXQbcQ41if9T9WH.0Q3Iqt9Er.JwVsf5rqx++M.k54
TMTNYOUT8th9GID87MEZyBdGqMN9F+7I1SBaTcz6QX6ebwBk6oZ0lsbzFBcI
0tqA7MA7+l2yfl9We+8YnphMkIMhtl2iZsd6ZjJpTx73FMled2KbOTef1Poj
9ER3Ioj9sxz0SIugvSLGPIJzGzSwRQ6MZ1Ngmbc7HdHnYnC3w2L96AMwtfRA
CgmzCGx0RI+gPIpKnDeHTx2EnGan7zUSoAo64h4W525SmlRtP2iMDaFMCmqh
R7gnkqe8eb05db9sRKeHho2LZbjcvgXAlEzMkoWmFyPjijHWnwLDcSlKVilL
Da8bWPI1P76fF4JJQNE54LJcJkQOrqnD6TTh5JJ4cJJwbEk3mhRAthR9mRK2
YnWvonjKPuAMwcu1rRlttPlqpCbn9Ufk8zkZq3HyEt1f8PFnDuAORI0GOutG
p3qyGa5P77cu1zptguNRSFfQq2L9bFsG.WG0Gk6VjwXlSOoGuVzYzw16hv8+
z4gKMj0.XmIS4YOKP7NdQA5UWWQG4YL+NpGCNiZpvfCe10FS7Pl1guPoZWUv
JmZAf9eFMj1A3vvm5YLF8Pvwsy74CwJE87PKOe64ff6Yeg7Yyu5dWQhLEVWs
R.4HUJ3fYDh6YDlmQrS4b6POxxHsuZDXD73wHD+1LxdWMBLxP7k+LsDUubc8
6+O7AWPsGPyZtvb0UxEdQil3n9sRtcFwdWPruYVaDF5qtV1HbzXCRHauAK65
Gr9i2f0dfL0ukPwGbk6Qc93wH0kQesBy9W4dF4VkwM1frZ3hr8xNmrH53Dpv
NizU4V2CGDoI8jFoqyUCF9Zb295H8vmFdc4naPZuthPjAvQDGruOmjg3ig5B
87cevgYZcPgPQtAaplgPTWCtDO2uvTSBJrm3bcQVbvUa8c1+vqbuCnD74qB8
V+MqCD6lFA6fhjgOFKLMDJSBOO0hFAJw99Vp1o88tx36RP86cpSkcKag.Dud
8ihxp5glgglrJ9WsUJR3ClKk41KM+ifwjRg9n.Zau8NwkIKk52vLaJsGWtm8
sEtgozTJy2Hqsk.P485i9WbUk9n0UsNNo9e7OJddx8e99+Gfx4Dib
-----------end_max5_patcher-----------
</code></pre>
2 Likes

yes it allows for always matching to a track so mega jumps allowed

[over simplification warning]
1 = 100% chance of finding a track
0 = respect the rules 100%
this is being investigated, thanks for your patience

2 Likes

Aaah I see. That’s an interesting parameter then, in case you want to interpolate between “steady” and “pointy” patterns.

2 Likes

and now this object does what it’s for, its hidden agenda (like most objects decomposing more involved algo) is to show and explain and allow to subvert the moving parts… fluid.sines~ is a complex machine, and now it is clearer a bit :slight_smile:

2 Likes

os SC friends, here is a very very terse example. You need to use the latest nightly (because i fixed the format out of FluidSineFeatures) for it to work as is:

b = Bus.audio(s);

(
c = {var input = In.ar(b);
	var sines = FluidSineFeature.kr(input,5);
	var voices = FluidVoiceAllocator.kr(sines[0], sines[1], 6);
	SendReply.kr(Impulse.kr(1),"/sourcesines", [sines ++ voices].flat);
	input.dup;
}.play;
)

(
o = OSCFunc({
    arg msg;
	"freqI + magI ".post; msg[3..12].round(0.01).postln;
	"freqO + magO ".post; msg[13..24].round(0.01).postln;
	"voice states ".post; msg[25..].round(1).postln;
},"/sourcesines");
)

// observe the voices as you add sines...
d = {Out.ar(b,SinOsc.ar(440,mul: 0.1))}.play
e = {Out.ar(b,SinOsc.ar(550,mul: 0.05))}.play
f = {Out.ar(b,SinOsc.ar(330,mul: 0.1))}.play
g = {Out.ar(b,SinOsc.ar(220,mul: 0.15))}.play
h = {Out.ar(b,SinOsc.ar(110,mul: 0.2))}.play

// or remove them
e.free
d.free
g.free

// add 6 sines too quiet too high so should not voice steal the looudest lowst 2 remaining

i = {Out.ar(b,SinOsc.ar(700.series(750,950),mul: 0.02).sum)}.play
i.free

MacOS fat binaries:
voiceallocSC.zip (100.3 KB)

now I have not done a supercool example like @fearne on Max - but it works. one could use the state changes to make some super great array of synths from that control stream… anyway, let me know what you think!

1 Like

Thanks for this! Upon running this example with one voice, looks good! If I run a second voice, the output values get pretty noisy; these were three subsequent poll values:

freqI + magI [ 439.86, 552.91, 0.0, 0.0, 0.0, 0.1, 0.05, 0.0, 0.0, 0.0 ]
freqO + magO [ 439.86, 552.91, 545.17, 543.08, 541.23, 545.9, 0.1, 0.05, 0.05, 0.05, 0.05, 0.05 ]
voice states [ 2.0, 2.0, 0.0, 0.0, 0.0, 0.0 ]

freqI + magI [ 441.94, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0 ]
freqO + magO [ 441.94, 541.81, 546.64, 541.23, 545.9, 543.76, 0.1, 0.05, 0.05, 0.05, 0.05, 0.05 ]
voice states [ 2.0, 0.0, 0.0, 0.0, 0.0, 0.0 ]

freqI + magI [ 441.9, 539.34, 0.0, 0.0, 0.0, 0.1, 0.05, 0.0, 0.0, 0.0 ]
freqO + magO [ 441.9, 546.64, 544.46, 542.44, 539.34, 541.81, 0.1, 0.05, 0.05, 0.05, 0.05, 0.05 ]
voice states [ 2.0, 0.0, 0.0, 0.0, 2.0, 0.0 ]

My understanding of this object is only from this thread, so forgive me if I’m missing something…but it seems like the new voice is getting repeatedly allocated and then unallocated - is this expected behaviour? It seems to be less noisy with frequencies farther apart from eachother, but I still get “spilling” between voices, especially upon adding or removing a voice:

freqI + magI [ 219.25, 4040.62, 0.0, 0.0, 0.0, 0.15, 0.1, 0.0, 0.0, 0.0 ]
freqO + magO [ 546.1, 219.25, 4040.62, 4040.62, 0.0, 0.03, 0.15, 0.1, 0.1, 0.0 ]
voice states [ 0.0, 2.0, 0.0, 2.0, 0.0 ]

Using fewer prioritisedVoices resulted in some double allocation as well, ie. playing 440Hz and 550Hz and getting three active voices in the output (1 at 400Hz and 2 at 550Hz).

(And just to double check: it’s intended that the outputs hold their last value if no voice is allocated, yes?)

these are strange results. I’ve tried to replicate but I can’t reproduce…

I’ll try to make a more verbose example but when I am at line 36 after freeing the cluster of high voices I have 2 very stable voices as expected.

Did a fresh SC install, no extensions (not even SC3-plugins) and I get the same behaviour…

After evaluating line 26:

freqI + magI [ 440.61, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0 ]
freqO + magO [ 440.61, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0 ]
voice states [ 2.0, 0.0, 0.0, 0.0, 0.0, 0.0 ]

And then after evaluating line 27:

-> Synth('temp__29' : 1029)
freqI + magI [ 440.15, 551.04, 0.0, 0.0, 0.0, 0.1, 0.05, 0.0, 0.0, 0.0 ]
freqO + magO [ 440.15, 545.8, 551.04, 0.0, 0.0, 0.0, 0.1, 0.05, 0.05, 0.0, 0.0, 0.0 ]
voice states [ 2.0, 0.0, 2.0, 0.0, 0.0, 0.0 ]

freqI + magI [ 440.26, 550.34, 0.0, 0.0, 0.0, 0.1, 0.05, 0.0, 0.0, 0.0 ]
freqO + magO [ 440.26, 545.8, 543.67, 541.73, 546.53, 550.34, 0.1, 0.05, 0.05, 0.05, 0.05, 0.05 ]
voice states [ 2.0, 0.0, 0.0, 0.0, 0.0, 2.0 ]

freqI + magI [ 439.33, 556.63, 0.0, 0.0, 0.0, 0.1, 0.05, 0.0, 0.0, 0.0 ]
freqO + magO [ 439.33, 542.35, 556.63, 541.73, 546.53, 544.37, 0.1, 0.05, 0.05, 0.05, 0.05, 0.05 ]
voice states [ 2.0, 0.0, 2.0, 0.0, 0.0, 0.0 ]

This is with the SendReply trigger set to 4Hz, by the way, but I observe the same jumpiness at 1Hz as well.

this is very strange. after line 26 you should have 2 peaks detected, not just one. so your thresholds are wrong, or my values are just so near the thresh it freaks out. you have not changed anything?

did you as requested download the last nightly (otherwise this code doesn’t work - I repaired the interface of FluidSineFeature so it is not right (but breaks old code)

edit the thresholds are right and I’m now almost certain you didn’t update the SineFeature - but please do confirm I am known to be wrong

Ah sorry, I had a vertical indent in my file, shouldn’t have used line numbers…this printed after evaluating d = {...}.play:

And this prints after evaluating e = {...}.play

I did indeed download the latest nightly and haven’t changed anything in your code aside from the `Impulse.kr()’ freq - I set it to 4 Hz just to get a bit more action in the post window.

When I add sines.postln to your c SynthDef, I get:

[ [ an OutputProxy, an OutputProxy, an OutputProxy, an OutputProxy, an OutputProxy ], [ an OutputProxy, an OutputProxy, an OutputProxy, an OutputProxy, an OutputProxy ] ]

Which is [[freqs],[amps]]. Also:

FluidSineFeature.version; // returns:

-> FluidSineFeature
Fluid Corpus Manipulation Toolkit: version 1.0.6+sha.7fbaf8f.core.sha.c8ff5a9f

Where 7fbaf8f is the build hash…right?

I really hope this isn’t something dumb I’m missing - I apologize in advance for anything silly I’ve missed!

actually now you have the right state. you have 2 stable peaks and 2 stable voices (in index 0 and 2 of the tracks)

now run the rest of starting f,g,h - you should have 5 stable voices each at the right place (no jumps between the nearest values)

then free e d g and you should get back to 2 stable voices.

then add the cluster and the 2 stable voices should stay there and the other jump in and out (they are bordeline and less priority so should not voice steal)

===
let me know if that works for you

Sorry, I think I’m being confusing - the point I’m trying to make is the two voices are not stable. If I run just the following:

b = Bus.audio(s);

(
c = {var input = In.ar(b);
	var sines = FluidSineFeature.kr(input,5);
	var voices = FluidVoiceAllocator.kr(sines[0], sines[1], 6);
	SendReply.kr(Impulse.kr(1),"/sourcesines", [sines ++ voices].flat);
	input.dup;
}.play;
)

(
o = OSCFunc({
    arg msg;
	"freqI + magI ".post; msg[3..12].round(0.01).postln;
	"freqO + magO ".post; msg[13..24].round(0.01).postln;
	"voice states ".post; msg[25..].round(1).postln;
	"\n".postln
},"/sourcesines");
)

d = {Out.ar(b,SinOsc.ar(440,mul: 0.1))}.play
e = {Out.ar(b,SinOsc.ar(550,mul: 0.05))}.play

The server spits out:

-> Synth('temp__13' : 1013) // this is the server returning e.play
freqI + magI [ 441.97, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0 ] // second voice doesn't appear here
freqO + magO [ 441.97, 541.81, 0.0, 0.0, 0.0, 0.0, 0.1, 0.05, 0.0, 0.0, 0.0, 0.0 ] // but it shows up here?
voice states [ 2.0, 0.0, 0.0, 0.0, 0.0, 0.0 ]  // but not here!

freqI + magI [ 441.37, 543.08, 0.0, 0.0, 0.0, 0.1, 0.05, 0.0, 0.0, 0.0 ] // oops, there it is!
freqO + magO [ 441.37, 546.63, 544.46, 543.08, 543.75, 541.81, 0.1, 0.05, 0.05, 0.05, 0.05, 0.05 ] // oh, but in the previous second it's been allocated to five voices?
voice states [ 2.0, 0.0, 0.0, 2.0, 0.0, 0.0 ] // but it seems to have settled on index 3!

freqI + magI [ 440.61, 548.09, 0.0, 0.0, 0.0, 0.1, 0.05, 0.0, 0.0, 0.0 ]
freqO + magO [ 440.61, 542.43, 545.17, 543.08, 541.22, 548.09, 0.1, 0.05, 0.05, 0.05, 0.05, 0.05 ]
voice states [ 2.0, 0.0, 0.0, 0.0, 0.0, 2.0 ] // oh wait, nevermind!

freqI + magI [ 441.97, 0.0, 0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0 ] // wait, where did it go? 
freqO + magO [ 441.97, 541.81, 543.08, 541.22, 545.9, 543.76, 0.1, 0.05, 0.05, 0.05, 0.05, 0.05 ]
voice states [ 2.0, 0.0, 0.0, 0.0, 0.0, 0.0 ]  // gone here too...

Which to me doesn’t seem stable - unless this is expected/intended behaviour…in which case I’ve misunderstood something and apologize for the confusion!

If I understand what’s supposed to be happening (now I’m starting to worry that I don’t understand what the intended output of your example code is supposed to be…), I get two stable voices (an array that looks something like [ 0.0, 0.0, 2.0, 0.0, 0.0, 2.0 ], right) after freeing e, d, and, g. And after adding and removing the cluster those voices are still there.

1 Like

Okay, sorry for the spam, was just trying a few different things…if I keep everything the same, a few small changes seem to produce consistent voice allocation:

d = {Out.ar(b,SinOsc.ar(440,mul: 0.1))}.play
e = {Out.ar(b,SinOsc.ar(560,mul: 0.05))}.play // freq at 560Hz instead of 550 - works like a charm!

freqI + magI [ 438.03, 553.34, 0.0, 0.0, 0.0, 0.1, 0.1, 0.0, 0.0, 0.0 ]
freqO + magO [ 438.03, 553.34, 0.0, 0.0, 0.0, 0.0, 0.1, 0.05, 0.0, 0.0, 0.0, 0.0 ]
voice states [ 2.0, 2.0, 0.0, 0.0, 0.0, 0.0 ]

// also, if I play a single synth with two freqs:
d = {Out.ar(b,SinOsc.ar([440,550],mul: 0.1).sum)}.play

freqI + magI [ 443.16, 544.99, 0.0, 0.0, 0.0, 0.1, 0.1, 0.0, 0.0, 0.0 ]
freqO + magO [ 443.16, 544.99, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0 ]
voice states [ 2.0, 2.0, 0.0, 0.0, 0.0, 0.0 ]

// or if I use the original code but raise the mul arg of the second synth:
d = {Out.ar(b,SinOsc.ar(440,mul: 0.1))}.play
e = {Out.ar(b,SinOsc.ar(550,mul: 0.1))}.play // from 0.05 to 0.1

freqI + magI [ 443.42, 544.56, 0.0, 0.0, 0.0, 0.1, 0.1, 0.0, 0.0, 0.0 ]
freqO + magO [ 443.42, 203.59, 278.76, 544.56, 0.0, 0.0, 0.1, 0.01, 0.01, 0.1, 0.0, 0.0 ]
voice states [ 2.0, 0.0, 0.0, 2.0, 0.0, 0.0 ]

Perhaps this points to what you mentioned earlier regarding thresholds? What are the defaults supposed to be (I’m not passing any values to FluidVoiceAllocator)?

the threshold would be in FluidSineFeatures but it is by default -96 dB so 0.05 should deffo be stable.

the code above produces what you understand correctly to be the expected behaviour here. If you crank the sine mag to 0.5 do you still get the jittery behaviour?

If I use the original example but e = {Out.ar(b,SinOsc.ar(550,mul: 0.5))}.play I still get the jitters (and not in a cool Max/MSP way)… :cry:

AHA! I just tried the example at 44.1kHz and it worked wonderfully! Was running at 48k before…does that point to something perhaps?

not supposed to but I will check this out as this would be worrying.

now this is crazy. good news for @fearne it is not in the voiceallocator

in sinefeature it seems that the algo is rejecting the perfect harmonic at 48k

if I do this:

{FluidSineFeature.kr(SinOsc.ar([440,550],mul: 0.1).sum,2).poll}.play

I get 2 peaks all the time

if I do that

{FluidSineFeature.kr(SinOsc.ar([440,550],mul: [0.1,0.5]).sum,2).poll}.play

it misses many of them at 48k and a few of them at 44.1

if I augment my resolution I lose almost none

{FluidSineFeature.kr(SinOsc.ar([440,550],mul: [0.1,0.5]).sum,2,windowSize: 2048,hopSize: 512).poll}.play

and further res sorts the problem

{FluidSineFeature.kr(SinOsc.ar([440,550],mul: [0.1,0.5]).sum,2,windowSize: 4096,hopSize: 512).poll}.play

===
it is more prominent when the highest freq is loudest and when they have a harmonic relation. So my hunch is that it is the sideband rejection at play here, but @weefuzzy is a magician of such things so might remember the long discussions we had with @groma

in the meantime, doing non harmonic wide enough sorts the ‘problem’ so I will need to change the example - and explain something in sinefeature!