New Teensy 4.1 DIY Synthesizer

Jeannie with self-made wooden side panels..

20230513-135527.jpg


Case-Metall.jpg


20230516-184103.jpg


20230519-103306-1.jpg


20230516-192917.jpg


20230513-150127.jpg


20230518-200809.jpg


Seite.jpg


20230518-194109.jpg


20230519-081902.jpg


Video: https://youtu.be/0iixKh_5uvA
 
Looks great Rolf! All synt projects need wood side panels. Looks like you color-matched it to your Deepmind as well.

Any recommendations on a (EU) supplier/manufacturer for something similar to that sheet metal enclosure part?
 
@MrCanvas - good experience with Grawart on a short run of 10 custom cases, I used FreeCAD and the SheetMetal workbench to work out a design.
 
[FONT=&quot]There is a virtual midi controller for the Jeannie. I am still working on it...

[/FONT]
The midi controller receives and sends the data via USB. Some functions are too special and can only be programmed or set on the Jeannie.
Now I want to display the prg no. and name when switching. Maybe someone knows how to program the Ctrlr App. For help I would be grateful in any case.


[FONT=&quot]
[/FONT]
Jeannie-Ctrl-1-01.png
 
Hallo Teensy friends..

I would like to implement the Mutable Braids Vowel sound into my Jeannie DIY synthesizer. It sounds very good without pitch modulation. When I turn the pitch wheel up I get distortions in the sound. There seems to be an overflow of increments in the oscillator. I use the Teensy4.1 Audio lib and the AudioSynthWaveformModulated as an oscillator.

Vowel Sound test without pitch modulation. I change parameter_a for vokal and paramter_b for format_shift

Vowel Sound test with pitch modulation..

Vowel Sound with old shruthi core inbuild Jeannie..
Shruthi has only one formant parameter

Thanks for help..


synth_waveform.h
Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 *
 * Development of this audio library was funded by PJRC.COM, LLC by sales of
 * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
 * open source software by purchasing Teensy or other PJRC products.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 
 ElectroTechnique 2020
 Added WAVEFORM_SILENT, sync()                           
 */


#ifndef synth_waveform_h_
#define synth_waveform_h_


#include <Arduino.h>
#include "AudioStream.h"
#include "arm_math.h"




// waveforms.c
extern "C" {
extern const int16_t AudioWaveformSine[257];
}


extern uint8_t LFO1mode;
extern uint8_t LFO2mode;
extern uint8_t LFO3mode;
extern boolean lfo1oneShoot; 
extern boolean lfo2oneShoot;
extern boolean lfo3oneShoot;  
extern uint8_t LFO1phase;
extern uint8_t LFO2phase;
extern uint8_t LFO3phase;
extern uint8_t lfo1ph;
extern uint8_t lfo2ph;
extern uint8_t lfo3ph;
extern int8_t Lfo3Modoutput;
extern boolean LFO1randomFlag;
extern boolean LFO2randomFlag;
extern boolean LFO3randomFlag;
extern uint32_t LFO1delayTime;
extern uint32_t LFO2delayTime;
extern uint32_t LFO3delayTime;
extern uint8_t SupersawSpreadA;
extern uint8_t SupersawSpreadB;
extern float Supersaw_gain1A;
extern float Supersaw_gain2A;
extern float Supersaw_gain1B;
extern float Supersaw_gain2B;
extern uint32_t rng_value32;
extern 


// Oscillator
#define WAVEFORM_SINE                            0
#define WAVEFORM_TRIANGLE                        1
#define WAVEFORM_SAWTOOTH                        3
#define WAVEFORM_SQUARE                            2
#define WAVEFORM_PULSE                            4
#define WAVEFORM_SAWTOOTH_REVERSE                5
#define WAVEFORM_SAMPLE_HOLD                    6
#define WAVEFORM_TRIANGLE_VARIABLE                7
#define WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE        8
#define WAVEFORM_BANDLIMIT_SAWTOOTH                9
#define WAVEFORM_BANDLIMIT_SQUARE                10
#define WAVEFORM_BANDLIMIT_PULSE                11
#define WAVEFORM_ARBITRARY                        12
#define WAVEFORM_SAWTOOTH2                        30
#define WAVEFORM_SILENT                            19


// LFO1 (OSC)
#define WAVEFORM_ARBITRARY1                        20
#define WAVEFORM_SAMPLE_HOLD1                    21
#define PWM_WAVEFORM_SINE                        26
#define PWM_WAVEFORM_TRIANGLE                    27
#define PWM_WAVEFORM_SAWTOOTH                    28
#define PWM_WAVEFORM_SQUARE                        29


const uint8_t wav_res_vowel_data[] = {
    27,     40,     89,     15,     13,      1,      0,     18,
    51,     62,     13,     12,      6,      0,     15,     69,
    93,     14,     12,      7,      0,     10,     84,    110,
    13,     10,      8,      0,     23,     44,     87,     15,
    12,      1,      0,     13,     29,     80,     13,      8,
    0,      0,      6,     46,     81,     12,      3,      0,
    0,      9,     51,     95,     15,      3,      0,      3,
    6,     73,     99,      7,      3,     14,      9,
};


const int16_t wav_formant_square[] = {
    0,      1,      1,      2,      2,      3,      3,      4,
    4,      5,      6,      8,      9,     11,     13,     16,
    0,      1,      1,      2,      2,      3,      3,      4,
    4,      5,      6,      8,      9,     11,     13,     16,
    0,      1,      1,      2,      2,      3,      3,      4,
    4,      5,      6,      8,      9,     11,     13,     16,
    0,      1,      1,      2,      2,      3,      3,      4,
    4,      5,      6,      8,      9,     11,     13,     16,
    0,      1,      1,      2,      2,      3,      3,      4,
    4,      5,      6,      8,      9,     11,     13,     16,
    0,      1,      1,      2,      2,      3,      3,      4,
    4,      5,      6,      8,      9,     11,     13,     16,
    0,      1,      1,      2,      2,      3,      3,      4,
    4,      5,      6,      8,      9,     11,     13,     16,
    0,      1,      1,      2,      2,      3,      3,      4,
    4,      5,      6,      8,      9,     11,     13,     16,
    0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
    -4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
    0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
    -4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
    0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
    -4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
    0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
    -4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
    0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
    -4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
    0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
    -4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
    0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
    -4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
    0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
    -4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
};


const int16_t wav_formant_sine[] = {
    0,      0,      0,      0,      0,      0,      0,      0,
    0,      0,      0,      0,      0,      0,      0,      0,
    0,      2,      2,      3,      3,      4,      5,      6,
    7,      8,     10,     12,     14,     17,     20,     24,
    0,      3,      4,      5,      6,      7,      9,     10,
    12,     15,     18,     21,     26,     31,     37,     45,
    0,      4,      5,      6,      8,      9,     11,     13,
    16,     19,     23,     28,     34,     40,     49,     58,
    0,      5,      6,      7,      8,     10,     12,     15,
    17,     21,     25,     30,     36,     44,     53,     63,
    0,      4,      5,      6,      8,      9,     11,     13,
    16,     19,     23,     28,     34,     40,     49,     58,
    0,      3,      4,      5,      6,      7,      9,     10,
    12,     15,     18,     21,     26,     31,     37,     45,
    0,      2,      2,      3,      3,      4,      5,      6,
    7,      8,     10,     12,     14,     17,     20,     24,
    0,      0,      0,      0,      0,      0,      0,      0,
    0,      0,      0,      0,      0,      0,      0,      0,
    0,     -2,     -2,     -3,     -3,     -4,     -5,     -6,
    -7,     -8,    -10,    -12,    -14,    -17,    -20,    -24,
    0,     -3,     -4,     -5,     -6,     -7,     -9,    -10,
    -12,    -15,    -18,    -21,    -26,    -31,    -37,    -45,
    0,     -4,     -5,     -6,     -8,     -9,    -11,    -13,
    -16,    -19,    -23,    -28,    -34,    -40,    -49,    -58,
    0,     -5,     -6,     -7,     -8,    -10,    -12,    -15,
    -17,    -21,    -25,    -30,    -36,    -44,    -53,    -63,
    0,     -4,     -5,     -6,     -8,     -9,    -11,    -13,
    -16,    -19,    -23,    -28,    -34,    -40,    -49,    -58,
    0,     -3,     -4,     -5,     -6,     -7,     -9,    -10,
    -12,    -15,    -18,    -21,    -26,    -31,    -37,    -45,
    0,     -2,     -2,     -3,     -3,     -4,     -5,     -6,
    -7,     -8,    -10,    -12,    -14,    -17,    -20,    -24,
};


struct PhonemeDefinition {
    uint8_t formant_frequency[3];
    uint8_t formant_amplitude[3];
};


static const PhonemeDefinition vowels_data[9] = {
    { { 27,  40,  89 }, { 15,  13,  1 } },
    { { 18,  51,  62 }, { 13,  12,  6 } },
    { { 15,  69,  93 }, { 14,  12,  7 } },
    { { 10,  84, 110 }, { 13,  10,  8 } },
    { { 23,  44,  87 }, { 15,  12,  1 } },
    { { 13,  29,  80 }, { 13,   8,  0 } },
    { {  6,  46,  81 }, { 12,   3,  0 } },
    { {  9,  51,  95 }, { 15,   3,  0 } },
    { {  6,  73,  99 }, {  7,   3,  14 } }
};


static const PhonemeDefinition consonant_data[8] = {
    { { 6, 54, 121 }, { 9,  9,  0 } },
    { { 18, 50, 51 }, { 12,  10,  5 } },
    { { 11, 24, 70 }, { 13,  8,  0 } },
    { { 15, 69, 74 }, { 14,  12,  7 } },
    { { 16, 37, 111 }, { 14,  8,  1 } },
    { { 18, 51, 62 }, { 14,  12,  6 } },
    { { 6, 26, 81 }, { 5,  5,  5 } },
    { { 6, 73, 99 }, { 7,  10,  14 } },
};


typedef struct step_state
{
  int offset ;
  bool positive ;
} step_state ;




class BandLimitedWaveform
{
public:
  BandLimitedWaveform (void) ;
  int16_t generate_sawtooth (uint32_t new_phase, int i) ;
  int16_t generate_square (uint32_t new_phase, int i) ;
  int16_t generate_pulse (uint32_t new_phase, uint32_t pulse_width, int i) ;
  void init_sawtooth (uint32_t freq_word) ;
  void init_square (uint32_t freq_word) ;
  void init_pulse (uint32_t freq_word, uint32_t pulse_width) ;
  


private:
  int32_t lookup (int offset) ;
  void insert_step (int offset, bool rising, int i) ;
  int32_t process_step (int i) ;
  int32_t process_active_steps (uint32_t new_phase) ;
  int32_t process_active_steps_saw (uint32_t new_phase) ;
  int32_t process_active_steps_pulse (uint32_t new_phase, uint32_t pulse_width) ;
  void new_step_check_square (uint32_t new_phase, int i) ;
  void new_step_check_pulse (uint32_t new_phase, uint32_t pulse_width, int i) ;
  void new_step_check_saw (uint32_t new_phase, int i) ;
  
  
  uint32_t phase_word ;
  int32_t dc_offset ;
  step_state states [32] ; // circular buffer of active steps
  int newptr ;         // buffer pointers into states, AND'd with PTRMASK to keep in buffer range.
  int delptr ;
  int32_t  cyclic[16] ;    // circular buffer of output samples
  bool pulse_state ;
  uint32_t sampled_width ; // pulse width is sampled once per waveform
};




class AudioSynthWaveformTS : public AudioStream
{
public:
  AudioSynthWaveformTS(void) : AudioStream(0,NULL),
    phase_accumulator(0), phase_increment(0), phase_offset(0),
    magnitude(0), pulse_width(0x40000000),
    arbdata(NULL), sample(0), tone_type(WAVEFORM_SINE),
    tone_offset(0),syncFlag(0) {
  }


