Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 3 of 3

Thread: phase modulation broken

  1. #1
    Senior Member
    Join Date
    Jul 2020
    Posts
    460

    phase modulation broken

    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
    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:
    Click image for larger version. 

Name:	pm_broken.png 
Views:	9 
Size:	18.9 KB 
ID:	21225

    and fixed:
    Click image for larger version. 

Name:	pm_working.png 
Views:	10 
Size:	15.9 KB 
ID:	21226
    Last edited by MarkT; 08-01-2020 at 03:39 AM. Reason: change modulation index to modulation factor

  2. #2
    Senior Member
    Join Date
    Jul 2020
    Posts
    460
    On further reflection this is not the correct fix, the multiply needs to avoid overflow, so should be done as unsigned,
    so the signed phase data ought to be cast to unsigned, have 0x8000 added, then multiplied.

    Ah, perhaps not, I think its as simple as casting direct to 32 bit unsigned, branch ammended...
    Last edited by MarkT; 08-01-2020 at 03:57 AM.

  3. #3
    Senior Member
    Join Date
    Jul 2020
    Posts
    460
    Quote Originally Posted by MarkT View Post
    Sorry, that's the wrong one (and a bogus pull request, please delete!).

    The correct one is: https://github.com/PaulStoffregen/Audio/pull/362

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •