[comp.sys.next] fix for Partials 'scaling' parameter bug

davem@cm-next-2.Stanford.EDU (08/04/90)

In a music kit Partials object, the only 'scaling' value that works in
computing a wavetable for the Partials object is 0.0, for
normalization scaling.  ("Normalization scaling" means that the values
in the wavetable are all scaled up or down by the same amount so that
the maximum value in the table is the maximum value that a DSP 24-bit
integer can hold, 2^23 - 1.)  Other values for the Partials's scaling
parameter produce an incomplete or erroneous wavetable when you ask
for the wavetable with the 'dataDSP' message or its kin.

Also, when you hand a Partials object to an oscillator unitgenerator
for specifying a wavetable, there is no way to specify a scaling value
to use.  So you're always stuck with normalization scaling anyway.

The method below, which I use in a SynthPatch subclass, fixes these
problems.  It works by generating the wavetable using the usual
normalization scaling.  Then it computes a scaling value, which is the
return value, that can be used as an 'amp' parameter (or multiplied by
another 'amp' parameter) to give true absolute scaling.  You need to
call this method whenever your wavetable changes, which is usually
whenever your Partials object changes.

You could also use the return value from this method to multiply each
wavetable entry by, rather that using it as an 'amp' parameter, but
then you must be extremely careful that the wavetable in the Partials
object never gets recomputed.  You also lose some resolution in the
wavetable this way.

				Dave Mellinger
				davem@cs.Stanford.EDU



/* This method necessary because the only non-buggy scaling available in
 * a Partials object is normalization scaling.  Since we want absolute
 * scaling, we have to first compute the samples
 * (with [partials dataDSP]), then compute a value to use for
 * scaling to the originally intended size.  This value, which is
 * the return value, can be used in an 'amp' parameter.
 */
#import <musickit/musickit.h>
#import <musickit/Partials.h>

- (double)ampForDataDSP:(id)partials
{
    DSPDatum *buf;
    int len, np, i;
    double xx, sum, phase;
    double *amps, *freqs, *phases;

    /* make dataDSP with normalization scaling */
    buf = [partials dataDSP];
    
    len		= [partials length];
    np		= [partials partialCount];
    amps	= [partials ampRatios];
    freqs	= [partials freqRatios];
    phases	= [partials phases];
    phase	= [partials defaultPhase];

    /* find where the maximum wavetable value occurs */
    for (i = 0; i < len; i++)
	if (buf[i] == DSP_I_MAXPOS || buf[i] == -DSP_I_MAXPOS)
	    break;
    if (i == len) {
	fprintf(stderr, "ampForDataDSP: Can't find max wavetable value.\n");
	return 1.0;
    }
    xx = (float)i / len * 2 * M_PI;

    /* compute correct (absolute scaling) value at that wavetable position */
    sum = 0.0;
    for (i = 0; i < np; i++)
	sum += amps[i] * sin(xx * freqs[i] + (phases ? phases[i] : phase));

    /* amplitudes > 1.0 usually cause overflow in the DSP */
    if (sum > 1.0 || sum < -1.0) {
	fprintf(stderr, "ampForDataDSP: sum is too large!\n");
	return 1.0;
    }
    return sum >= 0 ? sum : -sum;
}