  void frequency(float freq) {
    if (freq < 0.0) {
      freq = 0.0;
    } else if (freq > AUDIO_SAMPLE_RATE_EXACT / 2) {
      freq = AUDIO_SAMPLE_RATE_EXACT / 2;
    }
    phase_increment = freq * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT);
    if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000;
  }
  void phase(float angle) {
    if (angle < 0.0) {
      angle = 0.0;
    } else if (angle > 360.0) {
      angle = angle - 360.0;
      if (angle >= 360.0) return;
    }
    phase_offset = angle * (4294967296.0 / 360.0);
  }
  void sync() {
    syncFlag = 1;
  }       
  void amplitude(float n) { // 0 to 1.0
    if (n < 0) {
      n = 0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    magnitude = n * 65536.0;
  }
  void offset(float n) {
    if (n < -1.0) {
      n = -1.0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    tone_offset = n * 32767.0;
  }
  void pulseWidth(float n) {  // 0.0 to 1.0
    if (n < 0) {
      n = 0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    pulse_width = n * 4294967296.0;
  }
  void begin(short t_type) {
    phase_offset = 0;
    tone_type = t_type;
    if (t_type == WAVEFORM_BANDLIMIT_SQUARE)
      band_limit_waveform.init_square (phase_increment) ;
    else if (t_type == WAVEFORM_BANDLIMIT_PULSE)
      band_limit_waveform.init_pulse (phase_increment, pulse_width) ;
    else if (t_type == WAVEFORM_BANDLIMIT_SAWTOOTH || t_type == WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE)
      band_limit_waveform.init_sawtooth (phase_increment) ;
  }
  void begin(float t_amp, float t_freq, short t_type) {
    amplitude(t_amp);
    frequency(t_freq);
    phase_offset = 0;
    begin (t_type);
  }
  void arbitraryWaveform(const int16_t *data, float maxFreq) {
    arbdata = data;
  }
  virtual void update(void);


private:
  uint32_t phase_accumulator;
  uint32_t phase_increment;
  uint32_t phase_offset;
  int32_t  magnitude;
  uint32_t pulse_width;
  const int16_t *arbdata;
  int16_t  sample; // for WAVEFORM_SAMPLE_HOLD
  int16_t  sample2; // for WAVEFORM_SAMPLE_HOLD and oneShot
  short  tone_type;
  int16_t tone_offset;
  int16_t syncFlag;
  BandLimitedWaveform band_limit_waveform;
};




class AudioSynthWaveformModulatedTS : public AudioStream
{
public:
  AudioSynthWaveformModulatedTS(void) : AudioStream(2, inputQueueArray),
    phase_accumulator(0), phase_increment(0), modulation_factor(32768),
    magnitude(0), arbdata(NULL), sample(0), tone_offset(0),
    tone_type(WAVEFORM_SINE), modulation_type(0), syncFlag(0), osc_par_a(0),
    osc_par_b(0)
     {
  }


  void frequency(float freq) {
   freq = freq / 2.0f;    // only for tone_type Vowel
    if (freq < 0.0) {
      freq = 0.0;
    } else if (freq > AUDIO_SAMPLE_RATE_EXACT / 2) {
      freq = AUDIO_SAMPLE_RATE_EXACT / 2;
    }
    phase_increment = freq * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT);
    //if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000;
  }
  void amplitude(float n) { // 0 to 1.0
    if (n < 0) {
      n = 0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    magnitude = n * 65536.0;
  }
   void sync() {
    syncFlag = 1;
  }              
  void offset(float n) {
    if (n < -1.0) {
      n = -1.0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    tone_offset = n * 32767.0;
  }
  void begin(short t_type) {
    tone_type = t_type;
    if (t_type == WAVEFORM_BANDLIMIT_SQUARE)
      band_limit_waveform.init_square (phase_increment) ;
    else if (t_type == WAVEFORM_BANDLIMIT_PULSE)
      band_limit_waveform.init_pulse (phase_increment, 0x80000000u) ;
    else if (t_type == WAVEFORM_BANDLIMIT_SAWTOOTH || t_type == WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE)
      band_limit_waveform.init_sawtooth (phase_increment) ;
  }
  void begin(float t_amp, float t_freq, short t_type) {
    amplitude(t_amp);
    frequency(t_freq);
    begin (t_type) ;
  }
  void arbitraryWaveform(const int16_t *data, float maxFreq) {
    arbdata = data;
  }
  void frequencyModulation(float octaves) {
    if (octaves > 12.0) {
      octaves = 12.0;
    } else if (octaves < 0.1) {
      octaves = 0.1;
    }
    modulation_factor = octaves * 4096.0;
    modulation_type = 0;
  }
  void phaseModulation(float degrees) {
    if (degrees > 9000.0) {
      degrees = 9000.0;
    } else if (degrees < 30.0) {
      degrees = 30.0;
    }
    modulation_factor = degrees * (65536.0 / 180.0);
    modulation_type = 1;
  }
  void parameter_a(uint8_t Osc_par_a) {
      osc_par_a = Osc_par_a;
  }
  void parameter_b(uint8_t Osc_par_b) {
      osc_par_b = Osc_par_b;
  }
  virtual void update(void);
  
 
  
private:
  audio_block_t *inputQueueArray[2];
  uint32_t phase_accumulator;
  uint32_t phase_increment;
  uint32_t modulation_factor;
  int32_t  magnitude;
  const int16_t *arbdata;
  uint32_t phasedata[AUDIO_BLOCK_SAMPLES];
  int16_t  sample; // for WAVEFORM_SAMPLE_HOLD
  int16_t  tone_offset;
  uint8_t  tone_type;
  uint8_t  modulation_type;
  int16_t  syncFlag;
  uint32_t data_qs_phase[4];
  uint32_t data_qs_phase_2[4];
  uint8_t Osc_data_cr_decimate;
  uint16_t Osc_data_cr_state;
  uint16_t OscData_sec_phase;
  BandLimitedWaveform band_limit_waveform;
  uint8_t osc_par_a;
  uint8_t osc_par_b;
  uint8_t Osc_vw_update;
  uint32_t state_vow_formant_phase[3];
  uint32_t state_vow_formant_increment[3];
  uint32_t state_vow_formant_amplitude[3];
  boolean strike_;
  uint16_t state_vow_consonant_frames;
  uint32_t state_saw_phase[6];
  uint32_t state_saw_lp;
  uint32_t state_saw_bp;
  uint16_t Osc_vw1_formant_increment[3];
  uint16_t Osc_vw1_formant_amplitude[3];
  uint16_t Osc_vw1_formant_phase[3];
};


synth_waveform.cpp
Code:
/* Audio Library for Teensy 3.X
* Copyright (c) 2018, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
ElectroTechnique 2020
Added WAVEFORM_SILENT, syncFlag
*/


#include <Arduino.h>
#include "synth_waveform.h"
#include "arm_math.h"
#include "utility/dspinst.h"
#include "Entropy.h"




// uncomment for more accurate but more computationally expensive frequency modulation
//#define IMPROVE_EXPONENTIAL_ACCURACY
#define BASE_AMPLITUDE 0x6000  // 0x7fff won't work due to Gibb's phenomenon, so use 3/4 of full range.






void AudioSynthWaveformTS::update(void)
{
    audio_block_t *block;
    int16_t *bp, *end;
    int32_t val1, val2;
    int16_t val3;
    int16_t magnitude15;
    uint32_t i, ph, index, index2, scale;
    const uint32_t inc = phase_increment;
    uint32_t phaseX;
    
    if(syncFlag == 1) {
        phase_accumulator = 0;
        phaseX = 0;
        syncFlag = 0;
        LFO1randomFlag = false;
        LFO2randomFlag = false;
    }
    
    ph = phase_accumulator + phase_offset;
    phaseX = phase_accumulator;
    
    if (magnitude == 0) {
        phase_accumulator += inc * AUDIO_BLOCK_SAMPLES;
        return;
    }
    block = allocate();
    if (!block) {
        phase_accumulator += inc * AUDIO_BLOCK_SAMPLES;
        return;
    }
    bp = block->data;


    switch(tone_type) {
        
        // PWM SINE
        case PWM_WAVEFORM_SINE:
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            index = ph >> 24;
            val1 = AudioWaveformSine[index];
            val2 = AudioWaveformSine[index+1];
            scale = (ph >> 8) & 0xFFFF;
            val2 *= scale;
            val1 *= 0x10000 - scale;
            *bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
            ph += inc;
        }
        break;
        
        case PWM_WAVEFORM_TRIANGLE:
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            uint32_t phtop = ph >> 30;
            if (phtop == 1 || phtop == 2) {
                *bp++ = ((0xFFFF - (ph >> 15)) * magnitude) >> 16;
                } else {
                *bp++ = (((int32_t)ph >> 15) * magnitude) >> 16;
            }
            ph += inc;
        }
        break;
        
        // PWM SAWTOOTH (LFO offset 0.0f)
        case PWM_WAVEFORM_SAWTOOTH:        // normal sawtooth and inv sawtooth
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            *bp++ = signed_multiply_32x16t(magnitude, ph >> 1);
            ph += inc;
        }
        break;


        
        // PWM SQUARE (LFO offset 1.0)
        case PWM_WAVEFORM_SQUARE:
        magnitude15 = signed_saturate_rshift(magnitude, 16, 1);
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            //if (ph & 0x80000000) {
            if (ph & 0x80000000) {
                *bp++ = -magnitude15;
                } else {
                *bp++ = magnitude15;
            }
            ph += inc;
        }
        break;
        
        case WAVEFORM_ARBITRARY1:
        if (!arbdata) {
            release(block);
            phase_accumulator += inc * AUDIO_BLOCK_SAMPLES;
            return;
        }
        // len = 256
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            index = ph >> 24;
            index2 = index + 1;
            if (LFO1mode == 0) {
                if (index2 >= 256) index2 = 0;  // loop
            }
            else {
                if (index2 >= 256) index2 = 255; // one shot
            }
            val1 = *(arbdata + index);
            val2 = *(arbdata + index2);
            scale = (ph >> 8) & 0xFFFF;
            val2 *= scale;
            val1 *= 0x10000 - scale;
            *bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
            uint32_t ph_old = ph;
            uint32_t phaseX_old = phaseX;
            if (LFO1phase == 0) {    // Shape normal
                ph += inc;
                phaseX += inc;
            }
            else {
                ph -= inc;
                phaseX -= inc;        // Shape inverse
            }
            
            if (lfo1oneShoot == true && phaseX < inc) {
                ph = ph_old;
                phaseX = phaseX_old;
            }
        }
        break;


        case WAVEFORM_SAMPLE_HOLD1:
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            uint32_t newph = ph + inc;
            if (lfo1oneShoot == true && LFO1randomFlag == false) {    // one shot
                sample = random(magnitude) - (magnitude >> 1);
                sample2 = sample;
                LFO1randomFlag = true;
            }
            else if (newph < ph && lfo1oneShoot == false) {
                sample = random(magnitude) - (magnitude >> 1);
            }
            else if (lfo1oneShoot == true && LFO1randomFlag == true) {
                sample = sample2;
            }
            
            *bp++ = sample;
            ph = newph;
        }
        break;
    
    phase_accumulator = ph - phase_offset;


    if (tone_offset) {
        bp = block->data;
        end = bp + AUDIO_BLOCK_SAMPLES;
        do {
            val1 = *bp;
            *bp++ = signed_saturate_rshift(val1 + tone_offset, 16, 0);
        } while (bp < end);
    }
    transmit(block, 0);
    release(block);
}


//--------------------------------------------------------------------------------


void AudioSynthWaveformModulatedTS::update(void)
{
    audio_block_t *block, *moddata, *shapedata;
    int16_t *bp, *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);


    if(syncFlag==1){
        phase_accumulator = 0;
        syncFlag = 0;
    }
    
    // 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;
        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(5368709  12, 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;
        }
        release(moddata);
        } else if (moddata) {
        // Phase Modulation
        bp = moddata->data;
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            // more than +/- 180 deg shift by 32 bit overflow of "n"
            uint32_t n = (uint16_t)(*bp++) * modulation_factor;
            phasedata[i] = ph + n;
            ph += inc;
        }
        release(moddata);
        } else {
        // No Modulation Input
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            phasedata[i] = ph;
            ph += inc;
        }
    }
    phase_accumulator = ph;


    //Amplitude is always 1 on TSynth when oscillator is sounding
    //magnitude must be set to zero, otherwise digital noise comes through
    if(tone_type == WAVEFORM_SILENT){
        magnitude  = 0;
        }else{
        magnitude = 65536.0;
    }
    // If the amplitude is zero, no output, but phase still increments properly
    if (magnitude == 0) {
        if (shapedata) release(shapedata);
        return;
    }
    block = allocate();
    if (!block) {
        if (shapedata) release(shapedata);
        return;
    }
    bp = block->data;


    // Now generate the output samples using the pre-computed phase angles
    switch(tone_type) {
        case WAVEFORM_SINE:
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            ph = phasedata[i];
            index = ph >> 24;
            val1 = AudioWaveformSine[index];
            val2 = AudioWaveformSine[index+1];
            scale = (ph >> 8) & 0xFFFF;
            val2 *= scale;
            val1 *= 0x10000 - scale;
            *bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
        }
        break;


        case WAVEFORM_ARBITRARY:
        if (!arbdata) {
            release(block);
            if (shapedata) release(shapedata);
            return;
        }
        // len = 256
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            ph = phasedata[i];
            index = ph >> 24;
            index2 = index + 1;
            if (index2 >= 256) index2 = 0;
            val1 = *(arbdata + index);
            val2 = *(arbdata + index2);
            scale = (ph >> 8) & 0xFFFF;
            val2 *= scale;
            val1 *= 0x10000 - scale;
            *bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
        }
        break;


        case WAVEFORM_PULSE:
        if (shapedata) {
            magnitude15 = signed_saturate_rshift(magnitude, 16, 1);
            for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
                uint32_t width = ((shapedata->data[i] + 0x8000) & 0xFFFF) << 16;
                if (phasedata[i] < width) {
                    *bp++ = magnitude15;
                    } else {
                    *bp++ = -magnitude15;
                }
            }
            break;
        } // else fall through to orginary square without shape modulation


        case WAVEFORM_SQUARE:
        magnitude15 = signed_saturate_rshift(magnitude, 16, 1);
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            if (phasedata[i] & 0x80000000) {
                *bp++ = -magnitude15;
                } else {
                *bp++ = magnitude15;
            }
        }
        break;


        case WAVEFORM_BANDLIMIT_PULSE:
        if (shapedata)
        {
            for (i=0; i < AUDIO_BLOCK_SAMPLES; i++)
            {
                uint32_t width = ((shapedata->data[i] + 0x8000) & 0xFFFF) << 16;
                int32_t val = band_limit_waveform.generate_pulse (phasedata[i], width, i) ;
                *bp++ = (int16_t) ((val * magnitude) >> 16) ;
            }
            break;
        } // else fall through to orginary square without shape modulation


        case WAVEFORM_BANDLIMIT_SQUARE:
        for (i = 0 ; i < AUDIO_BLOCK_SAMPLES ; i++)
        {
            int32_t val = band_limit_waveform.generate_square (phasedata[i], i) ;
            *bp++ = (int16_t) ((val * magnitude) >> 16);
        }
        break;
        
        // Vowel ------------------------------------------------
        case WAVEFORM_Vowel
        int16_t parameter_[2];
        uint16_t balance;
        uint8_t vowel_index;
        uint16_t formant_shift;
        parameter_[0] = osc_par_a << 8; // convert parameter into int16 value
        parameter_[1] = osc_par_b << 8; // convert parameter into int16 value
        vowel_index = parameter_[0] >> 12;
        balance = parameter_[0] & 0x0fff;
        formant_shift = (400 + (parameter_[1] >> 5));
        
        if (strike_) {
            strike_ = false;
            state_vow_consonant_frames = 160;
            for (size_t i = 0; i < 3; i++) {
                state_vow_formant_increment[i] = static_cast<uint32_t>(consonant_data[index].formant_frequency[i]) *
                0x1000 * formant_shift;
                state_vow_formant_amplitude[i] = consonant_data[index].formant_amplitude[i];
            }
        }
        if (state_vow_consonant_frames) {
            --state_vow_consonant_frames;
            } else {
            for (size_t i = 0; i < 3; ++i) {
                state_vow_formant_increment[i] = (vowels_data[vowel_index].formant_frequency[i] *
                (0x1000 - balance) + vowels_data[vowel_index + 1].formant_frequency[i] * balance) * formant_shift;
                state_vow_formant_amplitude[i] = (vowels_data[vowel_index].formant_amplitude[i] *
                (0x1000 - balance) + vowels_data[vowel_index + 1].formant_amplitude[i] * balance) >> 12;
            }
        }        
        uint32_t phase_;
                
        for (uint8_t i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
            phase_ = phasedata[i];
            size_t phaselet;
            int16_t sample = 0;
            state_vow_formant_phase[0] += state_vow_formant_increment[0];
            phaselet = (state_vow_formant_phase[0] >> 24) & 0xf0;
            sample += wav_formant_sine[phaselet | state_vow_formant_amplitude[0]];
            state_vow_formant_phase[1] += state_vow_formant_increment[1];
            phaselet = (state_vow_formant_phase[1] >> 24) & 0xf0;
            sample += wav_formant_sine[phaselet | state_vow_formant_amplitude[1]];
            state_vow_formant_phase[2] += state_vow_formant_increment[2];
            phaselet = (state_vow_formant_phase[2] >> 24) & 0xf0;
            sample += wav_formant_square[phaselet | state_vow_formant_amplitude[2]];
            sample *= 255 - (phase_ >> 24);
            if (phase_ < phase_increment_) {
                state_vow_formant_phase[0] = 0;
                state_vow_formant_phase[1] = 0;
                state_vow_formant_phase[2] = 0;
                sample = 0;
            }    
            // amplify and soft clip Sound
            int32_t x = (sample * 1.35f);
            int32_t Threshold = 32768;
            float sample_x = (1.0f / Threshold) * x; // convert input into float
            if (sample_x > 1.0f) {
                sample_x = 1.0f;
            }
            if (sample_x < - 1.0f) {
                sample_x = -1.0f;
            }
            sample_x = 1.49f * sample_x - 0.5f * sample_x * sample_x * sample_x;
            x = sample_x * Threshold; // convert float into int
            *bp++ = x;
        }
        break;


        case WAVEFORM_SAWTOOTH_REVERSE:
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            *bp++ = signed_multiply_32x16t(0xFFFFFFFFu - magnitude, phasedata[i]);
        }
        break;


        case WAVEFORM_BANDLIMIT_SAWTOOTH:
        case WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE:
        for (i = 0 ; i < AUDIO_BLOCK_SAMPLES ; i++)
        {
            int16_t val = band_limit_waveform.generate_sawtooth (phasedata[i], i) ;
            val = (int16_t) ((val * magnitude) >> 16) ;
            *bp++ = tone_type == WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE ? (int16_t) -val : (int16_t) +val ;
        }
        break;


        case WAVEFORM_TRIANGLE_VARIABLE:
        if (shapedata) {
            for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
                uint32_t width = (shapedata->data[i] + 0x8000) & 0xFFFF;
                uint32_t rise = 0xFFFFFFFF / width;
                uint32_t fall = 0xFFFFFFFF / (0xFFFF - width);
                uint32_t halfwidth = width << 15;
                uint32_t n;
                ph = phasedata[i];
                if (ph < halfwidth) {
                    n = (ph >> 16) * rise;
                    *bp++ = ((n >> 16) * magnitude) >> 16;
                    } else if (ph < 0xFFFFFFFF - halfwidth) {
                    n = 0x7FFFFFFF - (((ph - halfwidth) >> 16) * fall);
                    *bp++ = (((int32_t)n >> 16) * magnitude) >> 16;
                    } else {
                    n = ((ph + halfwidth) >> 16) * rise + 0x80000000;
                    *bp++ = (((int32_t)n >> 16) * magnitude) >> 16;
                }
                ph += inc;
            }
            break;
        } // else fall through to orginary triangle without shape modulation


        case WAVEFORM_TRIANGLE:
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            ph = phasedata[i];
            uint32_t phtop = ph >> 30;
            if (phtop == 1 || phtop == 2) {
                *bp++ = ((0xFFFF - (ph >> 15)) * magnitude) >> 16;
                } else {
                *bp++ = (((int32_t)ph >> 15) * magnitude) >> 16;
            }
        }
        break;
        
        case WAVEFORM_SAMPLE_HOLD:
        for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
            ph = phasedata[i];
            if (ph < priorphase) { // does not work for phase modulation
                sample = random(magnitude) - (magnitude >> 1);
            }
            priorphase = ph;
            *bp++ = sample;
        }
        break;
    }


    if (tone_offset) {
        bp = block->data;
        end = bp + AUDIO_BLOCK_SAMPLES;
        do {
            val1 = *bp;
            *bp++ = signed_saturate_rshift(val1 + tone_offset, 16, 0);
        } while (bp < end);
    }
    if (shapedata) release(shapedata);
    transmit(block, 0);
    release(block);
}




