Weighted averages for spectral descriptors

Based on some of the comments that @b.hackbarth made in the thread about AudioGuide, I decided to whip something up to compare spectral centroid using vanilla averaging (ala fluid.bufstats~) vs applying a weighted average based on the linear amplitude for each entry.

For the sake of simplicity I’ve done all the stats-ing in Max-land, rather than peek~-ing the values back into a buffer~ for fluid.bufstats~ to do its thing.


----------begin_max5_patcher----------
4003.3oc0c09iihil+yU+WAJ59zopy52sY+Tuys2MqzMyrq1YNM5TqUkHIjz
zMAh.RWcOq19u88w1.AHDfj3TolpjRQgw3mW94m2rcU+y27vrEoeILel2ez6
8dO7v+7MO7f4V5a7P4u+vrsAeYYbPt4wlsLc61vjhYOZaqH7KEl6mmDjEVc2
cAEK+PTxlmxBWVXe6TDcN5QOhOYNA4i4XJGIQBeI+QOpuoIzbj2+n7MDsx7V
SW7w2hwxp2ax9sQIwgEFRAe3lo6KptKRey+0adi9iGuRd5SQK+z.rDgaYIl+
bezguURklOGlkD2IVZQPxFuhTukAwK2GGTTqxhiRBWltOw7XjJlNKLGdKAEQ
oIO0+SbrXQhIyAkJHUnhFeKYO5gQHsXgIZHVVmlTrNXYXS9uknhOqwSlG8al
mTKYGRBpe1jfslmc1eIL9ygEQKClcyku+ZXzlOTDtxK3ygYAaFbFAVpEFbIs
kfhH.4jzBeNK4D62SxoeJMaaP7DjR9TwIkR7KPJQeMJkRBeFntiDR67Vre85
vr7AjObrOXPUgHTj8KsjgP3Fgl4SB4TFgv8ZDB0Oa0jDByJYlRtAjPQwgftL
GLTzPv8vrfc6Zb6GZzEsH3iolWj5w5aEkXuEt9VYgeNpp+756FjABhBPJrOy
pU9hnB+qeMoqByR1GYdS1aBJiRRxH10Zy7ck.Ei1op4FhVC7BiX5ePs3PJGe
PZBp0MwoK+T3pFRnGlktKLIJoosyVMuJbcv93hmZB.ASl81dETt2FqQj+orn
f3ZFXSVzpzDMQzRSnuc0v8dXtig23MYFySjDrqmNC3EPrbhFyAlbe9hfLshZ
QbXC+C.XNMMtcS08KNbcQYy6hRR5HEKR2c5FyzVYOcyKRgF2Nz61zR9S6Srs
9DfIJdJGLG094BhiKma190+kfjnsf2yhnskFPpaLLI.XzOjuLKMNtE+Za4y8
zxJ.iuL74nUEevLPMACviGsqBDMqVKuJZSXdQ66UDrIu8cxK9pUn23V6WTNG
9ohvs6Lw.z5AfYGQ4E4eH847xGrBn0T.bHpwlyoaZnq08GxfWmHTLl79l2ae
6aSSxAqOdeLMYS7WmGDstYG5yVnI5BrBO2uwWJk1WASz0VnEXarvU70ckyKl
sNNM.bWYiTZVqmsgsSRSJoOWF8XAkT0fw4PoMoap7KeGHZxBhy+PvtvoI6rg
hioNUZwP+tPbEmteURXd9zjTnxe36RIEU4HI0Aq55v1Kmq58OdSSWgyz16V8
j019SAEEYQK1WXe3l91OKaPfCwEAwcLbzmIp2bfhOynk.O24MBZ7PJnEAYEf
FAx8hHYTovCqCjt5ZNZfvnJUm8ldJAgOJNpizuy5MDKjp2PrH8FhE14xBzbv
XHCYX9FWNfbPVlRqI0DrjewLt+MlwC+sUAK+1nbB0WXRF0vHL9ohDFcFTqKC
vON3qM7zMN2vXZtQoFWsjGsIQGTXGCNMYaI5LJBAwgfShDbQS3bkzenLaPFl
kCfvtY9I8uXbo7FiKOgl92h8x2ucHtUX4VhZtjb3aAkA3V5DX2GOIKyOCVl3
NV9ygeYWl2+wZr2+I7IYbdmo.NrYkzPJ.xiuTcsBceT0G36+flu8dW9xf3fL
cNod3IHEn74L9guoJh1RL4BkBB+Wo.dIVpYWJXLSgO7sjKtR.uPcev6Edwdw
CwvHo0FNYNtIGSMUKle47q7FaF+hK4uAyJ3V+vCUObA+0YE9YFN.BOvFa3fr
.6FyBCFo25LHRaH9FpO9QOiC+qpB9loeBlMUP5kZ4gbmh8kR84yETIgwFP2p
fnAOQ3EJwExxb58wX61vfjgpgtcEY5M1B5DbuVm6ZjF5zCeyt0lfNAe+1Acn
p7KKxoFMzQEOEKtmhaIh6EvV.yLwHFBw.iqX7bt.yITHFIhXNUhnBL3OU.ZY
eIF66wDvyHov7AkGSRvy8wLNGCtWUx49RARBuGoDRMj6iU.T.h0hYdF30Swy
YDDC6gUPVG50UPIzkQCMm.isToqUDDvhufhofLVABTgvjfISA2mAzGCtFASu
X9TAWqGjygAUmLNQ.iJCykRLjrIGnLf7DJPsPkvvBzuumf.4tACBV3IY5z3n
DIl6I7gHF.5kdHEy110niZ5iOTc.JKTHhLmzbxBGhBy2tR.50s6LMLPtSocr
+2h.AxPyQrl2KKikZBE5v5c4vOO4rjyIvSpSC7bSV59cCwzVWaDl53Etmiuh
POoj6Snm6BC+zIq36.lF6Ekiojo5RnWYP+ABQu0P8g8EHopaiufasG+E6KJR
Gx4tPZLXA1kOZ6VPJg4iNct25Fzn1L5nKKBydxtBVcVj3w72+RZNqTXbCLmg
42GyYqVTjFLNvtWaYzKtLYmSxL3WTa2kYQS.CX7lkIAwtRa2X5c11cmkeZHk
culsQ7qwrMlbiMaOg7WUcxc8TYlVN8Fit37ztOQisNdezp4K1utka5u48t7z
8YKCarj2uacXfdqsjejScno0EvyT.Blb8hcw7Hbg26.h1HDygYADTuQFSGnT
ML5w6hHsyRIgp7YfaRLFRWPuwFolIYL+oXTsNww9iTfdFVYn2.0P0LtQ0.UO
n26dNJYU5y58QiU1+tOjty7aNPITBt6UpyYNSpiuPot4k0b4jqTBFVQe+1pB
qHsj1phnv6.8uJLG.w06Op2eHsgFOz1nU6RAexkCoPYJZid0ZaUCUkD71yHm
nsChrrU1stV+EfapLhgFwivH5bAzLx0ORjwDYBGLR53aFejntZjFS5gcxHME
.mgbPGCQvW0PKHSXnoz9G5qCcxuai7zXZ7sPdyuairdUzGGjgQ24wF6ZksYK
dL5fWQgW4PodoFJwTXJkKFI5TlrnqaXCUWGugJHhAckKTniy8CiJ2CeC0H1G
2ShDXJx3H83FcMFZRx.0IfujqDSMESFl8KhdvupwRMoooUDz0gekSXnp.4W2
Hol5HcshOLcJijzURuwBMohwupQhME8jd4DuZo2jli0gbbj6IcofG2qrShSV
LkQh4hQZJfQrK3I7TjdtIN4o.QvGK8rYD14H9nGjNGsmNGqmiOROm937z8n7
X16x1i0PmrPObZX1uJJ8mMmIkm9wvj81zcqN4LsEEK1rNJNdYZb5QmnopDqm
Yas9zCU8ru2CMm3yvXveJZNESk5Zl.WAWv4MLiT1GbUmXblOhneTHCeIiatR
Qnbp9JTmtQNLVHrucDP9Jc4N0WA2BCiUytEjro7L3HabPR1kktKMq9vHMm5W
+76KR2jErJJzVgBTqcl8ikHoLn0tmljYaAAZTUeZh7pUF+v9pyIW+JffkKgW
bKgJk.BHC2w3.mZtRZuWC9rshfI06X6GMhIpx2dEGiDc5TXb31timTIDRSOT
HrRYuBFZBoceygNuTK.a2aBLHFhDKjDqJTgPXV6NqK9joeOEknmsDVqVo9Dr
vRuU3BhTwnsUqSBoVqEOAXch.n1vUUMHUIQbkxd0QDXW3pnR2IzeIsWYeS2D
zZOmTSvv0ecWXh2OGjj68ygaiVjFuZVCIZKwiOhgkFbGfeXRoEFP7sPCtnxx
2UNg3GCJJFZBQyS826aDKRySKndaJLG4HB5u8mJdxzMzYNOcZZ31XPet1joU
fxjHCZTCFocmbGGBtDNzSSMmgtC5EtcLAvnjNw4Xl9V8AtyoEtgPk7634aSt
a21Ib1ih5omiYY+9tB6LD8u9K+OmITlijBi9AI3TrAJiwDLYHWNXsGeqsAM9
0X3VV46eDWN5MlEx532mHXFPhdKXYxM+z3SUilpisqMlSGGhwIDP9bg0MpFN
XHO+6O5dZN2u6w9zO39u70UYoaBS9EiH5DXbrfBl1Lp.IS36WdEx2kPbyZbE
s7Lg4rRKs8c0oCsZhc5Zs95OJ39junKLJ9Clbo9RqASpeo6I8s3T2.JawDp5
38N9pKBJOHh86CSB+bvIwpiw3tDq9cM1HS8iXO1eupVDiJux4j0et9OcBuhH
puOHJ4aSjp.LPs+miux8jV9yQvjyyxxCtbB6gOtIzD4UEQ8+lGGoqh1Yfs5P
MCE.wDMJ2eNqSs2crVbr8WWJu9gvUm27v97DzmrRbSIaHetrnurrHK9JndmR
P5cp4fFOtF.UGHwKpomeJcUX9jsJNQGsWkznmHXbJCeVlPt0SP+o8aWLLwzm
0licZdCHs+VzxhwkUWjl1ojo9DoT7pKZiedY5tvoGtwgpV6S0e0a.42Og7uD
rX5rxDmm2GxYT6KNkofjqVLZ.z8jx2I8T9BFIzujtQmJiyBmc.kBUB4dis8f
QwlbxXkKqhKYo+usirvRufh2eM3ygqSy19MmKgO+nGQy4Nh4VEj8o2ln+y71
aMUC3Lw8SsPy0Us3Lqi5IJQAq1TfrdwbpWVGkY7cPM3vi.2cdQKbd82b4RL9
wx+XSgPk6rYW7pFLWxtqNwfk0DMP7h7QfSGVhkitvcU+ZZz9qafzIVLhEwgf
KS6B.9cooeZliLN8wcQIe5bWtLfKQpZ2wT8F6mMPcWmvy2a1tSXXNB9dXsLL
ONs4Ui6PXBC4QlYmPeN0ThGa8oCpC7zooWP6uNdkL5V32w7x4joIexlS1up2
ZO26fkh0gRLdHz8Nkp1CNu1tXklenZif7Yk6DEeepRVdkT4zrd1F7EQ8e9hm
rwHU67VjkXwqa8bZw+cTjtKvPMCaN1RSOKOBkS7a6WSC+wtVOz5HN1OgcIR0
qNLQU8Fz33qFvVkTo2UZO16Uuft3ul8oV6YGSSKZO9amWQ2Zrm9bdcMrmhwu
Kde3awm6VoCKKWK5C1v7aZ4xkDXV5yImMENpsUGRg+WeM37IPDwma2fFRIkR
rycHXLl5dB76yBCu.JzH8pKEGVnct6dh6GAKaIEAmM4oXHgvVjPABHNinT3i
84tmF+6gqNa5SVG4FtN+GToGCWSe++gwwoOe9hPB2tojj5yypg.4.pjO1lYi
q+Sn4sfAH+djALKew2mFeAPj5RHwgHl31M2iy21Z61msK9xBdPVua5XXtPYq
Es1L.ejHTYHafQDNR3alDPTbAQ4R9JKbYXzmGdk65CyTmXxAdB0b+k5Fhylh
hFVLYO9BFXPyuzQfrbmBCnYe5fR6WFTTdXxp7WadPNHjc+bud2lwHIFIsKQE
Sf7K2K2RrSwNEA6Famb2nXT+PPQp2OnyBclyF+7hv06iiKNuEXQenUjFQpnN
VccoO5t6S6twFUbN1VRaAURsmYAAx9uhIGJTsL0MhmbX98DkTYCrgVO0gR7o
9N05Yg9+vBulNu.OqqlyWO2sA.L9ZEjIGRZodAlbWtJMSptCS77FzlwohQqn
H12GYCKEvxrx0ZWfPTb68Lcm4y+4nrhu58euI8hlNaO5bm3eEUu4e8l+Mrdf
hX.
-----------end_max5_patcher-----------

I’m just using a kick and snare sound from jongly.aif also for the sake of simplicity.

The results are pretty striking, particularly for the kick example. Without the weighted average, I get a spectral centroid of 3395.637244 Hz and with it I get 2781.255879 Hz.

I wonder if it would be more accurate to also do the averaging in the log domain for centroid.

Doing this for each channel of the output of fluid.bufspectralshape~ starts to get a bit messy as well, but I wanted to whip up a quick proof of concept for the idea.

///////////////////////////////////////////////////////////////////////////////////////////////////////////////

Interface-wise, with the currently implementation it can get a little tricky as I have no idea where this would fit into the workflow.

I suppose it would be interesting to generalize it out, so you can weigh whatever statistics by a specific descriptor (perhaps a @weighting attribute or something). Could also be useful for weighting things by pitch confidence, as @tremblap alluded to, or something funkier like that.

I guess since fluid.bufloudness~ and fluid.bufspectralshape~ are completely independent processes, there’s no way to have this weighting be default (unless ...spectralshape~ did its own internal loudness calculation, which would be a bit wasteful).