DissonanceCurve Analyzes timbres in order to create tunings



Uses Bill Seathares' Dissoncance Curve algorithm in order to compute tunings for a given timbre.


See also:


 http://eceserv0.ece.wisc.edu/~sethares/consemi.html




Creation / Class Methods


*new (freqs, amps, highInterval )

Create a new curve based on arrays of data. This would mostly be used for additive synthesis.

freqs - An array of signficant frequencies of a timbre. 

amps - An array of amplitudes for the given frequencies. 

highInterval - The largest interval to compute in cents. For octave-based systems, you would usually use 1200. Default value is 1902.

*fm (carrier, modulator, depth, highInterval )

Create a curve based on the timbre of a given FM specification

carrier - The carrier frequency in Hz. 

modulator - The modulation frequency in Hz. 

depth - The depth (or delta range) of the modulator, in Hz. 

highInterval - The largest interval to compute in cents. For octave-based systems, you would usually use 1200. Default value is 1902.

// Example

// This example shows how a fm synthdef might be related to a curve

(

SynthDef("fm", {arg out, amp, carrier, modulator, depth;

var sin;

sin = SinOsc.ar(SinOsc.ar(modulator, 0, depth, carrier));

Out.ar(out, sin * amp);

}).memStore;

)

(

var carrier, modulator, depth, curve, scale;

carrier = 440;

modulator = 600;

depth = 100;

curve = DissonanceCurve.fm(carrier, modulator, depth, 1200);

scale = curve.scale;

)

*fft (buffer, size, cutoff , highInterval , action)

Create a curve based on the timbre of a given FFT buffer. THIS IS VERY SLOW and uses a lot of system resources

buffer - An FFT Buffer. 

size - The size of the Bufer in Frames. 

cutoff - The lower cutoff amplitude for a bin. Defaults to -60 dB.

highInterval - The largest interval to compute in cents. For octave-based systems, you would usually use 1200. Default value is 1902.



Accessing Instance and Class Variables



tuning 

Return a Tuning based on the minima of the Dissonance Curve



scale (size)

Returns a Scale in which the Tuning is the minima of the curve.

size - The number of degrees in the scale. For size n, it picks the n most consonant degrees. Defaults 

to inf, which makes a Scale degree for every degree of the Tuning.

// Example


(

d = DissonanceCurve.fm(1563, 560, 1200, 1200);

c = d.scale(3);

c.degrees.postln;

)


plot


Plots the DissonanceCurve

// Example


(

d = DissonanceCurve.fm(1563, 560, 1200, 1200);

d.plot;

)



digestibleTuning (window)


Returns a Tuning related to Sethares' algorithm, but calculated using a modification of Calrence 

Barlow's Digestibility Formula.  

Basically, it  does a comparison of every partial at every tuning, but instead of looking at Plomp and 

Levit's ideas  of local consonance, it compares the ratio relationships between the partials.  The 

numerator and denominator of this ratio is summed and then multiplied by the amplitude of the 

quieter partial.

This algorythm just gives a list of the relative dissonance at all possible tunings. To figure out scale 

degrees, the most consonant tuning is picked from it's neighbors, given the window size.

The tuning generated by this way and by the Sethares / Plomp and Levit algorithms may have 

nothing to do with each other.


window - The idealized step size, in cents. To do normal-ish semitones, this would be set to 100, 

which is the default.


// Example

(

d = DissonanceCurve.fm(100, 200, 300, 1200);

t = d.digestibleTuning(100);

t.cents.postln;

)



digestibleScale (window, size)


Returns a Scale based on the digestibleTuning.  

window - The window size used to compute the digestibleTuning. Defaults to 100.

size - The number of degrees in the scale. For size n, it picks the n most consonant degrees. Defaults 

to inf, which makes a Scale degree for every degree of the Tuning.



// Example

(

d = DissonanceCurve.fm(100, 200, 300, 1200);

c = d.digestibleScale(100, 5);

c.degrees.postln;

)


Examples



// Use this SynthDef for all examples

(

SynthDef("fm", {arg out = 0, amp = 0.2, dur = 1, midinote = 0, carrier, modulator, depth;

var sin, env, ratio;

// the ratios between the carrier, modulaor and depth should stay the same

// so multiply all of them by the current note

ratio = midinote.midiratio;

carrier = carrier * ratio;

modulator = modulator * ratio;

depth = depth * ratio;

env = EnvGen.kr(Env.sine(dur, amp), doneAction: 2);

sin = SinOsc.ar(SinOsc.ar(modulator, 0, depth, carrier), 0, env);

Out.ar(out, sin);

}).memStore;

)



// Using a Tuning from a DissonanceCurve

(

var carrier, modulator, depth, curve, tuning, note;

carrier = 700;

modulator = 600;

depth = 800;

curve = DissonanceCurve.fm(carrier, modulator, depth, 1200);

tuning = curve.tuning;

Task({

6.do({

note = tuning.semitones.choose;

Synth(\fm, [\dur, 0.7, \midinote, note, 

\carrier, carrier,

\modulator, modulator,

\depth, depth]);

0.5.wait;

});

}).play;

)




// Use a Scale from a DissonanceCurve with a Pbind

(

var carrier, modulator, depth, curve, scale, degrees;

carrier = 440;

modulator = 600;

depth = 100;

curve = DissonanceCurve.fm(carrier, modulator, depth, 1200);

scale = curve.scale(5);

degrees = (0..scale.size); // make an array of all the scale degrees


// We don't know how many pitches per octave  will be until after the 

// DissonanceCurve is calculated.  However, deprees outside of the range

// will be mapped accordingly.


Pbind(

\instrument, \fm,

\octave, 0,

\scale, scale,

\degree, Pseq([

Pseq(degrees, 1), // play one octave

Pseq([-3, 2, 0, -1, 3, 1], 1)], 1), // play other notes

\carrier, carrier,

\modulator, modulator,

\depth, depth

).play

)



// Use the digestible Scale

(

var carrier, modulator, depth, curve, scale, degrees;

carrier = 350;

modulator = 710;

depth = 505;

curve = DissonanceCurve.fm(carrier, modulator, depth, 1200);

scale = curve.digestibleScale(100, 7); // pick the 7 most consonant tunings

degrees = (0..(scale.size - 1)); // make an array of all the scale degrees


Pbind(

\instrument, \fm,

\octave, 0,

\scale, scale,

\degree, Pseq([

Pseq(degrees, 1), // play one octave

Pseq([-7, 2, 0, -5, 4, 1], 1)], 1), // play other notes

\carrier, carrier,

\modulator, modulator,

\depth, depth

).play

)