// BandLimitedWaveform




#define SUPPORT_SHIFT 4
#define SUPPORT (1 << SUPPORT_SHIFT)
#define PTRMASK ((2 << SUPPORT_SHIFT) - 1)


#define SCALE 16
#define SCALE_MASK (SCALE-1)
#define N (SCALE * SUPPORT * 2)


#define GUARD_BITS 8
#define GUARD      (1 << GUARD_BITS)
#define HALF_GUARD (1 << (GUARD_BITS-1))




#define DEG180 0x80000000u


#define PHASE_SCALE (0x100000000L / (2 * BASE_AMPLITUDE))




extern "C"
{
    extern const int16_t bandlimit_step_table [258] ;
}


int32_t BandLimitedWaveform::lookup (int offset)
{
    int off = offset >> GUARD_BITS ;
    int frac = offset & (GUARD-1) ;


    int32_t a, b ;
    if (off < N/2)   // handle odd symmetry by reflecting table
    {
        a = bandlimit_step_table [off+1] ;
        b = bandlimit_step_table [off+2] ;
    }
    else
    {
        a = - bandlimit_step_table [N-off] ;
        b = - bandlimit_step_table [N-off-1] ;
    }
    return  BASE_AMPLITUDE + ((frac * b + (GUARD - frac) * a + HALF_GUARD) >> GUARD_BITS) ; // interpolated
}


// create a new step, apply its past waveform into the cyclic sample buffer
// and add a step_state object into active list so it can be added for the future samples
void BandLimitedWaveform::insert_step (int offset, bool rising, int i)
{
    while (offset <= (N/2-SCALE)<<GUARD_BITS)
    {
        if (offset >= 0)
        cyclic [i & 15] += rising ? lookup (offset) : -lookup (offset) ;
        offset += SCALE<<GUARD_BITS ;
        i ++ ;
    }


    states[newptr].offset = offset ;
    states[newptr].positive = rising ;
    newptr = (newptr+1) & PTRMASK ;
}


// generate value for current sample from one active step, checking for the
// dc_offset adjustment at the end of the table.
int32_t BandLimitedWaveform::process_step (int i)
{
    int off = states[i].offset ;
    bool positive = states[i].positive ;


    int32_t entry = lookup (off) ;
    off += SCALE<<GUARD_BITS ;
    states[i].offset = off ;  // update offset in table for next sample
    if (off >= N<<GUARD_BITS)             // at end of step table we alter dc_offset to extend the step into future
    dc_offset += positive ? 2*BASE_AMPLITUDE : -2*BASE_AMPLITUDE ;


    return positive ? entry : -entry ;
}


// process all active steps for current sample, basically generating the waveform portion
// due only to steps
// square waves use this directly.
int32_t BandLimitedWaveform::process_active_steps (uint32_t new_phase)
{
    int32_t sample = dc_offset ;
    
    int step_count = (newptr - delptr) & PTRMASK ;
    if (step_count > 0)        // for any steps in-flight we sum in table entry and update its state
    {
        int i = newptr ;
        do
        {
            i = (i-1) & PTRMASK ;
            sample += process_step (i) ;
        } while (i != delptr) ;
        if (states[delptr].offset >= N<<GUARD_BITS)  // remove any finished entries from the buffer.
        {
            delptr = (delptr+1) & PTRMASK ;
            // can be upto two steps per sample now for pulses
            if (newptr != delptr && states[delptr].offset >= N<<GUARD_BITS)
            delptr = (delptr+1) & PTRMASK ;
        }
    }
    return sample ;
}


// for sawtooth need to add in the slope and compensate for all the steps being one way
int32_t BandLimitedWaveform::process_active_steps_saw (uint32_t new_phase)
{
    int32_t sample = process_active_steps (new_phase) ;


    sample += (int16_t) ((((uint64_t)phase_word * (2*BASE_AMPLITUDE)) >> 32) - BASE_AMPLITUDE) ;  // generate the sloped part of the wave


    if (new_phase < DEG180 && phase_word >= DEG180) // detect wrap around, correct dc offset
    dc_offset += 2*BASE_AMPLITUDE ;


    return sample ;
}


// for pulse need to adjust the baseline according to the pulse width to cancel the DC component.
int32_t BandLimitedWaveform::process_active_steps_pulse (uint32_t new_phase, uint32_t pulse_width)
{
    int32_t sample = process_active_steps (new_phase) ;


    return sample + BASE_AMPLITUDE/2 - pulse_width / (0x80000000u / BASE_AMPLITUDE) ; // correct DC offset for duty cycle
}


// Check for new steps using the phase update for the current sample for a square wave
void BandLimitedWaveform::new_step_check_square (uint32_t new_phase, int i)
{
    if (new_phase >= DEG180 && phase_word < DEG180) // detect falling step
    {
        int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (sampled_width - phase_word) / (new_phase - phase_word)) ;
        if (offset == SCALE<<GUARD_BITS)
        offset -- ;
        if (pulse_state) // guard against two falling steps in a row (if pulse width changing for instance)
        {
            insert_step (- offset, false, i) ;
            pulse_state = false ;
        }
    }
    else if (new_phase < DEG180 && phase_word >= DEG180) // detect wrap around, rising step
    {
        int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (- phase_word) / (new_phase - phase_word)) ;
        if (offset == SCALE<<GUARD_BITS)
        offset -- ;
        if (!pulse_state) // guard against two rising steps in a row (if pulse width changing for instance)
        {
            insert_step (- offset, true, i) ;
            pulse_state = true ;
        }
    }
}


// Checking for new steps for pulse waveform has to deal with changing frequency and pulse width and
// not letting a pulse glitch out of existence as these change across a single period of the waveform
// now we detect the rising edge just like for a square wave and use that to sample the pulse width
// parameter, which then has to be checked against the instantaneous frequency every sample.
void BandLimitedWaveform::new_step_check_pulse (uint32_t new_phase, uint32_t pulse_width, int i)
{
    if (pulse_state && phase_word < sampled_width && (new_phase >= sampled_width || new_phase < phase_word))  // falling edge
    {
        int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (sampled_width - phase_word) / (new_phase - phase_word)) ;
        if (offset == SCALE<<GUARD_BITS)
        offset -- ;
        insert_step (- offset, false, i) ;
        pulse_state = false ;
    }
    if ((!pulse_state) && phase_word >= DEG180 && new_phase < DEG180) // detect wrap around, rising step
    {
        // sample the pulse width value so its not changing under our feet later in cycle due to modulation
        sampled_width = pulse_width ;


        int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (- phase_word) / (new_phase - phase_word)) ;
        if (offset == SCALE<<GUARD_BITS)
        offset -- ;
        insert_step (- offset, true, i) ;
        pulse_state = true ;
        
        if (pulse_state && new_phase >= sampled_width) // detect falling step directly after a rising edge
        //if (new_phase - sampled_width < DEG180) // detect falling step directly after a rising edge
        {
            int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (sampled_width - phase_word) / (new_phase - phase_word)) ;
            if (offset == SCALE<<GUARD_BITS)
            offset -- ;
            insert_step (- offset, false, i) ;
            pulse_state = false ;
        }
    }
}


// new steps for sawtooth are at 180 degree point, always falling.
void BandLimitedWaveform::new_step_check_saw (uint32_t new_phase, int i)
{
    if (new_phase >= DEG180 && phase_word < DEG180) // detect falling step
    {
        int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (DEG180 - phase_word) / (new_phase - phase_word)) ;
        if (offset == SCALE<<GUARD_BITS)
        offset -- ;
        insert_step (- offset, false, i) ;
    }
}


// the generation function pushd new sample into cyclic buffer, having taken out the oldest entry
// to return.  The output is thus 16 samples behind, which allows the non-casual step function to
// work in real time.
int16_t BandLimitedWaveform::generate_sawtooth (uint32_t new_phase, int i)
{
    new_step_check_saw (new_phase, i) ;
    int32_t val = process_active_steps_saw (new_phase) ;
    int16_t sample = (int16_t) cyclic [i&15] ;
    cyclic [i&15] = val ;
    phase_word = new_phase ;
    return sample ;
}


int16_t BandLimitedWaveform::generate_square (uint32_t new_phase, int i)
{
    new_step_check_square (new_phase, i) ;
    int32_t val = process_active_steps (new_phase) ;
    int16_t sample = (int16_t) cyclic [i&15] ;
    cyclic [i&15] = val ;
    phase_word = new_phase ;
    return sample ;
}


int16_t BandLimitedWaveform::generate_pulse (uint32_t new_phase, uint32_t pulse_width, int i)
{
    new_step_check_pulse (new_phase, pulse_width, i) ;
    int32_t val = process_active_steps_pulse (new_phase, pulse_width) ;
    int32_t sample = cyclic [i&15] ;
    cyclic [i&15] = val ;
    phase_word = new_phase ;
    return (int16_t) ((sample >> 1) - (sample >> 5)) ; // scale down to avoid overflow on narrow pulses, where the DC shift is big
}


void BandLimitedWaveform::init_sawtooth (uint32_t freq_word)
{
    phase_word = 0 ;
    newptr = 0 ;
    delptr = 0 ;
    for (int i = 0 ; i < 2*SUPPORT ; i++)
    phase_word -= freq_word ;
    dc_offset = phase_word < DEG180 ? BASE_AMPLITUDE : -BASE_AMPLITUDE ;
    for (int i = 0 ; i < 2*SUPPORT ; i++)
    {
        uint32_t new_phase = phase_word + freq_word ;
        new_step_check_saw (new_phase, i) ;
        cyclic [i & 15] = (int16_t) process_active_steps_saw (new_phase) ;
        phase_word = new_phase ;
    }
}




