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

Thread: Fun with FM synthesis

  1. #1
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,748

    Fun with FM synthesis

    I've played with some simple FM synthesis, and reworked the PlaySynthMusic example to use it,
    as well as trying to declutter all the toplevel boilerplate so it can be parameterized by a number of voices.

    The way I've addressed this is to bundle all the apparatus for a voice into a class, 3 oscillators and an
    envelope, like this:
    Code:
    // 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 (180) ; // mid range so no discontinuities
        middle.begin (depth2, f2 * freq, WAVEFORM_SINE) ;
        finalosc.phaseModulation (180) ; // 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 ;
    };
    The whole reworked example is attached as a zip - I'd recommend finding something less annoying as the example
    data than the William Tell overture (I found a midi file of Bach's Tocatta / Fugue in D-minor, which can be listened too
    repeatedly without going crazy! - and put it through miditones, but it was copyright so didn't include it here alas).

    The FM parameters I found after a little tinkering sound fairly organ-like and are pretty funky on the 'scope!

    Click image for larger version. 

Name:	fm_synth.png 
Views:	62 
Size:	8.7 KB 
ID:	21224

    Comments welcome - the topic of how to structure audio components hierarchically is relevant, found a few comments
    about this in a search on teensy FM synthesis.

    BTW this is on Teensy 4.0 with 150MHz clock, about 22% audio CPU for 16 voices which is pretty reasonable...

    It was quite fun to play with FM synthesis for the first time

    [ I was partly inspired by reading this https://cs.gmu.edu/~sean/book/synthesis/Synthesis.pdf ]
    Attached Files Attached Files

  2. #2
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,748
    This time I've made a library AudioStream object to implement 4-operator FM synthesis with 8 algorithms, like the DX9.

    The 4 operators have individual amplitude settings (which for modulators are the modulation depth), and the
    single input stream is used for overall modulation depth control which scales all the modulation depths. Operator
    4 has configurable feedback.

    The synth example code uses 16-fold polyphony and makes a Teensy 4.0 work pretty hard, note, I'm not sure a
    slower board will handle this.

    Code:
    class AudioSynthFM : public AudioStream
    {
    public:
      AudioSynthFM(void) : AudioStream(1, inputQueueArray) { ... }
    
      void algorithm (int alg_num) ;
      void frequencies (float freq1, float freq2, float freq3, float freq4) ;
      void amplitudes (float amp1, float amp2, float amp3, float amp4, float feedb) ;
      void enable (bool on) ;
      ...
    };
    algorithm() takes an integer in range 0 to 7 to set the FM algorithm, standard DX9 set.

    frequencies() takes Hz values for the four operators.

    amplitudes() takes 5 float values for the output amplitudes of the oscillators and operator 4's feedback amount.

    enable(false) is a convenience for shutting down the unit totally to save cycles, to be called when an output
    envelope drops from active to idle, and on noteOn.

    The example routes DC -> expression envelope -> AudioSynthFM -> note envelope -> mixers,
    and triggers both envelopes on noteOn.

    Code:
    zip fm2.zip FM2PlaySynth/* libraries/FM/*
      adding: FM2PlaySynth/FM2PlaySynth.ino (deflated 61%)
      adding: FM2PlaySynth/FMPlaySynth.h (deflated 47%)
      adding: FM2PlaySynth/william_tell_overture.c (deflated 83%)
      adding: libraries/FM/FM.cpp (deflated 62%)
      adding: libraries/FM/FM.h (deflated 49%)
    Attached Files Attached Files

  3. #3
    Member
    Join Date
    Jun 2019
    Location
    Sydney, Australia
    Posts
    42
    This looks very cool. Thanks for posting.

    I've been looking at the Korg Opsix - seems they've really thought out the interface well. It's quite difficult to make FM synthesis immediate in the way subtractive synthesis is. Makes me interested to whip something similar up with the Teensy.

    Ken

  4. #4
    Senior Member
    Join Date
    May 2018
    Posts
    150
    https://codeberg.org/dcoredump/MicroDexed

    This is a 6-OP-FM synth for the Teensy, compatible with a famous japanese synth from the 80's ;-)

  5. #5
    Member
    Join Date
    Jun 2019
    Location
    Sydney, Australia
    Posts
    42
    Quote Originally Posted by C0d3man View Post
    https://codeberg.org/dcoredump/MicroDexed

    This is a 6-OP-FM synth for the Teensy, compatible with a famous japanese synth from the 80's ;-)
    I have just returned to this and your code looks very promising, so I had a go with the Audio Library object version.

    I am using a Teensy 4.1 and the problems I describe occur with both the audio shield and I2S into a PCM5102A. I have also built with the CPU speed changing from 600MHz to 816MHz, and with the Optimise level at Fastest and at Debug.

    The first problem is with the SimplePlay example. It mostly worked, but the first note of one of the loops does not sound.
    Listen here: SimplePlay original
    If I add a small delay before the missing note it does sound reliably, but I'd like to understand what's going on and find a proper way to avoid it.

    More worrisome though, I made a slightly different demo and it is sounds very crackly and noisy.
    The code is below, and you can listen here: SimplePlay alternative.

    ***Edit: Found the stupid mistake in my code! I was retriggering the note repeatedly. Fixed and it plays as clean as a whistle. :-)

    <deleted broken code>

    Thanks,

    Ken
    Last edited by kallikak; 03-19-2022 at 10:45 PM.

  6. #6
    Senior Member
    Join Date
    May 2018
    Posts
    150
    Quote Originally Posted by kallikak View Post
    ***Edit: Found the stupid mistake in my code! I was retriggering the note repeatedly. Fixed and it plays as clean as a whistle. :-)
    Great that you were able to fix the error yourself!

    BTW, the Dexed audio object is still used in another project: https://github.com/probonopd/MiniDexed
    This is a bare-metal Dexed for the Raspi (based on the circle SDK, so there is no OS underneath). If you have a Raspi (preferably a Raspi >2), you can easily turn your Raspi into a 6-OP FM synth by creating an SD card and boot - only a USB MIDI keyboard is needed.

    The project is currently still in the startup phase, but already very promising. I'm curious what will come out of it in the next weeks/months.

    Regards, Holger

  7. #7
    Member
    Join Date
    Jun 2019
    Location
    Sydney, Australia
    Posts
    42
    I put together a Eurorack 4-operator FM voice using parts of the Dexed audio library object.

    Click image for larger version. 

Name:	demo.jpg 
Views:	10 
Size:	121.2 KB 
ID:	28146

    Because I wasn't trying to recreate a DX7, I dropped various things (like the LFO, pitch envelope, detune, effects), changed others (coarse frequency down to 1/128 like the OpSix, ADSR envelopes instead of the 4-level/4-rate DX7 ones), and it's CV driven monophonic rather than MIDI.

    Most of the code was pretty useful, though the highly interactive nature of my device meant I had to make changes to minimise digital artifacts and have the envelopes behave properly.

    Here's a short overview and demo.

    Thanks for the inspiration! :-)

  8. #8
    Member
    Join Date
    Jun 2019
    Location
    Sydney, Australia
    Posts
    42
    And now version 2.

    Click image for larger version. 

Name:	IMG_1739.jpg 
Views:	6 
Size:	137.1 KB 
ID:	28420

    Click image for larger version. 

Name:	IMG_1740.jpg 
Views:	4 
Size:	109.3 KB 
ID:	28421

Posting Permissions

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