Roughness
Parncutt's implementation of Hutchinson & Knopoff (1978)
March 1993 version, runs on Think C 5.0
Richard Parncutt, <lastname>@uni-graz.at
The computer program code that is appended below predicts the perceived roughness of a tonal sonority, i.e. the mean result of an experiment in which listeners are asked to rate the roughness of a range of different sounds (How rough does this sound? 1 = very smooth, 7 = very rough). Roughness is regarded as a sensory component of musical dissonance.
In the code, each sonority is specified by the chromatic pitch categories (pitCat) and amplitudes (amp) of its pure tone components. The program allows sonorities to be constructed by superposition of individual pure tones, harmonic complex tones, or spectra that are equally-spaced in log frequency ("X-spaced tones"). (A Shepard tone is called an "X-spaced tone with X=12 semitones" or a "12-spaced tone").
Amplitudes of individual pure tone components are input by the user. Amplitudes of partials of harmonic complex tones vary automatically as 1/n, 1<=n<=10. Amplitudes of partials of X-spaced tones are given by the bell-shaped envelope of Parncutt (1989).
The "standard curve" of Plomp & Levelt is represented by the function
g(x) = [e*(x/a)*exp(-x/a)]^i, x<1.2
g(x) = 0, x>1.2
where x is the interval between two partials expressed in critical bandwidths, a is the interval for maximum roughness (about 0.25 Cbs), and i is an index (power) of about 2. (Parncutt invented this function, which is published only here; Hutchinson & Knopoff used a look-up table.)
Critical bandwidth CBW is given by Plomp & Levelt's (1965) function, as cited by Hutchinson & Knopoff (1978).
Testing: I have compared calculations according to this model with those published by Hutchinson & Knopoff (1978). They agree quite well (give or take a few percent). Adjustment of a and i in the above equation could improve agreement, however the difference is unlikely to be very large.
More serious problems with this procedure include:
- its failure to account for the masking and audibility of individual components;
- its failure to account for the significant dependency of each masking contribution on the absolute (mean) frequency of the pure-tone pair (Terhardt, 1974; Aures, 1985);
- its failure to account for the role of critical bands in the summation of different contributions to overall loudness (Terhardt, 1974; Aures, 1985). Contributions from different critical bands should add more strongly than from within a single critical band. A possible amendment leading in the right direction would be to add contributions from successive, but not from overlapping, intervals.
- the assumption that roughness contributions add linearly. Kameoka & Kuriyagawa (1969) assumed that contributions should be raised to the power 4, added, and the sum raised to the power 0.25.
- its failure to account for the (apparently direct) dependency of roughness on the waveform envelope (Terhardt, 1974; Aures, 1985) -- not on the spectrum, as assumed by Plomp & Levelt (1965).
In spite of these caveats, predictions according to this procedure are sufficient for many music-theoretical purposes. Careful consideration of the above points could produce an improved music-theoretical model.
Other possible minor improvements:
- Hutchinson & Knopoff (1978) include frequencies up to B10. A lower cut-off may be more appropriate.
- Moore and Glasberg's (1983) ERB-rate function may produce better results than Plomp's (1965) critical bandwidth function.
- According to data presented by Rakowski (1982), the frequency interval for maximum roughness is close enough to 2*sqrt(f). Incorporating this assumption could simplify things - it would no longer be necessary to calculate CBW separately. A further simplification might be to plot Rakowski's data in semitones and fit a line to that. Then there would be no need to convert pitches to frequencies.
Literature
Aures, W. (1985). Ein Berechnungsverfahren der Rauhigkeit. Acustica, 58, 268-281.
Hutchinson, W. & Knopoff, L. (1978). The acoustical component of western consonance. Interface, 7, 1-29.
Kameoka, A. & Kuriyagawa, M. (1969). Consonance theory. Journal of the Acoustical Society of America, 45, 1451-1469.
Moore, B.C.J. & Glasberg, B.R. (1983). Suggested formulae for calculating auditory-filter bandwidths and excitation patterns. Journal of the Acoustical Society of America, 74, 750-753.
Parncutt, R. (1989). Harmony: A Psychoacoustical Approach. Berlin: Springer-Verlag.
Plomp, R., & Levelt, W.J.M. (1965). Tonal consonance and critical bandwidth. Journal of the Acoustical Society of America, 38, 548-560
Rakowski, A. (1982). Psychoacoustic dissonance in pure-tone intervals: Disparities and common findings. In Dahlhaus, C. & Krause, M. (Hrsg.), Tiefenstruktur der Musik (S. 51-67). Technische Universität Berlin.
Terhardt, E. (1974). On the perception of periodic sound fluctuations (roughness). Acustica, 30, 201-213.
/*
Implementation of Hutchinson & Knopoff (1978), here called "H&K",
which is in turn based on Plomp & Levelt (1965), here called "P&L.
March 1993 version, runs on Think C 5.0
Richard Parncutt, parncutt@music.mcgill.ca
The program calculates the roughness of a tonal sonority.
Each sonority is specified by the chromatic pitch categories (pitCat) and
amplitudes (amp) of its pure tone components.
The program allows sonorities to be constructed by
superposition of individual pure tones, harmonic complex tones, or
spectra that are equally-spaced in log frequency (e.g., Shepard tones).
(A Shepard tone is called an "X-spaced tone" with X=12 semitones).
Amplitudes of individual pure tone components are input by the user.
Amplitudes of partials of harmonic complex tones vary as 1/n, 1<=n<=10.
Amplitudes of partials of X-spaced tones are given by a bell-shaped envelope.
The "standard curve" of P&L is represented by the function
g(x) = [e*(x/a)*exp(-x/a)]^i, x<1.2
= 0, x>1.2
where x is the interval between two partials expressed in critical bandwidths,
and a is the interval for maximum roughness (about 0.25 CBs),
and i is an index (power) of about 2.
(NB: I invented this function; H&K used a look-up table.)
Critical bandwidth CBW is given by P&L's function, as cited by H&K.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
main ()
{
// list of variables in alphabetical order:
FILE *in, *out;
float amplitude, amp[132]; // amplitude of pure tone component
float e; // base of natural logarithms
float CBint; // interval between two partials in critical bandwidths
float CBint0; // interval for max roughness (P&L: ca. 0.25)
float CBint1; // interval beyond which roughness is negligible (P&L: 1.2)
float CBW; // critical bandwidth according to P&L, H&K
short chr; // chroma or pitch class in semitones above C
short cptNum; // component number
short cptPitCat, pitCatX; // pitch category of pure tone component
float freq[132]; // frequency in Hz of each pitCat
short harInt[11]; // interval in semits between fundamental and harmonic
short harNum; // harmonic number
float index; // for standard curve of P&L
short input;
double intensity; // sound intensity of a partial
float meanFreq; // mean frequency of two cpts
short number; // temporary variable
short numCpt; // number of components in an X-spaced tone
float numerator, denominator; // for calculating H&K Eq. (3)
short pitCat; // chromatic pitch category, pitCat=48 is middle C
short pitCat0; // pitCat of component at or just above C4 in an X-spaced tone
short pitCat1; // pitCat of the lowest component of an X-spaced tone
float ratio; // temporary variable
short reg; // pitch register in octaves (C4 has register 4)
float roughness;
float standardCurve; // P&L Fig. 10, H&K Fig. 1
short numSon; // number of sonorities
short numTon; // number of tones in sonority
short sonNum; // sonority number,
short tonNum; // tone number
short tonTyp; // tone type - 1:pure; 2:harmonic complex; 12: Shepard
// PART 0: INITIALIZE
// harmonic series in semitones:
for (harNum=1; harNum<=10; harNum++)
harInt[harNum] = 12.*log((float)harNum)/log(2.)+0.5; //rounded
// frequency in Hz of each pitch category:
for (pitCat=0; pitCat<132; pitCat++)
freq[pitCat]=440*pow(2,(float)(pitCat-57)/12.); // in Hz
// parameters for analytic version of standard curve of P&L:
CBint0=0.25; // interval for maximum roughness
CBint1=1.2; // upper limit of roughness
index=2; // the bigger index, the narrower the curve
e=exp(1); // 2.7182818=base of natural logs
// in=fopen("inputDibbenWebern","r");
// in=fopen("inputKrumhanslMozart","r");
in=fopen("inputSchneiderStumpf","r");
out=fopen("output","w");
// INPUT SPECTRUM
printf ("Hutchinson & Knopoff model of acoustic dissonance.\n");
printf ("Input from keyboard (0) or file(1)? ");
scanf ("%d", &input);
if (input==0) printf ("Pure (1), harmonic complex (2), or X-spaced (X>2)? ");
// To get Shepard tones, enter 12 (12 semitones between partials).
// To get fifth-spaced tones, enter 7.
if (input==0) scanf ("%d", &tonTyp);
if (input==1) fscanf (in,"%d", &tonTyp);
assert (tonTyp>=1);
printf ("How many sonorities? ");
// These can be pure, harmonic complex, or equally-spaced complex
scanf ("%d", &numSon);
assert (numSon>=1);
for (sonNum=1; sonNum<=numSon; sonNum++)
{
// initialize amplitude array:
for (pitCat=1; pitCat<132; pitCat++)
amp[pitCat]=0.;
if (input==0) printf ("\nHow many tones? ");
// These can be pure, harmonic complex, or equally-spaced complex
if (input==0) scanf ("%d", &numTon);
if (input==1) fscanf (in,"%d", &numTon);
assert (numTon>=1);
for (tonNum=1; tonNum<=numTon; tonNum++)
{
if (tonTyp==1) // pure tone component
{
if (input==0) printf ("Register? Chroma? ");
//Octave register, e.g. C4->4;
//Chroma in semits relative to C, e.g. C->0, G->7
// Amplitude in arbitrary units
if (input==0) scanf("%d", ®);
if (input==1) fscanf (in,"%d", ®);
assert ((reg>=0)&&(reg<10));
if (input==0) scanf("%d", &chr);
if (input==1) fscanf (in,"%d", &chr);
assert ((chr>=0)&&(chr<12));
// if (input==0) scanf("%f", &litude);
// if (input==1) fscanf (in,"%f", &litude);
// assert (amplitude>=0);
pitCat=12*reg+chr; // one-dimensional pitch scale in semits
assert ((pitCat>=0)&&(pitCat<132)); // roughly, the range of hearing
amp[pitCat]=1;
// amp[pitCat]=amplitude;
} // end if (tonTyp==1)
if (tonTyp==2) // harmonic complex tone component (musical note)
{
if (input==0) printf ("Register? Chroma? "); //Octave register, e.g. C4->4;
//Chroma in semits relative to C, e.g. C->0, G->7
if (input==0) scanf("%d", ®);
if (input==1) fscanf (in,"%d", ®);
assert ((reg>=0)&&(reg<10));
if (input==0) scanf("%d", &chr);
if (input==1) fscanf (in,"%d", &chr);
assert ((chr>=0)&&(chr<12));
//printf ("register: %d" , reg);
// printf ("chroma: %d\n", chr);
pitCat=12*reg+chr;
assert ((pitCat>=0)&&(pitCat<132));
for (harNum=1; harNum<=10; harNum++) // limited to 10 harmonics
{
cptPitCat=pitCat+harInt[harNum]; // pitch of harmonic no. harNum
if (cptPitCat<132) // i.e., if at or below B10, H&K's upper limit
{
amplitude=1/(float)harNum; // gradient=1/n
intensity=amp[cptPitCat]*amp[cptPitCat]+amplitude*amplitude; // add intensities
amp[cptPitCat]=sqrt(intensity); // add intensities if partials coincide
} // end if
} // next harNum
} // end if (tonTyp==2)
if (tonTyp >= 3) // equally-spaced complex tone (Shepard tone: tonTyp = 12)
{
if ((input==0)&&(tonNum==1)) printf ("Chroma? "); // number of semits above middle C of reference component
if (input==0) scanf("%d", &chr);
if (input==1) fscanf (in,"%d", &chr);
assert ((chr>=0)&&(chr<tonTyp));
numCpt=120/tonTyp; // number of cpts altogether, across range of hearing
pitCat0=48+chr; // pitch of reference cpt (at or above C4=48)
pitCat1=pitCat0-pitCat0/tonTyp*tonTyp; // pitch of lowest cpt
for (cptNum=1; cptNum<=numCpt; cptNum++)
{
cptPitCat=pitCat1+(cptNum-1)*tonTyp;
if (cptPitCat<120)
{
amplitude=(float)cptPitCat*(float)(120-cptPitCat)/60/60;
// arbitrary bell-shaped amplitude envelope, max 1 at pitCat=60
amp[cptPitCat]=amplitude;
// amp[cptPitCat]=1;
}
} //next cptNum
} // end if (tonTyp >= 3)
} // next tonNum
// Check input levels:
printf ("\nSonority no. %d. Amplitudes of pure tone components in percent: \n", sonNum);
fprintf (out,"\nSonority no. %d. Amplitudes of pure tone components in percent: \n", sonNum);
printf("Chroma C D E F G A B \n");
fprintf (out,"Chroma C D E F G A B \n");
for (reg=9; reg>0; reg--)
{
printf ("Register %2d ",reg);
fprintf (out,"Register %2d ",reg);
for (chr=0; chr<12; chr++)
{
pitCat=reg*12+chr;
if (amp[pitCat]>0.005)
{
printf ("%4.0f", 100*amp[pitCat]);
fprintf (out,"%4.0f", 100*amp[pitCat]);
}
else
{
printf (" -");
fprintf (out," -");
}
}
printf (" \n");
fprintf (out," \n");
}
// CALCULATE ROUGHNESS
numerator=0; denominator=0; // initialize
for (pitCat=1; pitCat<(132-1); pitCat++) // pitCat of lower cpt
if (amp[pitCat]>0) // select lower cpt
{
for (pitCatX=pitCat+1; pitCatX<132; pitCatX++) // pitCat of upper cpt
if (amp[pitCatX]>0)// select upper cpt
{
meanFreq=(freq[pitCatX]+freq[pitCat])/2; // in Hz
CBW=1.72*pow(meanFreq, 0.65); // H&K p. 5; this is slow to calculate!
CBint=(freq[pitCatX]-freq[pitCat])/CBW;
if (CBint<CBint1)
// (otherwise roughness is negligible) (save computing time)
{
ratio=CBint/CBint0;
standardCurve=pow((e*ratio)*exp(-1*ratio),index); // approximates P&L
numerator+=amp[pitCat]*amp[pitCatX]*standardCurve;
}
} // next pitCatX
denominator+=amp[pitCat]*amp[pitCat];
} // next pitCat
roughness=numerator/denominator;
printf ("Calculated roughness = %4.2f = %5.3f = %6.4f\n",roughness,roughness,roughness);
fprintf (out,"Calculated roughness = %4.2f = %5.3f = %6.4f\n",roughness,roughness,roughness);
} // next sonNum
fclose (in);
fclose(out);
} // end of main
Richard Parncutt
Centre for Systematic MusicologyGlacisstr. 27/1
8010 Graz, Austria