void BandLimitedWaveform::init_square (uint32_t freq_word)
{
    init_pulse (freq_word, DEG180) ;
}


void BandLimitedWaveform::init_pulse (uint32_t freq_word, uint32_t pulse_width)
{
    phase_word = 0 ;
    sampled_width = pulse_width ;
    newptr = 0 ;
    delptr = 0 ;
    for (int i = 0 ; i < 2*SUPPORT ; i++)
    phase_word -= freq_word ;


    if (phase_word < pulse_width)
    {
        dc_offset = BASE_AMPLITUDE ;
        pulse_state = true ;
    }
    else
    {
        dc_offset = -BASE_AMPLITUDE ;
        pulse_state = false ;
    }
    
    for (int i = 0 ; i < 2*SUPPORT ; i++)
    {
        uint32_t new_phase = phase_word + freq_word ;
        new_step_check_pulse (new_phase, pulse_width, i) ;
        cyclic [i & 15] = (int16_t) process_active_steps_pulse (new_phase, pulse_width) ;
        phase_word = new_phase ;
    }
}


BandLimitedWaveform::BandLimitedWaveform()
{
    newptr = 0 ;
    delptr = 0 ;
    dc_offset = BASE_AMPLITUDE ;
    phase_word = 0 ;
}
 
Last edited:
Result into vowel code..

Code:
if (phase_ < phaseOld_) {
                state_vow_formant_phase[0] = 0;
                state_vow_formant_phase[1] = 0;
                state_vow_formant_phase[2] = 0;
                sample = 0;
            }
            phaseOld_ = phase_;


synth_waveform.h
Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 *
 * Development of this audio library was funded by PJRC.COM, LLC by sales of
 * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
 * open source software by purchasing Teensy or other PJRC products.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 
 ElectroTechnique 2020
 Added WAVEFORM_SILENT, sync()                           
 */


#ifndef synth_waveform_h_
#define synth_waveform_h_


#include <Arduino.h>
#include "AudioStream.h"
#include "arm_math.h"




// waveforms.c
extern "C" {
extern const int16_t AudioWaveformSine[257];
}


//extern int8_t Lfo3Modoutput;




// Oscillator
#define WAVEFORM_SINE							0
#define WAVEFORM_TRIANGLE						1
#define WAVEFORM_SAWTOOTH						3
#define WAVEFORM_SQUARE							2
#define WAVEFORM_PULSE							4
#define WAVEFORM_SAWTOOTH_REVERSE				5
#define WAVEFORM_SAMPLE_HOLD					6
#define WAVEFORM_TRIANGLE_VARIABLE				7
#define WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE		8
#define WAVEFORM_BANDLIMIT_SAWTOOTH				9
#define WAVEFORM_BANDLIMIT_SQUARE				10
#define WAVEFORM_BANDLIMIT_PULSE				11
#define WAVEFORM_ARBITRARY						12
#define WAVEFORM_SAWTOOTH2						30
#define WAVEFORM_SILENT							19


// LFO and PWM waveforms
#define LFO_WAVEFORM_ARBITRARY					20
#define LFO_WAVEFORM_SAMPLE_HOLD				21
#define PWM_WAVEFORM_SINE						26
#define PWM_WAVEFORM_TRIANGLE					27
#define PWM_WAVEFORM_SAWTOOTH					28
#define PWM_WAVEFORM_SQUARE						29


/*
const uint8_t wav_res_sine[] {
	 2,      2,      2,      3,      2,      3,      3,      4,
	 5,      4,      7,      5,      9,      7,     10,     11,
	 11,     13,     13,     17,     16,     18,     21,     21,
	 23,     25,     27,     28,     32,     31,     36,     36,
	 39,     41,     43,     46,     48,     51,     53,     55,
	 57,     62,     63,     65,     70,     70,     75,     76,
	 81,     82,     85,     89,     92,     94,     97,    100,
	 104,    107,    109,    112,    116,    119,    122,    124,
	 129,    130,    135,    137,    140,    144,    147,    148,
	 154,    155,    158,    163,    163,    169,    169,    174,
	 177,    178,    182,    185,    187,    191,    192,    195,
	 199,    200,    203,    205,    209,    210,    212,    216,
	 216,    220,    221,    223,    226,    227,    230,    230,
	 233,    235,    235,    239,    238,    241,    242,    243,
	 245,    245,    246,    249,    247,    251,    249,    252,
	 251,    252,    253,    253,    253,    254,    254,    254,
	 253,    255,    254,    253,    253,    254,    252,    253,
	 251,    251,    250,    250,    248,    248,    246,    246,
	 245,    242,    243,    240,    239,    238,    236,    234,
	 233,    231,    230,    226,    226,    224,    221,    219,
	 217,    215,    212,    211,    208,    206,    202,    201,
	 198,    196,    192,    190,    188,    184,    182,    179,
	 177,    173,    170,    168,    164,    162,    158,    156,
	 153,    149,    147,    143,    141,    136,    135,    131,
	 128,    125,    122,    118,    116,    113,    109,    106,
	 104,    101,     96,     95,     92,     88,     85,     83,
	 80,     77,     74,     71,     69,     66,     63,     61,
	 57,     56,     53,     50,     49,     45,     43,     42,
	 38,     37,     35,     32,     31,     29,     26,     26,
	 22,     22,     20,     19,     16,     16,     13,     14,
	 11,     10,     10,      8,      8,      6,      6,      5,
	 4,      5,      2,      4,      2,      2,      2,      3,
	 1
};
*/


const int8_t wav_res_formant_square[256] = {
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16
};


const int8_t wav_res_formant_sine[256] = {
	0,      0,      0,      0,      0,      0,      0,      0,
	0,      0,      0,      0,      0,      0,      0,      0,
	0,      2,      2,      3,      3,      4,      5,      6,
	7,      8,     10,     12,     14,     17,     20,     24,
	0,      3,      4,      5,      6,      7,      9,     10,
	12,     15,     18,     21,     26,     31,     37,     45,
	0,      4,      5,      6,      8,      9,     11,     13,
	16,     19,     23,     28,     34,     40,     49,     58,
	0,      5,      6,      7,      8,     10,     12,     15,
	17,     21,     25,     30,     36,     44,     53,     63,
	0,      4,      5,      6,      8,      9,     11,     13,
	16,     19,     23,     28,     34,     40,     49,     58,
	0,      3,      4,      5,      6,      7,      9,     10,
	12,     15,     18,     21,     26,     31,     37,     45,
	0,      2,      2,      3,      3,      4,      5,      6,
	7,      8,     10,     12,     14,     17,     20,     24,
	0,      0,      0,      0,      0,      0,      0,      0,
	0,      0,      0,      0,      0,      0,      0,      0,
	0,     -2,     -2,     -3,     -3,     -4,     -5,     -6,
	-7,     -8,    -10,    -12,    -14,    -17,    -20,    -24,
	0,     -3,     -4,     -5,     -6,     -7,     -9,    -10,
	-12,    -15,    -18,    -21,    -26,    -31,    -37,    -45,
	0,     -4,     -5,     -6,     -8,     -9,    -11,    -13,
	-16,    -19,    -23,    -28,    -34,    -40,    -49,    -58,
	0,     -5,     -6,     -7,     -8,    -10,    -12,    -15,
	-17,    -21,    -25,    -30,    -36,    -44,    -53,    -63,
	0,     -4,     -5,     -6,     -8,     -9,    -11,    -13,
	-16,    -19,    -23,    -28,    -34,    -40,    -49,    -58,
	0,     -3,     -4,     -5,     -6,     -7,     -9,    -10,
	-12,    -15,    -18,    -21,    -26,    -31,    -37,    -45,
	0,     -2,     -2,     -3,     -3,     -4,     -5,     -6,
	-7,     -8,    -10,    -12,    -14,    -17,    -20,    -24
};


const uint16_t wav_res_sine16[] = {
		0,    10,	 39,    88,   157,   245,   352,   479,
	  625,   790,   974,  1178,  1400,  1641,  1901,  2179,
	 2475,  2790,  3122,  3472,  3840,  4225,  4627,  5045,
	 5481,  5932,  6400,  6884,  7382,  7897,  8426,  8969,
     9527, 10098, 10684, 11282, 11893, 12517, 13153, 13800,
    14459, 15129, 15809, 16500, 17200, 17909, 18628, 19355,
    20090, 20832, 21582, 22338, 23100, 23869, 24642, 25421,
    26203, 26990, 27780, 28574, 29369, 30167, 30966, 31767,
    32568, 33369, 34170, 34969, 35768, 36565, 37359, 38151,
    38940, 39724, 40505, 41281, 42052, 42818, 43577, 44330,
    45076, 45815, 46546, 47268, 47982, 48687, 49383, 50068,
    50743, 51408, 52061, 52703, 53332, 53950, 54555, 55147,
    55725, 56290, 56840, 57377, 57898, 58405, 58896, 59372,
    59831, 60275, 60702, 61112, 61506, 61882, 62241, 62582,
    62906, 63211, 63499, 63767, 64018, 64249, 64462, 64656,
    64831, 64987, 65123, 65240, 65338, 65416, 65475, 65514,
    65534, 65534, 65514, 65475, 65416, 65338, 65240, 65123,
    64987, 64831, 64656, 64462, 64249, 64018, 63767, 63499,
    63211, 62906, 62582, 62241, 61882, 61506, 61112, 60702,
    60275, 59831, 59372, 58896, 58405, 57898, 57377, 56840,
    56290, 55725, 55147, 54555, 53950, 53332, 52703, 52061,
    51408, 50743, 50068, 49383, 48687, 47982, 47268, 46546,
    45815, 45076, 44330, 43577, 42818, 42052, 41281, 40505,
    39724, 38940, 38151, 37359, 36565, 35768, 34969, 34170,
    33369, 32568, 31767, 30966, 30167, 29369, 28574, 27780,
    26990, 26203, 25421, 24642, 23869, 23100, 22338, 21582,
    20832, 20090, 19355, 18628, 17909, 17200, 16500, 15809,
    15129, 14459, 13800, 13153, 12517, 11893, 11282, 10684,
    10098,  9527,  8969,  8426,  7897,  7382,  6884,  6400,
     5932,  5481,  5045,  4627,  4225,  3840,  3472,  3122,
     2790,  2475,  2179,  1901,  1641,  1400,  1178,   974,
      790,   625,   479,   352,   245,   157,    88,    39,
	   10
};


const uint8_t wav_res_vowel_data[] = {
	27,     40,     89,     15,     13,      1,      0,     18,
	51,     62,     13,     12,      6,      0,     15,     69,
	93,     14,     12,      7,      0,     10,     84,    110,
	13,     10,      8,      0,     23,     44,     87,     15,
	12,      1,      0,     13,     29,     80,     13,      8,
	0,      0,      6,     46,     81,     12,      3,      0,
	0,      9,     51,     95,     15,      3,      0,      3,
	6,     73,     99,      7,      3,     14,      9,
};


const int16_t wav_formant_square[] = {
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,      1,      1,      2,      2,      3,      3,      4,
	4,      5,      6,      8,      9,     11,     13,     16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
	0,     -1,     -1,     -2,     -2,     -3,     -3,     -4,
	-4,     -5,     -6,     -8,     -9,    -11,    -13,    -16,
};


const int16_t wav_formant_sine[] = {
	0,      0,      0,      0,      0,      0,      0,      0,
	0,      0,      0,      0,      0,      0,      0,      0,
	0,      2,      2,      3,      3,      4,      5,      6,
	7,      8,     10,     12,     14,     17,     20,     24,
	0,      3,      4,      5,      6,      7,      9,     10,
	12,     15,     18,     21,     26,     31,     37,     45,
	0,      4,      5,      6,      8,      9,     11,     13,
	16,     19,     23,     28,     34,     40,     49,     58,
	0,      5,      6,      7,      8,     10,     12,     15,
	17,     21,     25,     30,     36,     44,     53,     63,
	0,      4,      5,      6,      8,      9,     11,     13,
	16,     19,     23,     28,     34,     40,     49,     58,
	0,      3,      4,      5,      6,      7,      9,     10,
	12,     15,     18,     21,     26,     31,     37,     45,
	0,      2,      2,      3,      3,      4,      5,      6,
	7,      8,     10,     12,     14,     17,     20,     24,
	0,      0,      0,      0,      0,      0,      0,      0,
	0,      0,      0,      0,      0,      0,      0,      0,
	0,     -2,     -2,     -3,     -3,     -4,     -5,     -6,
	-7,     -8,    -10,    -12,    -14,    -17,    -20,    -24,
	0,     -3,     -4,     -5,     -6,     -7,     -9,    -10,
	-12,    -15,    -18,    -21,    -26,    -31,    -37,    -45,
	0,     -4,     -5,     -6,     -8,     -9,    -11,    -13,
	-16,    -19,    -23,    -28,    -34,    -40,    -49,    -58,
	0,     -5,     -6,     -7,     -8,    -10,    -12,    -15,
	-17,    -21,    -25,    -30,    -36,    -44,    -53,    -63,
	0,     -4,     -5,     -6,     -8,     -9,    -11,    -13,
	-16,    -19,    -23,    -28,    -34,    -40,    -49,    -58,
	0,     -3,     -4,     -5,     -6,     -7,     -9,    -10,
	-12,    -15,    -18,    -21,    -26,    -31,    -37,    -45,
	0,     -2,     -2,     -3,     -3,     -4,     -5,     -6,
	-7,     -8,    -10,    -12,    -14,    -17,    -20,    -24,
};


struct PhonemeDefinition {
	uint8_t formant_frequency[3];
	uint8_t formant_amplitude[3];
};


static const PhonemeDefinition vowels_data[9] = {
	{ { 27,  40,  89 }, { 15,  13,  1 } },
	{ { 18,  51,  62 }, { 13,  12,  6 } },
	{ { 15,  69,  93 }, { 14,  12,  7 } },
	{ { 10,  84, 110 }, { 13,  10,  8 } },
	{ { 23,  44,  87 }, { 15,  12,  1 } },
	{ { 13,  29,  80 }, { 13,   8,  0 } },
	{ {  6,  46,  81 }, { 12,   3,  0 } },
	{ {  9,  51,  95 }, { 15,   3,  0 } },
	{ {  6,  73,  99 }, {  7,   3,  14 } }
};


