The signed data stream into AudioSynthWaveformModulated is cast to unsigned before multiplying
by the modulation_factor, so that discontinuities in phase appear, if the modulation index isn't a multiple of 180
degrees.
Pull request here: https://github.com/PaulStoffregen/Audio/pull/361
I'll add some 'scope shots soon to show the issue, but here's code to recreate it, tested on Teensy 4.0 and Audio shield rev D
Behaviour with stock Audio library:
and fixed:
by the modulation_factor, so that discontinuities in phase appear, if the modulation index isn't a multiple of 180
degrees.
Pull request here: https://github.com/PaulStoffregen/Audio/pull/361
I'll add some 'scope shots soon to show the issue, but here's code to recreate it, tested on Teensy 4.0 and Audio shield rev D
Code:
#include <Audio.h>
// Implement an FM voice, 3 stages in the algorithm in sequence, plus the envelope
class FMVoice
{
public:
FMVoice ()
{
new AudioConnection (modulator, 0, middle, 0) ; // on heap so destructors not called when this
new AudioConnection (middle, 0, finalosc, 0) ; // constructor exits!
new AudioConnection (finalosc, 0, env, 0) ;
}
AudioEffectEnvelope & output (void) // need this to connect up and to set envelope parameters
{
return env ;
}
// depth1 is modulation depth applied to middle,
// depth2 is modulation depth applied to final
// f2 and f3 are multipliers for fundamental used in middle and modulator respectively, small integers
void noteOn (float amp, float freq, float depth1, float depth2, int f2, int f3)
{
AudioNoInterrupts() ;
modulator.frequency (f3 * freq) ; // modulator is lowly sine wave generator, no begin method...
modulator.amplitude (depth1) ;
middle.phaseModulation (320) ; // mid range so no discontinuities
middle.begin (depth2, f2 * freq, WAVEFORM_SINE) ;
finalosc.phaseModulation (320) ; // mid range so no discontinuities
finalosc.begin (amp, freq, WAVEFORM_SINE) ;
env.noteOn () ;
AudioInterrupts () ;
}
void noteOff (void)
{
env.noteOff() ;
}
bool isActive (void)
{
return env.isActive() ;
}
void silence () // save cycles when the envelope has finished releasing
{
finalosc.amplitude (0) ;
middle.amplitude (0) ;
modulator.amplitude (0) ;
}
protected:
AudioSynthWaveformSine modulator ;
AudioSynthWaveformModulated middle ;
AudioSynthWaveformModulated finalosc ;
AudioEffectEnvelope env ;
};
// Create 16 waveforms, one for each MIDI channel, and the mixers
FMVoice fm ;
AudioOutputI2S audioOut;
AudioConnection patch (fm.output(), 0, audioOut, 0) ;
AudioConnection patch2 (fm.output(), 0, audioOut, 1) ;
// The Audio Shield chip
AudioControlSGTL5000 codec;
void setup_envelope (AudioEffectEnvelope & env)
{
env.attack(20);
env.hold(2);
env.decay(35);
env.sustain(0.8); // high sustain for more organ like sound.
env.release(100);
}
void setup()
{
Serial.begin(115200);
setup_envelope (fm.output()) ;
AudioMemory(20);
codec.enable();
codec.volume(0.4);
codec.lineOutLevel (13) ; // turn up line out to max
fm.noteOn (0.5, 100, 0.6, 0.4, 3, 2) ;
}
void loop()
{
}
Behaviour with stock Audio library:
and fixed:
Last edited: