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

Thread: FM synthesis block, audio design tool.

  1. #1

    FM synthesis block, audio design tool.

    Hello, I am currently working on a multi-function synth for the Teensy 4.0. Currently, I am designing an FM patch and I had a question regarding the design of an operator style block using the audio design tool. Essentially, I would like to know if either of the blocks below is more "correct" for the design and interconnection of two operators, or if the two design blocks are functionally equivalent. The blocks differ from the diagram in that there is more routing complexity and each operator can modulate itself.

    Click image for larger version. 

Name:	blockFMTeensy.png 
Views:	67 
Size:	80.5 KB 
ID:	18685
    Name:  blockFM.JPG
Views: 167
Size:  14.6 KB
    Last edited by RABB17; 01-09-2020 at 06:47 PM.

  2. #2
    Alright, so today I have some free time so I'm going to code up the second block today and compare scope outputs between block 1 and block 2 with Ableton's Operator and FM8. I'll post my results.

  3. #3
    Senior Member
    Join Date
    Feb 2015
    Posts
    217
    Well, neither of those look correct to me. Try something like this:

    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    // GUItool: begin automatically generated code
    AudioSynthWaveform       osc2;      //xy=387.50000381469727,256.25000381469727
    AudioEffectEnvelope      envelope1;      //xy=543.7500076293945,253.7500057220459
    AudioSynthWaveformModulated osc1;   //xy=735.0000076293945,260.00000381469727
    AudioEffectEnvelope      envelope2;      //xy=920.0000114440918,261.25000381469727
    AudioOutputI2S           i2s1;           //xy=1125,255
    AudioConnection          patchCord1(osc2, envelope1);
    AudioConnection          patchCord2(envelope1, 0, osc1, 0);
    AudioConnection          patchCord3(osc1, envelope2);
    AudioConnection          patchCord4(envelope2, 0, i2s1, 0);
    AudioConnection          patchCord5(envelope2, 0, i2s1, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=1125.000015258789,301.2500057220459
    // GUItool: end automatically generated code
    osc2 is the modulator, and osc1 is the carrier. Notice how osc2 is sending its output into the modulation input of osc1.

    This code won't do anything by itself, you will still need to write some kind of interface (midi, buttons, etc) and setup code (envelopes, etc) to make it actually do something. One other note, in the Teensy Audio library, the Envelope block is basically a VCA plus an envelope generator in one object, it's very handy.

  4. #4
    Thank you for the reply... If the above interconnection scheme is as I've included below, it is pretty much the same as block 1, that I posted. However, in my block design, the amps and mixers are used as internal routing cables(think operator a to b vs b to a), and waveformMod are used as modulators as opposed to waveform because it will allow for feedback modulation as in Massive's FM8. You can simply set the incoming modulator input to 0 amplitude to get a pure waveform from the waveformMod block. The first block does in fact work fine I have already coded up a midi synth example. I just haven't had time to compare scope results to existing FM vst's for comparison. The hmm kernel of the ? I was asking was really the diff between multiplying in the envelope with the 'vca' multiply module and just inputing the waveform into the asdr envelope. As someone recently ported microdexxed to the teensy I was hoping someone had had experience setting up the operator blocks. Apologies if I misinterpreted your reply

    Click image for larger version. 

Name:	Capture1.JPG 
Views:	8 
Size:	15.1 KB 
ID:	18694
    My block diagrams were not intended to be functional as displayed, neither mixer is routed to an output mixer array which would then be needed to be routed to i2s or some other out of course. I am intending to chain 8 operators for 1 voice, which the t4 should be able to do easily I believe if the 64 voice polyphony another user posted for microdexxed was correct
    Last edited by RABB17; 01-10-2020 at 05:57 PM. Reason: clarification

  5. #5
    ie. what is the difference between A and B, as the multiply module is described as a 'VCA'

    Click image for larger version. 

Name:	simplified.png 
Views:	12 
Size:	16.0 KB 
ID:	18697 Name:  Capture2.JPG
Views: 123
Size:  26.1 KB
    Last edited by RABB17; 01-10-2020 at 05:40 PM.

  6. #6
    Quote Originally Posted by wcalvert View Post
    the Envelope block is basically a VCA plus an envelope generator in one object, it's very handy.
    Skipped right over this part apparently, that's exactly what I wanted to know thanks a bunch!

  7. #7
    So I decided to run a few tests to compare these two blocks. They both appear to function, however, the FFT's for the two seem to differ somewhat in various locations. I have attached a simple sketch to illustrate. Perhaps someone can give me some insight into this issue? Also, when waveform type 4(triangle) is selected as the carrier, there seems to be no output in either case, which also has me a bit stumped. **LOUD VOLUME, PLS DO NOT LISTEN THROUGH SPEAKERS W/O ADJUSTING**
    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    // GUItool: begin automatically generated code
    AudioSynthWaveformDc     dc1;            //xy=55,122
    AudioSynthWaveform       waveform1;      //xy=183,52
    AudioEffectEnvelope      envelope2;      //xy=187,122
    AudioSynthWaveform       waveform2;      //xy=188,161
    AudioEffectEnvelope      envelope1;      //xy=328,52
    AudioEffectMultiply      multiply1;      //xy=332,138
    AudioSynthWaveformModulated waveformMod2;   //xy=490,145
    AudioSynthWaveformModulated waveformMod1;   //xy=492,58
    AudioEffectEnvelope      envelope4;      //xy=655,145
    AudioEffectEnvelope      envelope3;      //xy=656,58
    AudioMixer4              mixer1;         //xy=833,93
    AudioAnalyzeFFT1024      myFFT;      //xy=984,163
    AudioOutputI2S           i2s1;           //xy=991,100
    AudioConnection          patchCord1(dc1, envelope2);
    AudioConnection          patchCord2(waveform1, envelope1);
    AudioConnection          patchCord3(envelope2, 0, multiply1, 0);
    AudioConnection          patchCord4(waveform2, 0, multiply1, 1);
    AudioConnection          patchCord5(envelope1, 0, waveformMod1, 0);
    AudioConnection          patchCord6(multiply1, 0, waveformMod2, 0);
    AudioConnection          patchCord7(waveformMod2, envelope4);
    AudioConnection          patchCord8(waveformMod1, envelope3);
    AudioConnection          patchCord9(envelope4, 0, mixer1, 1);
    AudioConnection          patchCord10(envelope3, 0, mixer1, 0);
    AudioConnection          patchCord11(mixer1, 0, i2s1, 0);
    AudioConnection          patchCord12(mixer1, 0, i2s1, 1);
    AudioConnection          patchCord13(mixer1, myFFT);
    AudioControlSGTL5000     sgtl5000_1;     //xy=995,20
    // GUItool: end automatically generated code
    
    int numWavType = 9;
    float freq[] = {0, 130.8, 261.6, 523.2, 1046.4, 2092.8, 4185.6, 8371.2, 16472.4, 33483.8, 66969.6, 133939.2, 267878.4, 535756.8};
    //float freq[] = {0, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000};
    float n = 0.0;
    
    void setup() {
      Serial.begin(115200);
    
      AudioMemory(20);
    
      sgtl5000_1.enable();
      sgtl5000_1.volume(1.0);
    
      dc1.amplitude(1.0);
    
      mixer1.gain(1, 1.0);
      mixer1.gain(0, 0.0);
    
      waveform1.frequency(130.8);
      waveform1.pulseWidth(.15);
      waveform1.amplitude(1.0);
      waveform1.begin(WAVEFORM_SINE);
    
      waveform2.frequency(130.8);
      waveform2.pulseWidth(.15);
      waveform2.amplitude(1.0);
      waveform2.begin(WAVEFORM_SINE);
    
      waveformMod1.frequency(130.8);
      waveformMod1.amplitude(1.0);
      waveformMod1.frequencyModulation(1.0);
      waveformMod1.begin(WAVEFORM_SINE);
    
      waveformMod2.frequency(130.8);
      waveformMod2.amplitude(1.0);
      waveformMod2.frequencyModulation(1.0);
      waveformMod2.begin(WAVEFORM_SINE);
    
      envelope1.attack(10);
      envelope1.decay(0);
      envelope1.hold(140);
      envelope1.release(50);
      envelope1.sustain(1.0);
      //envelope1.on
    
      envelope2.attack(10);
      envelope2.decay(0);
      envelope2.hold(140);
      envelope2.release(50);
      envelope2.sustain(1.0);
    
      envelope3.attack(10);
      envelope3.decay(0);
      envelope3.hold(140);
      envelope3.release(50);
      envelope3.sustain(1.0);
    
      envelope4.attack(10);
      envelope4.decay(0);
      envelope4.hold(140);
      envelope4.release(50);
      envelope4.sustain(1.0);
    
      myFFT.windowFunction(AudioWindowHanning1024);
    
        //uncomment for 1st block, comment for 2nd
        /*
        mixer1.gain(0, 1.0);
        mixer1.gain(1, 0.0);
        for (int i = 0; i < numWavType - 1; i++)
        {
          waveformMod1.begin(i);
          Serial.print("Carrier Waveform Type: ");
          Serial.print(i);
          Serial.print("\n");
          for (int j = 1; j < 2; j++)
          {
            waveformMod1.frequency(freq[j]);
            //Serial.print("Carrier Freq: ");
            //Serial.print(freq[j]);
            //Serial.print("\n");
            for (int k = 1; k < 4; k++) //13
            {
              //Serial.print("Octave ratio: ");
              //Serial.print(k);
              //Serial.print("\n");
              waveformMod1.frequencyModulation((float)k);
              for (int l = 0; l < numWavType - 1; l++)
              {
                //Serial.print("Mod Waveform Type: ");
                //Serial.print(l);
                //Serial.print("\n");
                waveform1.begin(l);
                for (int m = 0; m < 13; m++) //14
                {
                  //Serial.print("Mod Freq: ");
                  //Serial.print(freq[m]);
                  //Serial.print("\n");
                  waveform1.frequency(freq[m]);
                  envelope3.noteOn();
                  envelope1.noteOn();
                  delay(150);
                  envelope3.noteOff();
                  envelope1.noteOff();
                  delay(50);
                  Serial.print("FFT: ");
                  for (int p = 0; p < 40; p++)
                  {
                    n = myFFT.read(p);
                    if (n >= 0.01)
                    {
                      Serial.print(n);
                      Serial.print(" ");
                    }
                    else
                    {
                      Serial.print("  -  "); 
                    }
                  }
                  Serial.println();
                }
              }
            }
          }
        }
          Serial.end();
          */
      
      //2nd block, comment out below to run 1st block
      mixer1.gain(1, 1.0);
      mixer1.gain(0, 0.0);
      for (int i = 0; i < numWavType - 1; i++)
      {
        waveformMod2.begin(i);
        Serial.print("Carrier Waveform Type: ");
        Serial.print(i);
        Serial.print("\n");
        for (int j = 1; j < 2; j++)
        {
          waveformMod2.frequency(freq[j]);
          //Serial.print("Carrier Freq: ");
          //Serial.print(freq[j]);
          //Serial.print("\n");
          for (int k = 1; k < 4; k++) //13
          {
            //Serial.print("Octave ratio: ");
            //Serial.print(k);
            //Serial.print("\n");
            waveformMod2.frequencyModulation((float)k);
            for (int l = 0; l < numWavType - 1; l++)
            {
              //Serial.print("Mod Waveform Type: ");
              //Serial.print(l);
              //Serial.print("\n");
              waveform2.begin(l);
              for (int m = 0; m < 13; m++) //14
              {
                //Serial.print("Mod Freq: ");
                //Serial.print(freq[m]);
                //Serial.print("\n");
                waveform2.frequency(freq[m]);
                envelope4.noteOn();
                envelope2.noteOn();
                delay(150);
                envelope4.noteOff();
                envelope2.noteOff();
                delay(50);
                Serial.print("FFT: ");
                for (int p = 0; p < 40; p++)
                {
                  n = myFFT.read(p);
                  if (n >= 0.01)
                  {
                    Serial.print(n);
                    Serial.print(" ");
                  }
                  else
                  {
                    Serial.print("  -  "); // don't print "0.00"
                  }
                }
                Serial.println();
              }
            }
          }
        }
      }
      Serial.end();
    }
    
    void loop()
    {
    }

  8. #8
    Well, it seems the difference in outputs is due to the differences in effect_multiply.cpp and effect_envelope.cpp. At this point, I think I'm just going to run with effect_envelope solution on a hunch. It's pretty hard to objectively compare fidelity issues with so many variants possible. If I'm feeling ambitious with nothing to do later I will dump both audio blocks directly and compare them to a matlab simulation, but as it stands I think the library default should suffice.

    TLR The blocks both function to the same effect in regards to being a VCA, however, there are frequency variations across permutations of carriers/modulators and I'm not sure as of yet which is better sounding, objective as that metric is.

Posting Permissions

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