static const PhonemeDefinition consonant_data[8] = {
	{ { 6, 54, 121 }, { 9,  9,  0 } },
	{ { 18, 50, 51 }, { 12,  10,  5 } },
	{ { 11, 24, 70 }, { 13,  8,  0 } },
	{ { 15, 69, 74 }, { 14,  12,  7 } },
	{ { 16, 37, 111 }, { 14,  8,  1 } },
	{ { 18, 51, 62 }, { 14,  12,  6 } },
	{ { 6, 26, 81 }, { 5,  5,  5 } },
	{ { 6, 73, 99 }, { 7,  10,  14 } },
};


 static const size_t kNumFormants = 5;
 


typedef struct step_state
{
  int offset ;
  bool positive ;
} step_state ;




class BandLimitedWaveform
{
public:
  BandLimitedWaveform (void) ;
  int16_t generate_sawtooth (uint32_t new_phase, int i) ;
  int16_t generate_square (uint32_t new_phase, int i) ;
  int16_t generate_pulse (uint32_t new_phase, uint32_t pulse_width, int i) ;
  void init_sawtooth (uint32_t freq_word) ;
  void init_square (uint32_t freq_word) ;
  void init_pulse (uint32_t freq_word, uint32_t pulse_width) ;
  


private:
  int32_t lookup (int offset) ;
  void insert_step (int offset, bool rising, int i) ;
  int32_t process_step (int i) ;
  int32_t process_active_steps (uint32_t new_phase) ;
  int32_t process_active_steps_saw (uint32_t new_phase) ;
  int32_t process_active_steps_pulse (uint32_t new_phase, uint32_t pulse_width) ;
  void new_step_check_square (uint32_t new_phase, int i) ;
  void new_step_check_pulse (uint32_t new_phase, uint32_t pulse_width, int i) ;
  void new_step_check_saw (uint32_t new_phase, int i) ;
  
  
  uint32_t phase_word ;
  int32_t dc_offset ;
  step_state states [32] ; // circular buffer of active steps
  int newptr ;         // buffer pointers into states, AND'd with PTRMASK to keep in buffer range.
  int delptr ;
  int32_t  cyclic[16] ;    // circular buffer of output samples
  bool pulse_state ;
  uint32_t sampled_width ; // pulse width is sampled once per waveform
};




class AudioSynthWaveformTS : public AudioStream
{
public:
  AudioSynthWaveformTS(void) : AudioStream(0,NULL),
    phase_accumulator(0), phase_increment(0), phase_offset(0),
    magnitude(0), pulse_width(0x40000000),
    arbdata(NULL), sample(0), tone_type(WAVEFORM_SINE),
    tone_offset(0),syncFlag(0) {
  }


  void frequency(float freq) {
    if (freq < 0.0) {
      freq = 0.0;
    } else if (freq > AUDIO_SAMPLE_RATE_EXACT / 2) {
      freq = AUDIO_SAMPLE_RATE_EXACT / 2;
    }
    phase_increment = freq * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT);
    if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000;
  }
  void phase(float angle) {
    if (angle < 0.0) {
      angle = 0.0;
    } else if (angle > 360.0) {
      angle = angle - 360.0;
      if (angle >= 360.0) return;
    }
    phase_offset = angle * (4294967296.0 / 360.0);
  }
  void sync() {
    syncFlag = 1;
  }       
  void amplitude(float n) { // 0 to 1.0
    if (n < 0) {
      n = 0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    magnitude = n * 65536.0;
  }
  void offset(float n) {
    if (n < -1.0) {
      n = -1.0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    tone_offset = n * 32767.0;
  }
  void pulseWidth(float n) {  // 0.0 to 1.0
    if (n < 0) {
      n = 0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    pulse_width = n * 4294967296.0;
  }
  void begin(short t_type) {
    phase_offset = 0;
    tone_type = t_type;
    if (t_type == WAVEFORM_BANDLIMIT_SQUARE)
      band_limit_waveform.init_square (phase_increment) ;
    else if (t_type == WAVEFORM_BANDLIMIT_PULSE)
      band_limit_waveform.init_pulse (phase_increment, pulse_width) ;
    else if (t_type == WAVEFORM_BANDLIMIT_SAWTOOTH || t_type == WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE)
      band_limit_waveform.init_sawtooth (phase_increment) ;
  }
  void begin(float t_amp, float t_freq, short t_type) {
    amplitude(t_amp);
    frequency(t_freq);
    phase_offset = 0;
    begin (t_type);
  }
  void arbitraryWaveform(const int16_t *data, float maxFreq) {
    arbdata = data;
  }
  void LFO_mode (uint8_t lfo_Mode) {
	  lfo_mode = lfo_Mode;
  }
  void LFO_phase (uint8_t lfo_Phase) {	// LFO parameter: SYN
	  lfo_phase = lfo_Phase;
  }
  void LFO_oneShoot (boolean lfo_OneShoot) {
	  lfo_oneShoot = lfo_OneShoot;
  }
  
  virtual void update(void);


private:
  uint32_t phase_accumulator;
  uint32_t phase_increment;
  uint32_t phase_offset;
  int32_t  magnitude;
  uint32_t pulse_width;
  const int16_t *arbdata;
  int16_t  sample; // for WAVEFORM_SAMPLE_HOLD
  int16_t  sample2; // for WAVEFORM_SAMPLE_HOLD and oneShot
  short  tone_type;
  int16_t tone_offset;
  int16_t syncFlag;
  BandLimitedWaveform band_limit_waveform;
  uint8_t lfo_mode;
  uint8_t lfo_phase;
  boolean lfo_oneShoot;
  uint8_t lfo1ph;
  boolean lfo_randomFlag;  
};




class AudioSynthWaveformModulatedTS : public AudioStream
{
public:
  AudioSynthWaveformModulatedTS(void) : AudioStream(2, inputQueueArray),
    phase_accumulator(0), phase_increment(0), modulation_factor(32768),
    magnitude(0), arbdata(NULL), sample(0), tone_offset(0),
    tone_type(WAVEFORM_SINE), modulation_type(0), syncFlag(0), osc_par_a(0),
	osc_par_b(0)
	 {
  }


  void frequency(float freq) {
   //freq = freq / 2.0f;	// only for tone_type Vowel
    if (freq < 0.0) {
      freq = 0.0;
    } else if (freq > AUDIO_SAMPLE_RATE_EXACT / 2) {
      freq = AUDIO_SAMPLE_RATE_EXACT / 2;
    }
    phase_increment = freq * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT);
    if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000;
  }
  void amplitude(float n) { // 0 to 1.0
    if (n < 0) {
      n = 0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    magnitude = n * 65536.0;
  }
   void sync() {
    syncFlag = 1;
  }              
  void offset(float n) {
    if (n < -1.0) {
      n = -1.0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    tone_offset = n * 32767.0;
  }
  void begin(short t_type) {
    tone_type = t_type;
    if (t_type == WAVEFORM_BANDLIMIT_SQUARE)
      band_limit_waveform.init_square (phase_increment) ;
    else if (t_type == WAVEFORM_BANDLIMIT_PULSE)
      band_limit_waveform.init_pulse (phase_increment, 0x80000000u) ;
    else if (t_type == WAVEFORM_BANDLIMIT_SAWTOOTH || t_type == WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE)
      band_limit_waveform.init_sawtooth (phase_increment) ;
  }
  void begin(float t_amp, float t_freq, short t_type) {
    amplitude(t_amp);
    frequency(t_freq);
    begin (t_type) ;
  }
  void arbitraryWaveform(const int16_t *data, float maxFreq) {
    arbdata = data;
  }
  void frequencyModulation(float octaves) {
    if (octaves > 12.0) {
      octaves = 12.0;
    } else if (octaves < 0.1) {
      octaves = 0.1;
    }
    modulation_factor = octaves * 4096.0;
    modulation_type = 0;
  }
  void phaseModulation(float degrees) {
    if (degrees > 9000.0) {
      degrees = 9000.0;
    } else if (degrees < 30.0) {
      degrees = 30.0;
    }
    modulation_factor = degrees * (65536.0 / 180.0);
    modulation_type = 1;
  }
  void parameter_a(uint8_t Osc_par_a) {
	  osc_par_a = Osc_par_a;
  }
  void parameter_b(uint8_t Osc_par_b) {
	  osc_par_b = Osc_par_b;
  }
  
  virtual void update(void);
  
private:
  audio_block_t *inputQueueArray[2];
  uint32_t phase_accumulator;
  uint32_t phase_increment;
  uint32_t modulation_factor;
  int32_t  magnitude;
  const int16_t *arbdata;
  uint32_t phasedata[AUDIO_BLOCK_SAMPLES];
  int16_t  sample; // for WAVEFORM_SAMPLE_HOLD
  int16_t  tone_offset;
  uint8_t  tone_type;
  uint8_t  modulation_type;
  int16_t  syncFlag;
  uint32_t data_qs_phase[4];
  uint32_t data_qs_phase_2[4];
  uint8_t Osc_data_cr_decimate;
  uint16_t Osc_data_cr_state;
  uint16_t OscData_sec_phase;
  BandLimitedWaveform band_limit_waveform;
  uint8_t osc_par_a;
  uint8_t osc_par_b;
  uint8_t Osc_vw_update;
  uint32_t state_vow_formant_phase[3];
  uint32_t state_vow_formant_increment[3];
  uint32_t state_vow_formant_amplitude[3];
  boolean strike_;
  uint16_t state_vow_consonant_frames;
  uint32_t state_saw_phase[6];
  uint32_t state_saw_lp;
  uint32_t state_saw_bp;
  uint16_t Osc_vw1_formant_increment[3];
  uint16_t Osc_vw1_formant_amplitude[3];
  uint16_t Osc_vw1_formant_phase[3];
  uint32_t phaseOld_= 0;
};


#endif


synth_waveform.cpp
Code:
/* Audio Library for Teensy 3.X
* Copyright (c) 2018, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
ElectroTechnique 2020
Added WAVEFORM_SILENT, syncFlag
R.Degen 2023
Added Supersaw and Mutable instruments Braids digital oscillator
*/


#include <Arduino.h>
#include "synth_waveform.h"
#include "arm_math.h"
#include "utility/dspinst.h"
#include "Entropy.h"




// uncomment for more accurate but more computationally expensive frequency modulation
//#define IMPROVE_EXPONENTIAL_ACCURACY
#define BASE_AMPLITUDE 0x6000  // 0x7fff won't work due to Gibb's phenomenon, so use 3/4 of full range.






void AudioSynthWaveformTS::update(void)
{
	audio_block_t *block;
	int16_t *bp, *end;
	int32_t val1, val2;
	int16_t val3;
	int16_t magnitude15;
	uint32_t i, ph, index, index2, scale;
	const uint32_t inc = phase_increment;
	uint32_t phaseX;
	
	if(syncFlag == 1) {
		phase_accumulator = 0;
		phaseX = 0;
		syncFlag = 0;
		lfo_randomFlag = false;
	}
	
	ph = phase_accumulator + phase_offset;
	phaseX = phase_accumulator;
	
	if (magnitude == 0) {
		phase_accumulator += inc * AUDIO_BLOCK_SAMPLES;
		return;
	}
	block = allocate();
	if (!block) {
		phase_accumulator += inc * AUDIO_BLOCK_SAMPLES;
		return;
	}
	bp = block->data;


	switch(tone_type) {
		
		// PWM SINE -------------------------------------------------------
		case PWM_WAVEFORM_SINE:
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			index = ph >> 24;
			val1 = AudioWaveformSine[index];
			val2 = AudioWaveformSine[index+1];
			scale = (ph >> 8) & 0xFFFF;
			val2 *= scale;
			val1 *= 0x10000 - scale;
			*bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
			ph += inc;
		}
		break;
		
		// PWM Triangle ---------------------------------------------------
		case PWM_WAVEFORM_TRIANGLE:
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			uint32_t phtop = ph >> 30;
			if (phtop == 1 || phtop == 2) {
				*bp++ = ((0xFFFF - (ph >> 15)) * magnitude) >> 16;
				} else {
				*bp++ = (((int32_t)ph >> 15) * magnitude) >> 16;
			}
			ph += inc;
		}
		break;
		
		// PWM SAWTOOTH ---------------------------------------------------
		case PWM_WAVEFORM_SAWTOOTH:		// normal sawtooth and inv sawtooth
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			*bp++ = signed_multiply_32x16t(magnitude, ph >> 1);
			ph += inc;
		}
		break;


		
		// PWM SQUARE -----------------------------------------------------
		case PWM_WAVEFORM_SQUARE:
		magnitude15 = signed_saturate_rshift(magnitude, 16, 1);
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			//if (ph & 0x80000000) {
			if (ph & 0x80000000) {
				*bp++ = -magnitude15;
				} else {
				*bp++ = magnitude15;
			}
			ph += inc;
		}
		break;
		
		// LFO Arbitrary waveform -----------------------------------------
		case LFO_WAVEFORM_ARBITRARY:
		uint8_t lfo_mode_;
		uint8_t lfo_phase_;
		boolean lfo_oneShoot_;
		lfo_mode_ = lfo_mode;
		lfo_phase_ = lfo_phase;			// LFO parameter: SYN
		lfo_oneShoot_ = lfo_oneShoot;
		
		if (!arbdata) {
			release(block);
			phase_accumulator += inc * AUDIO_BLOCK_SAMPLES;
			return;
		}
		// len = 256
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			index = ph >> 24;
			index2 = index + 1;
			if (lfo_mode_ == 0) {
				if (index2 >= 256) index2 = 0;		// loop
			}
			else {
				if (index2 >= 256) index2 = 255;	// OneShoot
			}
			val1 = *(arbdata + index);
			val2 = *(arbdata + index2);
			scale = (ph >> 8) & 0xFFFF;
			val2 *= scale;
			val1 *= 0x10000 - scale;
			*bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
			uint32_t ph_old = ph;
			uint32_t phaseX_old = phaseX;
			if (lfo_phase_ <= 1) {					// Shape normal
				ph += inc;
				phaseX += inc;
			}
			else {
				ph -= inc;
				phaseX -= inc;						// Shape inverse
			}
			
			if (lfo_oneShoot_ == true && phaseX < inc) {
				ph = ph_old;
				phaseX = phaseX_old;
			}
		}
		break;


		// LFO S&H waveform -----------------------------------------------
		case LFO_WAVEFORM_SAMPLE_HOLD:
		boolean randomFlag_;
		boolean oneShoot_;
		randomFlag_ = lfo_randomFlag;
		oneShoot_ = lfo_oneShoot;
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			uint32_t newph = ph + inc;
			if (oneShoot_ == true && randomFlag_ == false) {	// one shot
				sample = random(magnitude) - (magnitude >> 1);
				sample2 = sample;
				randomFlag_ = true;
			}
			else if (newph < ph && oneShoot_ == false) {
				sample = random(magnitude) - (magnitude >> 1);
			}
			else if (oneShoot_ == true && randomFlag_ == true) {
				sample = sample2;
			}
			
			*bp++ = sample;
			ph = newph;
		}
		lfo_randomFlag = randomFlag_;
		break;
	}
	
	phase_accumulator = ph - phase_offset;


	if (tone_offset) {
		bp = block->data;
		end = bp + AUDIO_BLOCK_SAMPLES;
		do {
			val1 = *bp;
			*bp++ = signed_saturate_rshift(val1 + tone_offset, 16, 0);
		} while (bp < end);
	}
	transmit(block, 0);
	release(block);
}


