// Salience:
c = 60.midicps;
//e = 71.midicps;
e = c * 8/5; e.asNote
f = Array.series(5, c, c) ++ Array.series(5, e, e);
~vp = {|freqs| Array.series(9, 1, 2).collect{|n| freqs /n }}; // subharmonics
g = ~vp.(f)
h = g.flop;
h[0].asNote;
w = [1, 0.5, 0.3, 0.2, 0.1, 0.06, 0.03, 0.015, 0.0075]; // weights
x = h.collect{|hx,i| hx.collect{|hy, j| [hy, w[j]]}};
p = ();
x.do{|xx| xx.do{|xxx| p[xxx[0]] = 0}};
x.do{|x0, i| x0.do{|x1, j| p[x1.first] = p[x1.first] + x1.last}};
p.keys.size
p.keys.asArray.asNote
d = p.values.select{|h| h >= 0.5}.removeDuplicates;
r = d.collect{|dd| [p.findKeyForValue(dd), dd]}
r.do{|rr| [rr[0].asNote, rr[1]].postln}
////////////////////
c = 60.midicps;
e = c * 5/4; e.asNote
g = 67.midicps;
f = c.series(c * 2, c * 5) ++ e.series(e * 2, e * 5) ++ g.series(g * 2,g * 5);
a = (1..5).reciprocal.ampdb + 90;
~m1 = [f[0..4], a].flop.compensateMasking;
~m2 = [f[5..9], a].flop.compensateMasking;
~m3 = [f[10..14], a].flop.compensateMasking;
[f[0..4], ~m1].flop.asSone.round(0.01);
[f[5..9], ~m1].flop.asSone.round(0.01);
[f[10..14], ~m1].flop.asSone.round(0.01);
a = Salience.calc(f);
a.pitches.values;
a.pitches.scanFor(f[1]);
a.pitches[f[0]]; // the salience value for the first note of the chord
a.pitches.includesKey(f[0]);
l = a.select(1.5); // shows the pitches with a salience of 1.5 or higher
a.spectral; // list of spectral pitches and their salience value
a.virtual; // the list of virtual pitches and their salience
a.virtual
z = Dissonance.make(Array.series(10, c, c), {1}!10, method:\parncutt)
z.harmonicAnalysis
z.play