frequencyModulation & phaseModulation at the sametime ?

chuinst

Member
Anyone can help me to find the way to make AudioSynthWaveformsModulated to working with 3 input
1. PitchEnvelope to control frequencyModulation.
2. Modulation Oscillator to control phaseModulation.
3. Lfo to control shape.

Code:
void AudioSynthWaveformModulated::update(void)
{
	audio_block_t *block, *moddata, *mod2data *shapedata;
	int16_t *bp, *bp2, *end;
	int32_t val1, val2;
	int16_t magnitude15;
	uint32_t i, ph, index, index2, scale, priorphase;
	const uint32_t inc = phase_increment;

	moddata = receiveReadOnly(0);
	shapedata = receiveReadOnly(1);
	mod2data = receiveReadOnly(2);

	// Pre-compute the phase angle for every output sample of this update
	ph = phase_accumulator;
	priorphase = phasedata[AUDIO_BLOCK_SAMPLES-1];
	if (moddata && modulation_type == 0) {
		// Frequency Modulation
		bp = moddata->data;
		bp2 = mod2data->data;
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			int32_t n = (*bp++) * modulation_factor; // n is # of octaves to mod
		        uint32_t n2 = ((uint32_t)(*bp2++)) * phase_factor;
			int32_t n2 = 
			int32_t ipart = n >> 27; // 4 integer bits
			n &= 0x7FFFFFF;          // 27 fractional bits
			#ifdef IMPROVE_EXPONENTIAL_ACCURACY
			// exp2 polynomial suggested by Stefan Stenzel on "music-dsp"
			// mail list, Wed, 3 Sep 2014 10:08:55 +0200
			int32_t x = n << 3;
			n = multiply_accumulate_32x32_rshift32_rounded(536870912, x, 1494202713);
			int32_t sq = multiply_32x32_rshift32_rounded(x, x);
			n = multiply_accumulate_32x32_rshift32_rounded(n, sq, 1934101615);
			n = n + (multiply_32x32_rshift32_rounded(sq,
				multiply_32x32_rshift32_rounded(x, 1358044250)) << 1);
			n = n << 1;
			#else
			// exp2 algorithm by Laurent de Soras
			// https://www.musicdsp.org/en/latest/Other/106-fast-exp2-approximation.html
			n = (n + 134217728) << 3;

			n = multiply_32x32_rshift32_rounded(n, n);
			n = multiply_32x32_rshift32_rounded(n, 715827883) << 3;
			n = n + 715827882;
			#endif
			uint32_t scale = n >> (14 - ipart);
			uint64_t phstep = (uint64_t)inc * scale;
			uint32_t phstep_msw = phstep >> 32;
			if (phstep_msw < 0x7FFE) {
				ph += phstep >> 16;
			} else {
				ph += 0x7FFE0000;
			}
			phasedata[i] = ph + n2;
			ph += inc;
		}
		release(moddata);
		release(mod2data);
 
I found this code example in musicdsp.org
It is the right way to do ?

Code:
/* Example implementation of digital oscillator with FM, PM, & AM */

#define PI 3.14159265358979
#define RADIANS_TO_INDEX (512.0 / (2.0 * PI))

typedef struct{     /* oscillator data */
    double freq;   /* oscillator frequency in radians per sample */
    double phase;  /* accumulated oscillator phase in radians */
    double wavetable[512]; /* waveform lookup table */
} OscilRec;


/* oscil - compute 1 sample of oscillator output whose freq. phase and
*    wavetable are in the OscilRec structure pointed to by orec.
*/
double oscil(orec, fm, pm, am)
    OscilRec *orec;  /* pointer to the oscil's data */
    double fm; /* frequency modulation input  in radians per sample */
    double pm; /* phase modulation input      in radians */
    double am; /* amplitude modulation input  in any units you want */
{
    long tableindex;            /* index into wavetable */
    double instantaneous_freq;  /* oscillator freq  + freq  modulation */
    double instantaneous_phase; /* oscillator phase + phase modulation */
    double output;              /* oscillator output */

    instantaneous_freq  = orec->freq  + fm; /* get instantaneous freq */
    orec->phase += instantaneous_freq;      /* accumulate phase */
    instantaneous_phase = orec->phase + pm; /* get instantaneous phase */

    /* convert to lookup table index */
    tableindex = RADIANS_TO_INDEX * instantaneous_phase;
    tableindex &= 511; /* make it mod 512 === eliminate multiples of 2*k*PI */

    output = orec->wavetable[tableindex] * am; /* lookup and mult by am input */

    return (output);  /* return oscillator output */
}
 
This is the result and it work. yeah!! but I'm not sure it have any drawback.

In synth_waveform.h, I add extra fuction call freqAndphaseModulation
Code:
void freqAndphaseModulation(float octaves, float degrees)
	{
		if (octaves > 12.0f) {
			octaves = 12.0f;
		} else if (octaves < 0.1f) {
			octaves = 0.1f;
		}
		modulation_factor = octaves * 4096.0f;

		if (degrees > 9000.0f) {
			degrees = 9000.0f;
		} else if (degrees < 30.0f) {
			degrees = 30.0f;
		}
		phase_factor = degrees * (float)(65536.0 / 180.0);	
		modulation_type = 2;
	}

Then, I implement freqAndphaseModulation_type in AudioSynthWaveformModulated::update
Code:
void AudioSynthWaveformModulated::update(void)
{
	audio_block_t *block, *freqModData, *phaseModData, *shapeData;
	int16_t *bp, *bp2, *end;
	int32_t val1, val2;
	int16_t magnitude15;
	uint32_t i, ph, index, index2, scale, priorphase;
	const uint32_t inc = phase_increment;

	freqModData = receiveReadOnly(0);
	phaseModData = receiveReadOnly(1);
	shapeData = receiveReadOnly(2);

	// Pre-compute the phase angle for every output sample of this update
	ph = phase_accumulator;
	priorphase = phasedata[AUDIO_BLOCK_SAMPLES-1];
	if (freqModData && modulation_type == 0) {
		// Frequency Modulation
		bp = freqModData->data;
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			int32_t n = (*bp++) * modulation_factor; // n is # of octaves to mod
			int32_t ipart = n >> 27; // 4 integer bits
			n &= 0x7FFFFFF;          // 27 fractional bits
			#ifdef IMPROVE_EXPONENTIAL_ACCURACY
			// exp2 polynomial suggested by Stefan Stenzel on "music-dsp"
			// mail list, Wed, 3 Sep 2014 10:08:55 +0200
			int32_t x = n << 3;
			n = multiply_accumulate_32x32_rshift32_rounded(536870912, x, 1494202713);
			int32_t sq = multiply_32x32_rshift32_rounded(x, x);
			n = multiply_accumulate_32x32_rshift32_rounded(n, sq, 1934101615);
			n = n + (multiply_32x32_rshift32_rounded(sq,
				multiply_32x32_rshift32_rounded(x, 1358044250)) << 1);
			n = n << 1;
			#else
			// exp2 algorithm by Laurent de Soras
			// https://www.musicdsp.org/en/latest/Other/106-fast-exp2-approximation.html
			n = (n + 134217728) << 3;

			n = multiply_32x32_rshift32_rounded(n, n);
			n = multiply_32x32_rshift32_rounded(n, 715827883) << 3;
			n = n + 715827882;
			#endif
			uint32_t scale = n >> (14 - ipart);
			uint64_t phstep = (uint64_t)inc * scale;
			uint32_t phstep_msw = phstep >> 32;
			if (phstep_msw < 0x7FFE) {
				ph += phstep >> 16;
			} else {
				ph += 0x7FFE0000;
			}
			phasedata[i] = ph;
			Serial.println("freq");
		}
		release(freqModData);
		release(phaseModData);
	} else if (phaseModData && modulation_type == 1) {
		// Phase Modulation
		bp2 = phaseModData->data;
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			// more than +/- 180 deg shift by 32 bit overflow of "n"
		        uint32_t n = ((uint32_t)(*bp2++)) * phase_factor;
			phasedata[i] = ph + n;
			ph += inc;
			Serial.println("phase");
		}
		release(freqModData);
		release(phaseModData);
	} else if (freqModData && phaseModData && modulation_type == 2) {
		bp = freqModData->data;
		bp2 = phaseModData->data;
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			int32_t n = (*bp++) * modulation_factor; // n is # of octaves to mod
		    uint32_t n2 = ((uint32_t)(*bp2++)) * phase_factor;
			int32_t ipart = n >> 27; // 4 integer bits
			n &= 0x7FFFFFF;          // 27 fractional bits
			#ifdef IMPROVE_EXPONENTIAL_ACCURACY
			// exp2 polynomial suggested by Stefan Stenzel on "music-dsp"
			// mail list, Wed, 3 Sep 2014 10:08:55 +0200
			int32_t x = n << 3;
			n = multiply_accumulate_32x32_rshift32_rounded(536870912, x, 1494202713);
			int32_t sq = multiply_32x32_rshift32_rounded(x, x);
			n = multiply_accumulate_32x32_rshift32_rounded(n, sq, 1934101615);
			n = n + (multiply_32x32_rshift32_rounded(sq,
				multiply_32x32_rshift32_rounded(x, 1358044250)) << 1);
			n = n << 1;
			#else
			// exp2 algorithm by Laurent de Soras
			// https://www.musicdsp.org/en/latest/Other/106-fast-exp2-approximation.html
			n = (n + 134217728) << 3;

			n = multiply_32x32_rshift32_rounded(n, n);
			n = multiply_32x32_rshift32_rounded(n, 715827883) << 3;
			n = n + 715827882;
			#endif
			uint32_t scale = n >> (14 - ipart);
			uint64_t phstep = (uint64_t)inc * scale;
			uint32_t phstep_msw = phstep >> 32;
			if (phstep_msw < 0x7FFE) {
				ph += phstep >> 16;
			} else {
				ph += 0x7FFE0000;
			}
			phasedata[i] = ph + n2;
			Serial.println("freqAndphase");
		}
		release(freqModData);
		release(phaseModData);
	}else {
		// No Modulation Input
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			phasedata[i] = ph;
			ph += inc;
		}
	}
 
Last edited:
Hello,

I try to add a third input to the AudioSynthWaveformsModulated object, just the same way you did.
I tried your code, but I can't make it work. I miss something...

Could you give me to full synth_waveform.h and synth_waveform.ccp files ?
It would be very kind.
Emmanuel
 
Back
Top