//--------------------------------------------------------------------------------


void AudioSynthWaveformModulatedTS::update(void)
{
	audio_block_t *block, *moddata, *shapedata;
	int16_t *bp, *end;
	int32_t val1, val2;
	int16_t magnitude15;
	uint32_t i, ph, index, index2, scale, priorphase;
	const uint32_t inc = phase_increment;
	uint32_t phase_spread;
	uint32_t saw_phase_increment;
	uint32_t increments[4];
	uint32_t ph_1;
	uint32_t ph_2;
	uint32_t ph_3;
	uint32_t ph_4;
	uint32_t ph_5;
	int32_t Ssaw_value;
	moddata = receiveReadOnly(0);
	shapedata = receiveReadOnly(1);


	if(syncFlag==1){
		phase_accumulator = 0;
		syncFlag = 0;
	}
	
	// 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;
		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;
		}
		
		release(moddata);
		} else if (moddata) {
		// Phase Modulation
		bp = moddata->data;
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			// more than +/- 180 deg shift by 32 bit overflow of "n"
			uint32_t n = (uint16_t)(*bp++) * modulation_factor;
			phasedata[i] = ph + n;
			ph += inc;
		}
		release(moddata);
		} else {
		// No Modulation Input
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			phasedata[i] = ph;
			ph += inc;
		}
	}
	phase_accumulator = ph;


	//Amplitude is always 1 on TSynth when oscillator is sounding
	//magnitude must be set to zero, otherwise digital noise comes through
	if(tone_type == WAVEFORM_SILENT){
		magnitude  = 0;
		}else{
		magnitude = 65536.0;
	}
	// If the amplitude is zero, no output, but phase still increments properly
	if (magnitude == 0) {
		if (shapedata) release(shapedata);
		return;
	}
	block = allocate();
	if (!block) {
		if (shapedata) release(shapedata);
		return;
	}
	bp = block->data;


	// Now generate the output samples using the pre-computed phase angles
	
	switch(tone_type) {
		
		// WaveformModulated Sine -----------------------------------------
		case WAVEFORM_SINE:
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			ph = phasedata[i];
			index = ph >> 24;
			val1 = AudioWaveformSine[index];
			val2 = AudioWaveformSine[index+1];
			scale = (ph >> 8) & 0xFFFF;
			val2 *= scale;
			val1 *= 0x10000 - scale;
			*bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
		}
		break;


		// WaveformModulated Arbitrary ------------------------------------
		case WAVEFORM_ARBITRARY:
		if (!arbdata) {
			release(block);
			if (shapedata) release(shapedata);
			return;
		}
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			ph = phasedata[i];
			index = ph >> 24;
			index2 = index + 1;
			if (index2 >= 256) index2 = 0;
			val1 = *(arbdata + index);
			val2 = *(arbdata + index2);
			scale = (ph >> 8) & 0xFFFF;
			val2 *= scale;
			val1 *= 0x10000 - scale;
			*bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
		}
		break;
		
		// WaveformModulated Pulse ----------------------------------------
		case WAVEFORM_PULSE:
		if (shapedata) {
			magnitude15 = signed_saturate_rshift(magnitude, 16, 1);
			for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
				uint32_t width = ((shapedata->data[i] + 0x8000) & 0xFFFF) << 16;
				if (phasedata[i] < width) {
					*bp++ = magnitude15;
					} else {
					*bp++ = -magnitude15;
				}
			}
			break;
		} // else fall through to orginary square without shape modulation


		// WaveformModulated Square ---------------------------------------
		case WAVEFORM_SQUARE:
		magnitude15 = signed_saturate_rshift(magnitude, 16, 1);
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			if (phasedata[i] & 0x80000000) {
				*bp++ = -magnitude15;
				} else {
				*bp++ = magnitude15;
			}
		}
		break;


		// WaveformModulated Bandlimit Puls -------------------------------
		case WAVEFORM_BANDLIMIT_PULSE:
		if (shapedata)
		{
			for (i=0; i < AUDIO_BLOCK_SAMPLES; i++)
			{
				uint32_t width = ((shapedata->data[i] + 0x8000) & 0xFFFF) << 16;
				int32_t val = band_limit_waveform.generate_pulse (phasedata[i], width, i) ;
				*bp++ = (int16_t) ((val * magnitude) >> 16) ;
			}
			break;
		} // else fall through to orginary square without shape modulation


		// WaveformModulated Bandlimit Square -----------------------------
		case WAVEFORM_BANDLIMIT_SQUARE:
		for (i = 0 ; i < AUDIO_BLOCK_SAMPLES ; i++)
		{
			int32_t val = band_limit_waveform.generate_square (phasedata[i], i) ;
			*bp++ = (int16_t) ((val * magnitude) >> 16);
		}
		break;
		
		// WaveformModulated Supersaw -------------------------------------
		case WAVEFORM_SAWTOOTH:
				
		// Braids vowel ---------------------------------------------------
		int16_t parameter_[2];
		uint16_t balance;
		uint8_t vowel_index;
		uint16_t formant_shift;
		parameter_[0] = osc_par_a << 8; // convert parameter into int16 value
		parameter_[1] = osc_par_b << 8; // convert parameter into int16 value
		vowel_index = parameter_[0] >> 12;
		balance = parameter_[0] & 0x0fff;
		formant_shift = (440 + (parameter_[1] >> 5));
		
		if (strike_) {
			strike_ = false;
			state_vow_consonant_frames = 160;
			for (size_t i = 0; i < 3; i++) {
				state_vow_formant_increment[i] = static_cast<uint32_t>(consonant_data[index].formant_frequency[i]) *
				0x1000 * formant_shift;
				state_vow_formant_amplitude[i] = consonant_data[index].formant_amplitude[i];
			}
		}
		if (state_vow_consonant_frames) {
			--state_vow_consonant_frames;
			} else {
			for (size_t i = 0; i < 3; ++i) {
				state_vow_formant_increment[i] = (vowels_data[vowel_index].formant_frequency[i] *
				(0x1000 - balance) + vowels_data[vowel_index + 1].formant_frequency[i] * balance) * formant_shift;
				state_vow_formant_amplitude[i] = (vowels_data[vowel_index].formant_amplitude[i] *
				(0x1000 - balance) + vowels_data[vowel_index + 1].formant_amplitude[i] * balance) >> 12;
			}
		}
				
		uint32_t phase_;
				
		for (uint8_t i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
			phase_ = phasedata[i];
			size_t phaselet;
			int16_t sample = 0;
			state_vow_formant_phase[0] += state_vow_formant_increment[0];
			phaselet = (state_vow_formant_phase[0] >> 24) & 0xf0;
			sample += wav_formant_sine[phaselet | state_vow_formant_amplitude[0]];
			state_vow_formant_phase[1] += state_vow_formant_increment[1];
			phaselet = (state_vow_formant_phase[1] >> 24) & 0xf0;
			sample += wav_formant_sine[phaselet | state_vow_formant_amplitude[1]];
			state_vow_formant_phase[2] += state_vow_formant_increment[2];
			phaselet = (state_vow_formant_phase[2] >> 24) & 0xf0;
			sample += wav_formant_square[phaselet | state_vow_formant_amplitude[2]];
			sample *= 255 - (phase_ >> 24);
			if (phase_ < phaseOld_) {
				state_vow_formant_phase[0] = 0;
				state_vow_formant_phase[1] = 0;
				state_vow_formant_phase[2] = 0;
				sample = 0;
			}
			phaseOld_ = phase_;	
					
			float sample_f;
			sample_f = sample * 1.35f / 32768;
			// a = 1.5*a - 0.5 * a³  //should be converted to fixed point
			sample_f = 1.50f * sample_f - 0.5f * sample_f * sample_f * sample_f; 
			sample = sample_f * 32768;
			*bp++ = (int16_t) ((sample * magnitude) >> 16);			
		}
		break;
	
		
		
		/*
		// Braids VowelFof ------------------------------------------------
		int16_t parameter_[2];
		
		const uint8_t* sync,
		int16_t* buffer,
		size_t size) {


			// The original implementation used FOF but we live in the future and it's
			// less computationally expensive to render a proper bank of 5 SVF.


			int16_t amplitudes[kNumFormants];
			int32_t svf_lp[kNumFormants];
			int32_t svf_bp[kNumFormants];
			int16_t svf_f[kNumFormants];
			
			for (size_t i = 0; i < kNumFormants; ++i) {
				int32_t frequency = InterpolateFormantParameter(
				formant_f_data,
				parameter_[1],
				parameter_[0],
				i) + (12 << 7);
				svf_f[i] = Interpolate824(lut_svf_cutoff, frequency << 17);
				amplitudes[i] = InterpolateFormantParameter(
				formant_a_data,
				parameter_[1],
				parameter_[0],
				i);
				if (init_) {
					svf_lp[i] = 0;
					svf_bp[i] = 0;
					} else {
					svf_lp[i] = state_.fof.svf_lp[i];
					svf_bp[i] = state_.fof.svf_bp[i];
				}
			}
			
			if (init_) {
				init_ = false;
			}
			
			uint32_t phase = phase_;
			int32_t previous_sample = state_.fof.previous_sample;
			int32_t next_saw_sample = state_.fof.next_saw_sample;
			uint32_t increment = phase_increment_ << 1;
			while (size) {
				int32_t this_saw_sample = next_saw_sample;
				next_saw_sample = 0;
				phase += increment;
				if (phase < increment) {
					uint32_t t = phase / (increment >> 16);
					if (t > 65535) {
						t = 65535;
					}
					this_saw_sample -= static_cast<int32_t>(t * t >> 18);
					t = 65535 - t;
					next_saw_sample -= -static_cast<int32_t>(t * t >> 18);
				}
				next_saw_sample += phase >> 17;
				int32_t in = this_saw_sample;
				int32_t out = 0;
				for (int32_t i = 0; i < 5; ++i) {
					int32_t notch = in - (svf_bp[i] >> 6);
					svf_lp[i] += svf_f[i] * svf_bp[i] >> 15;
					CLIP(svf_lp[i])
					int32_t hp = notch - svf_lp[i];
					svf_bp[i] += svf_f[i] * hp >> 15;
					CLIP(svf_bp[i])
					out += svf_bp[i] * amplitudes[0] >> 17;
				}
				CLIP(out);
				*buffer++ = (out + previous_sample) >> 1;
				*buffer++ = out;
				previous_sample = out;
				size -= 2;
			}
			phase_ = phase;
			state_.fof.next_saw_sample = next_saw_sample;
			state_.fof.previous_sample = previous_sample;
			for (size_t i = 0; i < kNumFormants; ++i) {
				state_.fof.svf_lp[i] = svf_lp[i];
				state_.fof.svf_bp[i] = svf_bp[i];
			}
		}
		break;
		*/
		
		
		
		// WaveformModulated Sawtooth reverse -----------------------------
		case WAVEFORM_SAWTOOTH_REVERSE:
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			*bp++ = signed_multiply_32x16t(0xFFFFFFFFu - magnitude, phasedata[i]);
		}
		break;
		
		// WaveformModulated Bandlimit Sawtooth and Sawtooth reverse ------
		case WAVEFORM_BANDLIMIT_SAWTOOTH:
		case WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE:
		for (i = 0 ; i < AUDIO_BLOCK_SAMPLES ; i++)
		{
			int16_t val = band_limit_waveform.generate_sawtooth (phasedata[i], i) ;
			val = (int16_t) ((val * magnitude) >> 16) ;
			*bp++ = tone_type == WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE ? (int16_t) -val : (int16_t) +val ;
		}
		break;


		// WaveformModulated Variable Triangle (Triangle -> Saw) ----------
		case WAVEFORM_TRIANGLE_VARIABLE:
		if (shapedata) {
			for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
				uint32_t width = (shapedata->data[i] + 0x8000) & 0xFFFF;
				uint32_t rise = 0xFFFFFFFF / width;
				uint32_t fall = 0xFFFFFFFF / (0xFFFF - width);
				uint32_t halfwidth = width << 15;
				uint32_t n;
				ph = phasedata[i];
				if (ph < halfwidth) {
					n = (ph >> 16) * rise;
					*bp++ = ((n >> 16) * magnitude) >> 16;
					} else if (ph < 0xFFFFFFFF - halfwidth) {
					n = 0x7FFFFFFF - (((ph - halfwidth) >> 16) * fall);
					*bp++ = (((int32_t)n >> 16) * magnitude) >> 16;
					} else {
					n = ((ph + halfwidth) >> 16) * rise + 0x80000000;
					*bp++ = (((int32_t)n >> 16) * magnitude) >> 16;
				}
				ph += inc;
			}
			break;
		} // else fall through to orginary triangle without shape modulation


		// WaveformModulated Triangle -------------------------------------
		case WAVEFORM_TRIANGLE:
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			ph = phasedata[i];
			uint32_t phtop = ph >> 30;
			if (phtop == 1 || phtop == 2) {
				*bp++ = ((0xFFFF - (ph >> 15)) * magnitude) >> 16;
				} else {
				*bp++ = (((int32_t)ph >> 15) * magnitude) >> 16;
			}
		}
		break;
		
		// WaveformModulated Sample & Hold --------------------------------
		case WAVEFORM_SAMPLE_HOLD:
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			ph = phasedata[i];
			if (ph < priorphase) { // does not work for phase modulation
				sample = random(magnitude) - (magnitude >> 1);
			}
			priorphase = ph;
			*bp++ = sample;
		}
		break;
	}


	if (tone_offset) {
		bp = block->data;
		end = bp + AUDIO_BLOCK_SAMPLES;
		do {
			val1 = *bp;
			*bp++ = signed_saturate_rshift(val1 + tone_offset, 16, 0);
		} while (bp < end);
	}
	if (shapedata) release(shapedata);
	transmit(block, 0);
	release(block);
}




