MUMT 616: Inharmonicity
Inharmonicity in the context of plucked strings.
Abstract
During the course MUMT 616: Timbre as a Form-Bearing Dimension in Music, I talked about inharmonicity in the context of plucked strings.
The slides are available here.
Five examples with varying degrees of inharmonicity are discussed. The examples are qualitatively classified as:
The musical examples were generated from Manuel María Ponce’s Valse for Solo Guitar.
The inharmonicity was computed for each partial using the following formula:
$$ f_n = nf_0 \sqrt{1 + Bn^2} $$
where $f_n$ is the frequency of a given partial, and the $B$ parameter controls the inharmonicity.
The SuperCollider programming language was used to generate the audio examples with varying inharmonicity.
The code is listed here for reference.
Examples
No inharmonicity
Low inharmonicity
Mid inharmonicity
High inharmonicity
Extreme inharmonicity
SuperCollider code for generating the audio samples
(
SynthDef(\inh, { |out=0, freq=440, amp=0.5, gate=1, c3=20, pan=0,fB = 180|
var env = Env.new([0,1, 1, 0],[0.001,0.006, 0.0005],[5,-5, -8]);
var inp = amp * LFClipNoise.ar(2000) * EnvGen.ar(env,gate);
var son = DWGPluckedStiff.ar(freq, amp, gate,0.1,1,c3,inp,1,MouseX.kr(0,200));
DetectSilence.ar(son, 0.001, doneAction:2);
Out.ar(out, Pan2.ar(son * 0.1, pan));
}).add;
)
(
SynthDef(\har, { |out=0, freq=440, amp=0.5, gate=1, c3=20, pan=0|
var env = Env.new([0,1, 1, 0],[0.001,0.006, 0.0005],[5,-5, -8]);
var inp = amp * LFClipNoise.ar(2000) * EnvGen.ar(env,gate);
var son = DWGPlucked.ar(freq, amp, gate,0.1,1,c3,inp,1);
DetectSilence.ar(son, 0.001, doneAction:2);
Out.ar(out, Pan2.ar(son * 0.1, pan));
}).add;
)
(
SynthDef(\noinh,
{
|out=0, freq=440, amp=0.4, fB=0| //partials = 4
// Number of partials cannot change dynamically in the SynthDef :(
var sig, env, partials = 16;
// Need an envelope, otherwise the sinewaves play forever
env = Env.linen(0.03, 0.5, 0.15, amp);
// Summing the sinewaves
sig = Mix.fill(partials,
{
arg i;
// Need a 1-based-index for the partials
var n = i+1, harmonic, partial, ampdecay;
harmonic = n*freq;
partial = harmonic * sqrt(1 + (fB* n**2));
ampdecay = 1/n;
SinOsc.ar(partial, 0, ampdecay * EnvGen.kr(env,doneAction:2));
}
);
Out.ar(out, Pan2.ar(sig));
}).add;
)
(
SynthDef(\sineSynth,
{
|out=0, freq=440, amp=0.4|
var sig;
sig = SinOsc.ar(freq, 0, amp);
Out.ar(out, Pan2.ar(sig));
}).add;
)
(
SynthDef(\lowinh,
{
|out=0, freq=440, amp=0.4, fB=0.005| //partials = 4
// Number of partials cannot change dynamically in the SynthDef :(
var sig, env, partials = 20;
// Need an envelope, otherwise the sinewaves play forever
env = Env.linen(0.03, 0.4, 0.15, amp);
// Summing the sinewaves
sig = Mix.fill(partials,
{
arg i;
// Need a 1-based-index for the partials
var n = i+1, harmonic, partial, ampdecay;
harmonic = n*freq;
partial = harmonic * sqrt(1 + (fB* n**2));
ampdecay = 1/n;
SinOsc.ar(partial, 0, ampdecay * EnvGen.kr(env,doneAction:2));
}
);
Out.ar(out, Pan2.ar(sig));
}).add;
)
(
SynthDef(\midinh,
{
|out=0, freq=440, amp=0.4, fB=0.015| //partials = 4
// Number of partials cannot change dynamically in the SynthDef :(
var sig, env, partials = 20;
// Need an envelope, otherwise the sinewaves play forever
env = Env.linen(0.03, 0.4, 0.15, amp);
// Summing the sinewaves
sig = Mix.fill(partials,
{
arg i;
// Need a 1-based-index for the partials
var n = i+1, harmonic, partial, ampdecay;
harmonic = n*freq;
partial = harmonic * sqrt(1 + (fB* n**2));
ampdecay = 1/n;
SinOsc.ar(partial, 0, ampdecay * EnvGen.kr(env,doneAction:2));
}
);
Out.ar(out, Pan2.ar(sig));
}).add;
)
(
SynthDef(\highinh,
{
|out=0, freq=440, amp=0.4, fB=0.0874| //partials = 4
// Number of partials cannot change dynamically in the SynthDef :(
var sig, env, partials = 20;
// Need an envelope, otherwise the sinewaves play forever
env = Env.linen(0.03, 0.4, 0.15, amp);
// Summing the sinewaves
sig = Mix.fill(partials,
{
arg i;
// Need a 1-based-index for the partials
var n = i+1, harmonic, partial, ampdecay;
harmonic = n*freq;
partial = harmonic * sqrt(1 + (fB* n**2));
ampdecay = 1/n;
SinOsc.ar(partial, 0, ampdecay * EnvGen.kr(env,doneAction:2));
}
);
Out.ar(out, Pan2.ar(sig));
}).add;
)
(
SynthDef(\crazyinh,
{
|out=0, freq=440, amp=0.4, fB=1| //partials = 4
// Number of partials cannot change dynamically in the SynthDef :(
var sig, env, partials = 20;
// Need an envelope, otherwise the sinewaves play forever
env = Env.linen(0.03, 0.4, 0.15, amp);
// Summing the sinewaves
sig = Mix.fill(partials,
{
arg i;
// Need a 1-based-index for the partials
var n = i+1, harmonic, partial, ampdecay;
harmonic = n*freq;
partial = harmonic * sqrt(1 + (fB* n**2));
ampdecay = 1/n;
SinOsc.ar(partial, 0, ampdecay * EnvGen.kr(env,doneAction:2));
}
);
Out.ar(out, Pan2.ar(sig));
}).add;
)
(
m = SimpleMIDIFile.read("/Stimulus 1 major.mid");
m.p(\noinh).play;
)
(
m = SimpleMIDIFile.read("/op28n20.mid");
m.p(\lowinh).play;
)
(
m = SimpleMIDIFile.read("/op28n20.mid");
m.p(\lowinh).play;
)
(
m = SimpleMIDIFile.read("/op28n20.mid");
m.p(\lowinh).play;
)
(
Routine.run {
var s, m, synth, release;
s = Server.default;
s.sync;
s.record(path: "~/out.wav".standardizePath);
release = 0.3;
m = SimpleMIDIFile.read("/Stimulus 1 major.mid");
m.p(\lowinh).play;
3.wait;
// Wait for the Synth to release before stopping the recording.
release.wait;
s.stopRecording;
};
)
s.stopRecording;
(
Routine.run {
var s, synth, release;
s = Server.default;
s.sync;
s.record(path: "~/out.wav".standardizePath);
release = 0.3;
synth = Synth(\noinh, [freq: 30.midicps]);
2.wait;
synth.set(0);
// Wait for the Synth to release before stopping the recording.
release.wait;
s.stopRecording;
};
)
(
x = Synth(\sineSynth);
f = {
arg m;
x.set("freq", m.midicps);
m.postln;
};
m = SimpleMIDIFile.read("/Stimulus 1 major.mid");
//x = Synth(\sineSynth);
m.playNotesWithFunction(f)
)