// BandLimitedWaveform
#define SUPPORT_SHIFT 4
#define SUPPORT (1 << SUPPORT_SHIFT)
#define PTRMASK ((2 << SUPPORT_SHIFT) - 1)


#define SCALE 16
#define SCALE_MASK (SCALE-1)
#define N (SCALE * SUPPORT * 2)


#define GUARD_BITS 8
#define GUARD      (1 << GUARD_BITS)
#define HALF_GUARD (1 << (GUARD_BITS-1))




#define DEG180 0x80000000u


#define PHASE_SCALE (0x100000000L / (2 * BASE_AMPLITUDE))




extern "C"
{
	extern const int16_t bandlimit_step_table [258] ;
}


int32_t BandLimitedWaveform::lookup (int offset)
{
	int off = offset >> GUARD_BITS ;
	int frac = offset & (GUARD-1) ;


	int32_t a, b ;
	if (off < N/2)   // handle odd symmetry by reflecting table
	{
		a = bandlimit_step_table [off+1] ;
		b = bandlimit_step_table [off+2] ;
	}
	else
	{
		a = - bandlimit_step_table [N-off] ;
		b = - bandlimit_step_table [N-off-1] ;
	}
	return  BASE_AMPLITUDE + ((frac * b + (GUARD - frac) * a + HALF_GUARD) >> GUARD_BITS) ; // interpolated
}


// create a new step, apply its past waveform into the cyclic sample buffer
// and add a step_state object into active list so it can be added for the future samples
void BandLimitedWaveform::insert_step (int offset, bool rising, int i)
{
	while (offset <= (N/2-SCALE)<<GUARD_BITS)
	{
		if (offset >= 0)
		cyclic [i & 15] += rising ? lookup (offset) : -lookup (offset) ;
		offset += SCALE<<GUARD_BITS ;
		i ++ ;
	}


	states[newptr].offset = offset ;
	states[newptr].positive = rising ;
	newptr = (newptr+1) & PTRMASK ;
}


// generate value for current sample from one active step, checking for the
// dc_offset adjustment at the end of the table.
int32_t BandLimitedWaveform::process_step (int i)
{
	int off = states[i].offset ;
	bool positive = states[i].positive ;


	int32_t entry = lookup (off) ;
	off += SCALE<<GUARD_BITS ;
	states[i].offset = off ;  // update offset in table for next sample
	if (off >= N<<GUARD_BITS)             // at end of step table we alter dc_offset to extend the step into future
	dc_offset += positive ? 2*BASE_AMPLITUDE : -2*BASE_AMPLITUDE ;


	return positive ? entry : -entry ;
}


// process all active steps for current sample, basically generating the waveform portion
// due only to steps
// square waves use this directly.
int32_t BandLimitedWaveform::process_active_steps (uint32_t new_phase)
{
	int32_t sample = dc_offset ;
	
	int step_count = (newptr - delptr) & PTRMASK ;
	if (step_count > 0)        // for any steps in-flight we sum in table entry and update its state
	{
		int i = newptr ;
		do
		{
			i = (i-1) & PTRMASK ;
			sample += process_step (i) ;
		} while (i != delptr) ;
		if (states[delptr].offset >= N<<GUARD_BITS)  // remove any finished entries from the buffer.
		{
			delptr = (delptr+1) & PTRMASK ;
			// can be upto two steps per sample now for pulses
			if (newptr != delptr && states[delptr].offset >= N<<GUARD_BITS)
			delptr = (delptr+1) & PTRMASK ;
		}
	}
	return sample ;
}


// for sawtooth need to add in the slope and compensate for all the steps being one way
int32_t BandLimitedWaveform::process_active_steps_saw (uint32_t new_phase)
{
	int32_t sample = process_active_steps (new_phase) ;


	sample += (int16_t) ((((uint64_t)phase_word * (2*BASE_AMPLITUDE)) >> 32) - BASE_AMPLITUDE) ;  // generate the sloped part of the wave


	if (new_phase < DEG180 && phase_word >= DEG180) // detect wrap around, correct dc offset
	dc_offset += 2*BASE_AMPLITUDE ;


	return sample ;
}


// for pulse need to adjust the baseline according to the pulse width to cancel the DC component.
int32_t BandLimitedWaveform::process_active_steps_pulse (uint32_t new_phase, uint32_t pulse_width)
{
	int32_t sample = process_active_steps (new_phase) ;


	return sample + BASE_AMPLITUDE/2 - pulse_width / (0x80000000u / BASE_AMPLITUDE) ; // correct DC offset for duty cycle
}


// Check for new steps using the phase update for the current sample for a square wave
void BandLimitedWaveform::new_step_check_square (uint32_t new_phase, int i)
{
	if (new_phase >= DEG180 && phase_word < DEG180) // detect falling step
	{
		int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (sampled_width - phase_word) / (new_phase - phase_word)) ;
		if (offset == SCALE<<GUARD_BITS)
		offset -- ;
		if (pulse_state) // guard against two falling steps in a row (if pulse width changing for instance)
		{
			insert_step (- offset, false, i) ;
			pulse_state = false ;
		}
	}
	else if (new_phase < DEG180 && phase_word >= DEG180) // detect wrap around, rising step
	{
		int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (- phase_word) / (new_phase - phase_word)) ;
		if (offset == SCALE<<GUARD_BITS)
		offset -- ;
		if (!pulse_state) // guard against two rising steps in a row (if pulse width changing for instance)
		{
			insert_step (- offset, true, i) ;
			pulse_state = true ;
		}
	}
}


// Checking for new steps for pulse waveform has to deal with changing frequency and pulse width and
// not letting a pulse glitch out of existence as these change across a single period of the waveform
// now we detect the rising edge just like for a square wave and use that to sample the pulse width
// parameter, which then has to be checked against the instantaneous frequency every sample.
void BandLimitedWaveform::new_step_check_pulse (uint32_t new_phase, uint32_t pulse_width, int i)
{
	if (pulse_state && phase_word < sampled_width && (new_phase >= sampled_width || new_phase < phase_word))  // falling edge
	{
		int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (sampled_width - phase_word) / (new_phase - phase_word)) ;
		if (offset == SCALE<<GUARD_BITS)
		offset -- ;
		insert_step (- offset, false, i) ;
		pulse_state = false ;
	}
	if ((!pulse_state) && phase_word >= DEG180 && new_phase < DEG180) // detect wrap around, rising step
	{
		// sample the pulse width value so its not changing under our feet later in cycle due to modulation
		sampled_width = pulse_width ;


		int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (- phase_word) / (new_phase - phase_word)) ;
		if (offset == SCALE<<GUARD_BITS)
		offset -- ;
		insert_step (- offset, true, i) ;
		pulse_state = true ;
		
		if (pulse_state && new_phase >= sampled_width) // detect falling step directly after a rising edge
		//if (new_phase - sampled_width < DEG180) // detect falling step directly after a rising edge
		{
			int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (sampled_width - phase_word) / (new_phase - phase_word)) ;
			if (offset == SCALE<<GUARD_BITS)
			offset -- ;
			insert_step (- offset, false, i) ;
			pulse_state = false ;
		}
	}
}


// new steps for sawtooth are at 180 degree point, always falling.
void BandLimitedWaveform::new_step_check_saw (uint32_t new_phase, int i)
{
	if (new_phase >= DEG180 && phase_word < DEG180) // detect falling step
	{
		int32_t offset = (int32_t) ((uint64_t) (SCALE<<GUARD_BITS) * (DEG180 - phase_word) / (new_phase - phase_word)) ;
		if (offset == SCALE<<GUARD_BITS)
		offset -- ;
		insert_step (- offset, false, i) ;
	}
}


// the generation function pushd new sample into cyclic buffer, having taken out the oldest entry
// to return.  The output is thus 16 samples behind, which allows the non-casual step function to
// work in real time.
int16_t BandLimitedWaveform::generate_sawtooth (uint32_t new_phase, int i)
{
	new_step_check_saw (new_phase, i) ;
	int32_t val = process_active_steps_saw (new_phase) ;
	int16_t sample = (int16_t) cyclic [i&15] ;
	cyclic [i&15] = val ;
	phase_word = new_phase ;
	return sample ;
}


int16_t BandLimitedWaveform::generate_square (uint32_t new_phase, int i)
{
	new_step_check_square (new_phase, i) ;
	int32_t val = process_active_steps (new_phase) ;
	int16_t sample = (int16_t) cyclic [i&15] ;
	cyclic [i&15] = val ;
	phase_word = new_phase ;
	return sample ;
}


int16_t BandLimitedWaveform::generate_pulse (uint32_t new_phase, uint32_t pulse_width, int i)
{
	new_step_check_pulse (new_phase, pulse_width, i) ;
	int32_t val = process_active_steps_pulse (new_phase, pulse_width) ;
	int32_t sample = cyclic [i&15] ;
	cyclic [i&15] = val ;
	phase_word = new_phase ;
	return (int16_t) ((sample >> 1) - (sample >> 5)) ; // scale down to avoid overflow on narrow pulses, where the DC shift is big
}


void BandLimitedWaveform::init_sawtooth (uint32_t freq_word)
{
	phase_word = 0 ;
	newptr = 0 ;
	delptr = 0 ;
	for (int i = 0 ; i < 2*SUPPORT ; i++)
	phase_word -= freq_word ;
	dc_offset = phase_word < DEG180 ? BASE_AMPLITUDE : -BASE_AMPLITUDE ;
	for (int i = 0 ; i < 2*SUPPORT ; i++)
	{
		uint32_t new_phase = phase_word + freq_word ;
		new_step_check_saw (new_phase, i) ;
		cyclic [i & 15] = (int16_t) process_active_steps_saw (new_phase) ;
		phase_word = new_phase ;
	}
}




void BandLimitedWaveform::init_square (uint32_t freq_word)
{
	init_pulse (freq_word, DEG180) ;
}


void BandLimitedWaveform::init_pulse (uint32_t freq_word, uint32_t pulse_width)
{
	phase_word = 0 ;
	sampled_width = pulse_width ;
	newptr = 0 ;
	delptr = 0 ;
	for (int i = 0 ; i < 2*SUPPORT ; i++)
	phase_word -= freq_word ;


	if (phase_word < pulse_width)
	{
		dc_offset = BASE_AMPLITUDE ;
		pulse_state = true ;
	}
	else
	{
		dc_offset = -BASE_AMPLITUDE ;
		pulse_state = false ;
	}
	
	for (int i = 0 ; i < 2*SUPPORT ; i++)
	{
		uint32_t new_phase = phase_word + freq_word ;
		new_step_check_pulse (new_phase, pulse_width, i) ;
		cyclic [i & 15] = (int16_t) process_active_steps_pulse (new_phase, pulse_width) ;
		phase_word = new_phase ;
	}
}


BandLimitedWaveform::BandLimitedWaveform()
{
	newptr = 0 ;
	delptr = 0 ;
	dc_offset = BASE_AMPLITUDE ;
	phase_word = 0 ;
}


// Braids Vowel waveform
int16_t InterpolateFormantParameter(
const int16_t table[][kNumFormants][kNumFormants],
int16_t x,
int16_t y,
uint8_t formant) {
	uint16_t x_index = x >> 13;
	uint16_t x_mix = x << 3;
	uint16_t y_index = y >> 13;
	uint16_t y_mix = y << 3;
	int16_t a = table[x_index][y_index][formant];
	int16_t b = table[x_index + 1][y_index][formant];
	int16_t c = table[x_index][y_index + 1][formant];
	int16_t d = table[x_index + 1][y_index + 1][formant];
	a = a + ((b - a) * x_mix >> 16);
	c = c + ((d - c) * x_mix >> 16);
	return a + ((c - a) * y_mix >> 16);
}
 
The next step. I want to modulate the Parameter_A and Parameter_B in the vowel oscillator with a source (LFO or envelope).
 
I programmed AudioSynthWaveformModulated with two additional modulation inputs..
The modulation inputs can be connected to any modulation source (envelope, LFO and more..) in the AudioPatch.h

waveformMod into synth_waveform.cpp
Code:
void AudioSynthWaveformModulatedTS::update(void)
{
	audio_block_t *block, *moddata, *shapedata, *par_A, *par_B;
	//audio_block_t *block, *moddata, *shapedata;
	int16_t *bp, *end, *par_A_mod, *par_B_mod;
	int32_t val1, val2;
	int16_t magnitude15;
	uint32_t i, ph, index, index2, scale, priorphase;
	const uint32_t inc = phase_increment;
	uint32_t phase_spread;
	uint32_t saw_phase_increment;
	uint32_t increments[4];
	uint32_t ph_1;
	uint32_t ph_2;
	uint32_t ph_3;
	uint32_t ph_4;
	uint32_t ph_5;
	int32_t Ssaw_value;
	
	moddata = receiveReadOnly(0);
	shapedata = receiveReadOnly(1);
	par_A = receiveReadOnly(2);			// new parameter_A input
	par_B = receiveReadOnly(3);			// new parameter_B input


	if(syncFlag==1){
		phase_accumulator = 0;
		syncFlag = 0;
	}
	
	// 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;
		
		// read two new Modulation parameter from AudioConnection into AudioPatching.h
		par_A_mod = par_A->data;
		par_B_mod = par_B->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;
		}
		release(moddata);
		release(par_A);
		release(par_B);
		par_a_mod_ = *par_A_mod; // save modulation data
		par_b_mod_ = *par_B_mod; // save modulation data
		
		} else if (moddata) {
		// Phase Modulation
		bp = moddata->data;
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			// more than +/- 180 deg shift by 32 bit overflow of "n"
			uint32_t n = (uint16_t)(*bp++) * modulation_factor;
			phasedata[i] = ph + n;
			ph += inc;
		}
		release(moddata);
		} else {
		// No Modulation Input
		for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
			phasedata[i] = ph;
			ph += inc;
		}
	}
	phase_accumulator = ph;
	
	//Amplitude is always 1 on TSynth when oscillator is sounding
	//magnitude must be set to zero, otherwise digital noise comes through
	if(tone_type == WAVEFORM_SILENT){
		magnitude  = 0;
		}else{
		magnitude = 65536.0;
	}
	// If the amplitude is zero, no output, but phase still increments properly
	if (magnitude == 0) {
		if (shapedata) release(shapedata);
		return;
	}
	block = allocate();
	if (!block) {
		if (shapedata) release(shapedata);
		return;
	}
	bp = block->data;


	// Now generate the output samples using the pre-computed phase angles

Synth_waveform.h
Code:
class AudioSynthWaveformModulatedTS : public AudioStream
{
public:
  AudioSynthWaveformModulatedTS(void) : AudioStream(4, inputQueueArray),
    phase_accumulator(0), phase_increment(0), modulation_factor(32768),
    magnitude(0), arbdata(NULL), sample(0), tone_offset(0),
    tone_type(WAVEFORM_SINE), modulation_type(0), syncFlag(0), osc_par_a(0),
    osc_par_b(0), par_a_mod_(0), par_b_mod_(0)
     {
  }
  void frequency(float freq) {
   //freq = freq / 2.0f;    // only for tone_type Vowel
    if (freq < 0.0) {
      freq = 0.0;
    } else if (freq > AUDIO_SAMPLE_RATE_EXACT / 2) {
      freq = AUDIO_SAMPLE_RATE_EXACT / 2;
    }
    phase_increment = freq * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT);
    if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000;
  }
  void amplitude(float n) { // 0 to 1.0
    if (n < 0) {
      n = 0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    magnitude = n * 65536.0;
  }
   void sync() {
    syncFlag = 1;
  }              
  void offset(float n) {
    if (n < -1.0) {
      n = -1.0;
    } else if (n > 1.0) {
      n = 1.0;
    }
    tone_offset = n * 32767.0;
  }
  void begin(short t_type) {
    tone_type = t_type;
    if (t_type == WAVEFORM_BANDLIMIT_SQUARE)
      band_limit_waveform.init_square (phase_increment) ;
    else if (t_type == WAVEFORM_BANDLIMIT_PULSE)
      band_limit_waveform.init_pulse (phase_increment, 0x80000000u) ;
    else if (t_type == WAVEFORM_BANDLIMIT_SAWTOOTH || t_type == WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE)
      band_limit_waveform.init_sawtooth (phase_increment) ;
  }
  void begin(float t_amp, float t_freq, short t_type) {
    amplitude(t_amp);
    frequency(t_freq);
    begin (t_type) ;
  }
  void arbitraryWaveform(const int16_t *data, float maxFreq) {
    arbdata = data;
  }
  void frequencyModulation(float octaves) {
    if (octaves > 12.0) {
      octaves = 12.0;
    } else if (octaves < 0.1) {
      octaves = 0.1;
    }
    modulation_factor = octaves * 4096.0;
    modulation_type = 0;
  }
  void phaseModulation(float degrees) {
    if (degrees > 9000.0) {
      degrees = 9000.0;
    } else if (degrees < 30.0) {
      degrees = 30.0;
    }
    modulation_factor = degrees * (65536.0 / 180.0);
    modulation_type = 1;
  }
  void parameter_a(uint8_t Osc_par_a) {        // parameter_a im Osc Menu
      osc_par_a = Osc_par_a;
  }
  void parameter_b(uint8_t Osc_par_b) {        // parameter_b im Osc Menu
      osc_par_b = Osc_par_b;
  }
  
  virtual void update(void);
  
private:
  audio_block_t *inputQueueArray[4];
  uint32_t phase_accumulator;
  uint32_t phase_increment;
  uint32_t modulation_factor;
  int32_t  magnitude;
  const int16_t *arbdata;
  uint32_t phasedata[AUDIO_BLOCK_SAMPLES];
  int16_t  sample; // for WAVEFORM_SAMPLE_HOLD
  int16_t  tone_offset;
  uint8_t  tone_type;
  uint8_t  modulation_type;
  int16_t  syncFlag;
  uint32_t data_qs_phase[4];
  uint32_t data_qs_phase_2[4];
  uint8_t Osc_data_cr_decimate;
  uint16_t Osc_data_cr_state;
  uint16_t OscData_sec_phase;
  BandLimitedWaveform band_limit_waveform;
  uint8_t osc_par_a;
  uint8_t osc_par_b;
  uint8_t Osc_vw_update;
  uint32_t state_vow_formant_phase[3];
  uint32_t state_vow_formant_increment[3];
  uint32_t state_vow_formant_amplitude[3];
  boolean strike_;
  uint16_t state_vow_consonant_frames;
  uint32_t state_saw_phase[6];
  uint32_t state_saw_lp;
  uint32_t state_saw_bp;
  uint16_t Osc_vw1_formant_increment[3];
  uint16_t Osc_vw1_formant_amplitude[3];
  uint16_t Osc_vw1_formant_phase[3];
  uint32_t phaseOld_= 0;
  uint16_t par_a_mod_;    // vowel parameter_A
  uint16_t par_b_mod_;    // vowel parameter_B
};
 
Last edited:
Next Update coming soon.. and more Braids synthesis

22.10.23​V2.79​Added Braids and Shruthi Synthesis
Braids VOWL : low-fi Vowel synthesis. PRM_A : formant, PRM_B : formant_shift​
Shruthi ZSAW : Phase-distortion sawtooth with filter sweep​
Shruthi ZSYNC :​
Shruthi ZTRI :​
Shruthi ZRESO :​
Shruthi ZPULS :​
Shruthi Chrushed_Sine :​
Braids VOSM : Sawtooth with 2 formants. PRM_A : formant1 frequency, PRM_B : formant2 frequency​
Braids TOY : Low-fi Circuit-bent sounds. PRM_A : sample reduction, PRM_B : bit toggling​
Braids SuperSaw : Swarm of 7 sawtooths, PRM_A: Detune, PRM_B High-pass filter​
Braids ZLPF : Direct synthesis of LP/Peaking/BP/HP filtered waveform​
Braids ZPKF : PRM_A: Cutoff frequency, PRM_B: Waveshape​
Braids ZBPF :​
Braids ZHPF :

Video
Screenshot-2023-10-23-160635.png

https://youtu.be/rIK02G-8oj4

Code on github..
Code.png


Link: https://github.com/rolfdegen/Jeannie-Open-source-Synthesizer

Greetings. Rolf
 
Really amazing; and thanks for making this an open project. I think you should have a paypal or something like that because this project deserve donations for sure
 
More wavetable from Jeannie and MiniFreak


Jeannie polyphonic DIY Synthesizer play wavetables with MiniFreak play sequenzer patches
 
Wavetable oscilator in Teensy Audio engine

C:
// WaveformModulated BRAIDS_WTX4 -------------------------
    case WAVEFORM_BRAIDS_WTX4:
    {
        parameter_[0] = (osc_par_a << 5) + par_a_mod_;  // osc_par_a from pot 0-1023, par_a_mod modulation input
        parameter_[1] = (osc_par_b << 5) + par_b_mod_;
        // clip max value
        parameter_[0] = CLIP(parameter_[0]);
        parameter_[1] = CLIP(parameter_[1]);

        if (strike_)
        {
            for (size_t i = 0; i < 4; ++i)
            {
                state_saw.phase[i] = random();
            }
            strike_ = false;
        }

        // Do not use an array here to allow these to be kept in arbitrary registers.
        uint32_t phase_0, phase_1, phase_2, phase_3;
        uint32_t phase_increment1[3];
        uint32_t phase_increment_0;

        // Phase increment
        phase_increment_0 = ((phasedata[1] - phasedata[0]));

        //phase_increment_0 = phase_increment;
        phase_0 = state_saw_phase_[0];
        phase_1 = state_saw_phase_[1];
        phase_2 = state_saw_phase_[2];
        phase_3 = state_saw_phase_[3];

        uint16_t chord_integral = parameter_[1] >> 11;
        uint16_t chord_fractional = parameter_[1] << 5;
        if (chord_fractional < 30720)
        {
            chord_fractional = 0;
        }
        else if (chord_fractional >= 34816)
        {
            chord_fractional = 65535;
        }
        else
        {
            chord_fractional = (chord_fractional - 30720) * 16;
        }

 

        for (size_t i = 0; i < 3; ++i)
        {
            uint16_t detune_1 = chords[chord_integral][i];
            uint16_t detune_2 = chords[chord_integral + 1][i];
            uint16_t detune = detune_1 + ((detune_2 - detune_1) * chord_fractional >> 16);
            uint32_t phase_increment1_ = ComputePhaseIncrement(detune - 6890); // Braids detune
            phase_increment1[i] = (phase_increment1_ * (phase_increment_0 >> 14));
        }

        const uint8_t* wave_1 = wavt_waves + mini_wave_line[parameter_[0] >> 10] * 129;
        const uint8_t* wave_2 = wavt_waves + mini_wave_line[(parameter_[0] >> 10) + 1] * 129;
        uint16_t wave_xfade = parameter_[0] << 6;

        for (uint8_t i = 0; i < AUDIO_BLOCK_SAMPLES; i++)
        {
            int32_t sample = 0;

            phase_0 = phasedata[i];
            phase_0 += phase_increment_0;
            phase_1 += phase_increment1[0];
            phase_2 += phase_increment1[1];
            phase_3 += phase_increment1[2];

            sample += Crossfade(wave_1, wave_2, phase_0 >> 1, wave_xfade);
            sample += Crossfade(wave_1, wave_2, phase_1 >> 1, wave_xfade);
            sample += Crossfade(wave_1, wave_2, phase_2 >> 1, wave_xfade);
            sample += Crossfade(wave_1, wave_2, phase_3 >> 1, wave_xfade);
            *bp++ = sample >> 2;
  
            phase_0 += phase_increment_0;
            phase_1 += phase_increment1[0];
            phase_2 += phase_increment1[1];
            phase_3 += phase_increment1[2];

            sample = 0;
            sample += Crossfade(wave_1, wave_2, phase_0 >> 1, wave_xfade);
            sample += Crossfade(wave_1, wave_2, phase_1 >> 1, wave_xfade);
            sample += Crossfade(wave_1, wave_2, phase_2 >> 1, wave_xfade);
            sample += Crossfade(wave_1, wave_2, phase_3 >> 1, wave_xfade);
            *bp++ = sample >> 2;
            i += 1;
        }

        state_saw_phase_[0] = phase_0;
        state_saw_phase_[1] = phase_1;
        state_saw_phase_[2] = phase_2;
        state_saw_phase_[3] = phase_3;
    }
    break;

int16_t Crossfade(const uint8_t* table_a, const uint8_t* table_b,
    uint32_t phase, uint16_t balance)
    {
      int32_t a = Interpolate824_8(table_a, phase);
      int32_t b = Interpolate824_8(table_b, phase);
      return a + ((b - a) * static_cast<int32_t>(balance) >> 16);
  }

int16_t Interpolate824_8(const uint8_t* table, uint32_t phase)
  {
    int32_t a = table[phase >> 24];
    int32_t b = table[(phase >> 24) + 1];
    return (a << 8) + ((b - a) * static_cast<int32_t>(phase & 0xffffff) >> 16) - 32768;
}

static const uint8_t wave_line[] PROGMEM = {
  187, 179, 154, 155, 135, 134, 137, 19, 24, 3, 8, 66, 79, 25, 180, 174, 64,
  127, 198, 15, 10, 7, 11, 0, 191, 192, 115, 238, 237, 236, 241, 47, 70, 76,
  235, 26, 133, 208, 34, 175, 183, 146, 147, 148, 150, 151, 152, 153, 117,
  138, 32, 33, 35, 125, 199, 201, 30, 31, 193, 27, 29, 21, 18, 182
};
static const uint8_t mini_wave_line[] PROGMEM = {
  157, 161, 171, 188, 189, 191, 192, 193, 196, 198, 201, 234, 232,
  229, 226, 224, 1, 2, 3, 4, 5, 8, 12, 32, 36, 42, 47, 252, 254, 141, 139,
  135, 174
};

#define SEMI * 128
static const uint16_t chords[17][3] = {
  { 2, 4, 6 },
  { 16, 32, 48 },
  { 2 SEMI, 7 SEMI, 12 SEMI },
  { 3 SEMI, 7 SEMI, 10 SEMI },
  { 3 SEMI, 7 SEMI, 12 SEMI },
  { 3 SEMI, 7 SEMI, 14 SEMI },
  { 3 SEMI, 7 SEMI, 17 SEMI },
  { 7 SEMI, 12 SEMI, 19 SEMI },
  { 7 SEMI, 3 + 12 SEMI, 5 + 19 SEMI },
  { 4 SEMI, 7 SEMI, 17 SEMI },
  { 4 SEMI, 7 SEMI, 14 SEMI },
  { 4 SEMI, 7 SEMI, 12 SEMI },
  { 4 SEMI, 7 SEMI, 11 SEMI },
  { 5 SEMI, 7 SEMI, 12 SEMI },
  { 4, 7 SEMI, 12 SEMI },
  { 4, 4 + 12 SEMI, 12 SEMI },
  { 4, 4 + 12 SEMI, 12 SEMI },
};

const uint8_t wavt_waves[] PROGMEM = {
wavetable data in Attach files
};

Complete code on my github page
 

Attachments

  • Wavetable.txt
    282.2 KB · Views: 31
Last edited:
Hello Rolf
I mounted teensy and TFT for a preliminary quick test.
Error messages look correct, but main screen looks slanted..how to?..
and what (where) are the "pic files" ?
 
Back
Top