Audio Tool PT8211 Oscillator stability

Status
Not open for further replies.

vincentiuș

Active member
Hey, I am trying to make a project and more precisely an VCO with CV for freq and CV for waveshape for now.
The design is made in the audio tool and the waveshape are fading between them via knob 1, freq via knob 2, etc.
The problem encountered is the lack of stability of the frequency, as you can see in this short video for SQR shape..

IMG_3369 2 copy.jpg


Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthNoiseWhite     noise1;         //xy=80,441
AudioSynthWaveform       waveform1;      //xy=85,69
AudioSynthWaveform       waveform2;      //xy=85,129
AudioSynthWaveform       waveform3;      //xy=85,195
AudioSynthWaveform       waveform4;      //xy=86,255
AudioSynthWaveform       waveform6;      //xy=87,379
AudioSynthWaveform       waveform5;      //xy=89,321
AudioSynthNoisePink      pink1;          //xy=91,589
AudioSynthWaveformSineModulated sine_fm1;       //xy=95,501
AudioMixer4              mixer3;         //xy=329,434
AudioMixer4              mixer2;         //xy=331,281
AudioMixer4              mixer1;         //xy=332.1428527832031,128.14285278320312
AudioOutputPT8211        pt8211_1;       //xy=579,151
AudioConnection          patchCord1(noise1, 0, mixer2, 3);
AudioConnection          patchCord2(waveform1, 0, mixer1, 0);
AudioConnection          patchCord3(waveform1, sine_fm1);
AudioConnection          patchCord4(waveform2, 0, mixer1, 1);
AudioConnection          patchCord5(waveform3, 0, mixer1, 2);
AudioConnection          patchCord6(waveform4, 0, mixer2, 0);
AudioConnection          patchCord7(waveform6, 0, mixer2, 2);
AudioConnection          patchCord8(waveform5, 0, mixer2, 1);
AudioConnection          patchCord9(pink1, 0, mixer3, 2);
AudioConnection          patchCord10(pink1, 0, mixer3, 1);
AudioConnection          patchCord11(pink1, 0, mixer3, 3);
AudioConnection          patchCord12(sine_fm1, 0, mixer3, 0);
AudioConnection          patchCord13(mixer3, 0, pt8211_1, 1);
AudioConnection          patchCord14(mixer2, 0, mixer1, 3);
AudioConnection          patchCord15(mixer1, 0, pt8211_1, 0);
// GUItool: end automatically generated code


const byte KnobPin = A0;

const byte NrParts = 7;
const unsigned int AnalogResolution = 4096;
const unsigned int PartRange = AnalogResolution / (NrParts - 1);
const unsigned int PartMax = 100;

float knobPartValues[NrParts];

void setup(){
 Serial.begin(9600);
  analogReadResolution(12);
  AudioMemory(36);
  waveform1.begin(WAVEFORM_SINE);
  waveform2.begin(WAVEFORM_TRIANGLE);
  waveform3.begin(WAVEFORM_SAWTOOTH);
  waveform4.begin(WAVEFORM_SQUARE);
  waveform5.begin(WAVEFORM_SAWTOOTH_REVERSE);
  waveform6.begin(WAVEFORM_PULSE);
  pink1.amplitude(1.0);
  noise1.amplitude(1.0);
 waveform1.amplitude(1.0);
 waveform2.amplitude(1.0);
 waveform3.amplitude(1.0);
 waveform4.amplitude(1.0);
 waveform5.amplitude(1.0);
 waveform6.amplitude(1.0);
 waveform6.pulseWidth(0.4);
 sine_fm1.amplitude(1.0);
 //pwm1.amplitude(1.0);
 
}

void loop(){
  const unsigned int KnobValue = analogRead(KnobPin);
  //Serial.print(F("KnobValue: "));
  //Serial.println(KnobValue);
  
  for(byte i = 0; i < NrParts; i++){
    //not yet passed
    if(KnobValue + PartRange <= i * PartRange){
      knobPartValues[i] = 0;
    }
    //rising
    else if(KnobValue < (i * PartRange) ){
      knobPartValues[i] = map(KnobValue, (i - 1) * PartRange, i * PartRange - 1, 0, PartMax) / (float)PartMax;
    }
    //falling
    else if(KnobValue < ((i + 1) * PartRange) ){
      knobPartValues[i] = map(KnobValue, i * PartRange, (i + 1) * PartRange - 1, PartMax, 0) / (float)PartMax;
    }
    //passed
    else{
      knobPartValues[i] = 0;
    }
  }

  
mixer1.gain(0, knobPartValues[6]);  
mixer1.gain(1, knobPartValues[5]);
mixer1.gain(2, knobPartValues[3]);
mixer1.gain(3,1.00);
mixer2.gain(0, knobPartValues[4]);
mixer2.gain(1, knobPartValues[2]);
mixer2.gain(2, knobPartValues[1]);
mixer2.gain(3, knobPartValues[0]);
mixer3.gain(0, knobPartValues[6]);
mixer3.gain(1, knobPartValues[5]);
mixer3.gain(2, knobPartValues[3]);
mixer3.gain(3, 0.00);

  float knob2 = (float)analogRead(A1) / 1023.0;
  float knob3 = (float)analogRead(A2) / 1023.0;
 // float knob4 = (float)analogRead(A3) / 1023.0;
  // pwm1.frequency(knob4);
  waveform1.frequency(360 * knob2 + 0.25);
  sine_fm1.frequency(knob3 * 1500 + 50);
  waveform1.frequency(360 * knob2 + 0.25);
  waveform2.frequency(360 * knob2 + 0.25);
  waveform3.frequency(360 * knob2 + 0.25);
  waveform4.frequency(360 * knob2 + 0.25);
  waveform5.frequency(360 * knob2 + 0.25);
  waveform6.frequency(360 * knob2 + 0.25);

    Serial.println(KnobValue);
    Serial.print(" ");
    Serial.print(knobPartValues[6]);
    Serial.print(" ");
    Serial.print(knobPartValues[5]);
    Serial.print(" ");
    Serial.print(knobPartValues[4]);
    Serial.print(" ");
    Serial.print(knobPartValues[3]);
    Serial.print(" ");
    Serial.print(knobPartValues[2]);
    Serial.print(" ");
    Serial.print(knobPartValues[1]);
    Serial.print(" ");
    Serial.print(knobPartValues[0]);
    Serial.print(" knob:");
    
   
    }

Video issue:
[video]https://drive.google.com/open?id=0B3N82IlvifmDTjAxYm43dDB1eFE[/video]

Maybe my code is not well done or maybe something is missing, what do you recommend, should i add something else to my code or do i have to use some tricks To make it more stable? Any suggestions? Thanks!
 
I don't know answers to the issues either of you may be having, but I have personally connected a PT8211 chip to a teensy 3.2, and used it with the audio library PT8211 output object. I tested it by using the SDCARD object and played a WAV file contained on the card ( ie one of the sample programs contained in the library but modified to use the PT8211 output object.) It worked fine for me- no noticeable distortion-subjectively it sounded the same as the SGTL5000 codec.
Granted, I did not use the PJRC 8211 PC board, but I would expect that board must be OK, as the other PJRC boards are great. I know its not much help, but I wanted to let you know the PT8211 does work with the teensy/audio lib.
 
vincentiuș;149781 said:
should i add something else to my code or do i have to use some tricks To make it more stable? Any suggestions? Thanks!

Can you try to reproduce this problem without the pots? Maybe just use fixed numbers for the parameters and see if you get a stable waveform? Or add some code to very gradually change the settings and see if the waveforms change smoothly?

If that doesn't work, at least you could post the simpler no-pots code, which would be a lot easier for me or anyone else to try running on our hardware to compare results.
 
@jdev & @bmillier Is not about any distortion, it's about a instability of the wave shapes freq, see the link on the top :)

@PaulStoffregen Hey, so here is the version with fixed numbers, for sine and triangle appears more stable but for sawtooth and square it's the same, unstable as in the top post video link.
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthNoiseWhite     noise1;         //xy=80,441
AudioSynthWaveform       waveform1;      //xy=85,69
AudioSynthWaveform       waveform2;      //xy=85,129
AudioSynthWaveform       waveform3;      //xy=85,195
AudioSynthWaveform       waveform4;      //xy=86,255
AudioSynthWaveform       waveform6;      //xy=87,379
AudioSynthWaveform       waveform5;      //xy=89,321
AudioSynthNoisePink      pink1;          //xy=91,589
AudioSynthWaveformSineModulated sine_fm1;       //xy=95,501
AudioMixer4              mixer3;         //xy=329,434
AudioMixer4              mixer2;         //xy=331,281
AudioMixer4              mixer1;         //xy=332.1428527832031,128.14285278320312
AudioOutputPT8211        pt8211_1;       //xy=579,151
AudioConnection          patchCord1(noise1, 0, mixer2, 3);
AudioConnection          patchCord2(waveform1, 0, mixer1, 0);
AudioConnection          patchCord3(waveform1, sine_fm1);
AudioConnection          patchCord4(waveform2, 0, mixer1, 1);
AudioConnection          patchCord5(waveform3, 0, mixer1, 2);
AudioConnection          patchCord6(waveform4, 0, mixer2, 0);
AudioConnection          patchCord7(waveform6, 0, mixer2, 2);
AudioConnection          patchCord8(waveform5, 0, mixer2, 1);
AudioConnection          patchCord9(pink1, 0, mixer3, 2);
AudioConnection          patchCord10(pink1, 0, mixer3, 1);
AudioConnection          patchCord11(pink1, 0, mixer3, 3);
AudioConnection          patchCord12(sine_fm1, 0, mixer3, 0);
AudioConnection          patchCord13(mixer3, 0, pt8211_1, 1);
AudioConnection          patchCord14(mixer2, 0, mixer1, 3);
AudioConnection          patchCord15(mixer1, 0, pt8211_1, 0);
// GUItool: end automatically generated code


const byte KnobPin = A0;

const byte NrParts = 7;
const unsigned int AnalogResolution = 4096;
const unsigned int PartRange = AnalogResolution / (NrParts - 1);
const unsigned int PartMax = 100;

float knobPartValues[NrParts];

void setup(){
 //Serial.begin(9600);
  analogReadResolution(12);
  AudioMemory(36);
  waveform1.begin(WAVEFORM_SINE);
  waveform2.begin(WAVEFORM_TRIANGLE);
  waveform3.begin(WAVEFORM_SAWTOOTH);
  waveform4.begin(WAVEFORM_SQUARE);
  waveform5.begin(WAVEFORM_SAWTOOTH_REVERSE);
  waveform6.begin(WAVEFORM_PULSE);
  pink1.amplitude(1.0);
  noise1.amplitude(1.0);
 waveform1.amplitude(1.0);
 waveform2.amplitude(1.0);
 waveform3.amplitude(1.0);
 waveform4.amplitude(1.0);
 waveform5.amplitude(1.0);
 waveform6.amplitude(1.0);
 waveform6.pulseWidth(0.4);
 sine_fm1.amplitude(1.0);
 //pwm1.amplitude(1.0);
 
}

void loop(){

mixer1.gain(0, 0.00);  
mixer1.gain(1, 0.00);
mixer1.gain(2, 1.00);
mixer1.gain(3,1.00);
mixer2.gain(0, 0.00);
mixer2.gain(1, 0.00);
mixer2.gain(2, 0.00);
mixer2.gain(3, 0.00);
mixer3.gain(0, 0.00);
mixer3.gain(1, 0.00);
mixer3.gain(2, 0.00);
mixer3.gain(3, 0.00);

  waveform1.frequency(1000.00);
  waveform2.frequency(1000.00);
  waveform3.frequency(1000.00);
  waveform4.frequency(1000.00);
  waveform5.frequency(1000.00);
  waveform6.frequency(1000.00);
    }
Or add some code to very gradually change the settings and see if the waveforms change smoothly?
Paul, How can I change gradually this in the settings? can you give me an example?
 
Last edited:
Sorry, I missed this earlier.

The problem is your code, not the PT8211.

Your program is adding (mixing) multiple 1.0 amplitude waveforms, using 1.0 gain setting on the mixer channels. That's going to cause massive clipping / harmonic distortion, because the total signal amplitude can't go over 1.0.

This clipping topic is covered in the audio library tutorial, part 2-2 and 2-3 about mixers.

https://www.pjrc.com/store/audio_tutorial_kit.html

Clipping is also a pretty well understood principle of audio electronics. You can find lots of info online about clipping audio.

Ultimately, you need to fix your code so you don't add 2 waveforms in such a way that the result ends up more than the maximum 1.0 amplitude. Or if you do add them together in such a way that it's rare occurrence (as in part 2-3 of the tutorial), you might accept perhaps some distortion as a trade-off, but it is still distorting your signal when it clips.

Just to be perfectly clear, I'm not going to investigate further with this clearly defective code. If you still believe there's a hardware problem (and there may indeed by an as-yet-unknown problem), you'll need to post a better program that uses lower signal levels or gain settings so the signals can't clip in the software, before they ever reach the PT8211 chip.
 
I've tryed above 1.0 and is the same thing. The code is generating values under 1.00, for each wave. i don't have any problems with cliping and distortion, the issue was with stability of the wave table frequency. As you can see in the serial print below.

Screen Shot 2017-10-19 at 13.01.45.png

The pot is fixed in one position but as you can see some values are variable..
Is there another alternative to make it more stabile?
 
When reading analog potentiometers, there is always a huge risk of unstable readings due to supply and quantization noise, as well as dirty ground and wiring picking up noise over the air.

If I were you I'd go for digital rotary encoders instead of the potentiometers. Once set, their value remains for sure stable.

What you will continue to see on square signals is quantization jitter due to the relatively low sampling frequency (44100Hz). That means that without oversampling and a well dimensioned reconstruction filter, you will always see a variation of +/- 1/44100 seconds = +/- 22.7us.
 
@Theremingenieur Thanks, i've tryed with digital rotary encoders and of course, the value is stable in the serial print but no on the dac outputs, still it's a variation. Anyway.. for this can we make a solution in the code or I just have to change the with something better and high resolution dac? what do you sugest if it's your case. Thanks!
 
vincentiuș;158020 said:
@Theremingenieur Thanks, i've tryed with digital rotary encoders and of course, the value is stable in the serial print but no on the dac outputs, still it's a variation. Anyway.. for this can we make a solution in the code or I just have to change the with something better and high resolution dac? what do you sugest if it's your case. Thanks!

Not enough information. If you add or mix several waveforms, it can be normal to have some periodic fluctuation, depending on sum and difference frequencies, due to rounding and quantization errors, etc... I don't actually have a PT8211 here, so I can't check what happens with your project. But it if were my project, I'd thoroughly analyze each stage already on the numeric level and make sure that the computation parts work correctly before blaming the hardware.
 
What I can see from your code above is that some signals go through two mixers, others only through one. Thus, the signals going through 2 mixers are delayed by 512 samples compared to those which go only through one mixer. If these more delayed and less delayed signals are finally summed up, you might have partial cancellation due to phase shift. Re-arrange your stuff so that every signal gets the same delay.
 
IMNSHFO, the problem is rather that the OP doesn't want to narrow down the problem systematically by giving all the required information. I tried just to do some guesswork and to share some general thoughts, awaiting more details which didn't come until now...
 
Here is the stretch with digital encoder and the rearranged connections stretch.

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Encoder.h>

// GUItool: begin automatically generated code
AudioSynthNoiseWhite     noise1;         //xy=77,422
AudioSynthWaveform       waveform1;      //xy=85,69
AudioSynthWaveform       waveform2;      //xy=85,129
AudioSynthWaveform       waveform3;      //xy=85,195
AudioSynthWaveform       waveform4;      //xy=86,255
AudioSynthWaveform       waveform6;      //xy=87,379
AudioSynthWaveform       waveform5;      //xy=89,321
AudioSynthNoisePink      pink1;          //xy=93,572
AudioSynthWaveformSineModulated sine_fm1;       //xy=95,504
AudioMixer4              mixer1;         //xy=332.1428527832031,128.14285278320312
AudioMixer4              mixer2;         //xy=333,255
AudioMixer4              mixer4;         //xy=334,435
AudioMixer4              mixer3;         //xy=501,122
AudioOutputPT8211        pt8211_1;       //xy=697,210
AudioConnection          patchCord1(noise1, 0, mixer2, 2);
AudioConnection          patchCord2(waveform1, 0, mixer1, 0);
AudioConnection          patchCord3(waveform1, sine_fm1);
AudioConnection          patchCord4(waveform2, 0, mixer1, 1);
AudioConnection          patchCord5(waveform3, 0, mixer1, 2);
AudioConnection          patchCord6(waveform4, 0, mixer1, 3);
AudioConnection          patchCord7(waveform6, 0, mixer2, 1);
AudioConnection          patchCord8(waveform5, 0, mixer2, 0);
AudioConnection          patchCord9(pink1, 0, mixer4, 1);
AudioConnection          patchCord10(sine_fm1, 0, mixer4, 0);
AudioConnection          patchCord11(mixer1, 0, mixer3, 0);
AudioConnection          patchCord12(mixer2, 0, mixer3, 1);
AudioConnection          patchCord13(mixer4, 0, pt8211_1, 1);
AudioConnection          patchCord14(mixer3, 0, pt8211_1, 0);
// GUItool: end automatically generated code



//const byte KnobPin = A0;
const byte NrParts = 7;
const unsigned int AnalogResolution = 4096;
const unsigned int PartRange = AnalogResolution / (NrParts - 1);
const unsigned int PartMax = 100;

float knobPartValues[NrParts];

Encoder enc(5, 6);
Encoder enc1(7, 8);

void setup(){

 Serial.begin(256);
  analogReadResolution(12);
  AudioMemory(36);
  waveform1.begin(WAVEFORM_SINE);
  waveform2.begin(WAVEFORM_TRIANGLE);
  waveform3.begin(WAVEFORM_SAWTOOTH);
  waveform4.begin(WAVEFORM_SQUARE);
  waveform5.begin(WAVEFORM_SAWTOOTH_REVERSE);
  waveform6.begin(WAVEFORM_PULSE);
  noise1.amplitude(1.0);
 pink1.amplitude(1.0);
 waveform1.amplitude(1.0);
 waveform2.amplitude(1.0);
 waveform3.amplitude(1.0);
 waveform4.amplitude(1.0);
 waveform5.amplitude(1.0);
 waveform6.amplitude(1.0);
 waveform6.pulseWidth(0.6);
}

void loop(){  
//Freq
  int val1 = enc.read(); 
  if(val1 <= 0) { // constrain for below 0
      enc.write(0); // constrain the encoder object
    }
    else if(val1 >= 1000){
enc.write(1000); 
}
float frqG = constrain(val1,0.1,1000.00); 

//Waves
int Value = enc1.read();
if(Value<=0){
 enc1.write(0);
 }
else if(Value>=1000){
 enc1.write(1000);
  }

int valx = constrain(Value,0,1000);
unsigned int KnobValue = valx * 4.095;

//mixer loop
  for(byte i = 0; i < NrParts; i++){
    //not yet passed
    if(KnobValue + PartRange <= i * PartRange){
      knobPartValues[i] = 0;
    }
    //rising
    else if(KnobValue < (i * PartRange) ){
      knobPartValues[i] = map(KnobValue, (i - 1) * PartRange, i * PartRange - 1, 0, PartMax) / (float)PartMax;
    }
    //falling
    else if(KnobValue < ((i + 1) * PartRange) ){
      knobPartValues[i] = map(KnobValue, i * PartRange, (i + 1) * PartRange - 1, PartMax, 0) / (float)PartMax;
    }
    //passed
    else{
      knobPartValues[i] = 0;
    }
  }

  waveform1.frequency(frqG*4.092);
  waveform2.frequency(frqG*4.092);
  waveform3.frequency(frqG*4.092);
  waveform4.frequency(frqG*4.092);
  waveform5.frequency(frqG*4.092);
  waveform6.frequency(frqG*4.092);
  
mixer1.gain(0, knobPartValues[0]);  //sine
mixer1.gain(1, knobPartValues[1]);  //trinagle
mixer1.gain(2, knobPartValues[2]);  //Saw
mixer1.gain(3, knobPartValues[3]);  //SQR
mixer2.gain(0, knobPartValues[4]);  //Rsaw
mixer2.gain(1, knobPartValues[5]);  //Pulse
mixer2.gain(2, knobPartValues[6]);  //noise
mixer2.gain(3, 0.0);  //noise

mixer3.gain(0, 1.0);
mixer3.gain(1, 1.0);
mixer3.gain(2, 0.0);
mixer3.gain(3, 0.0);

mixer4.gain(0, knobPartValues[6]);
mixer4.gain(1, knobPartValues[5]);
mixer4.gain(2, 0.0);
mixer4.gain(3, 0.0);
 

    Serial.println(frqG*4.092);
    Serial.print(" ");
    Serial.print(knobPartValues[0]);
    Serial.print(" ");
    Serial.print(knobPartValues[1]);
    Serial.print(" ");
    Serial.print(knobPartValues[2]);
    Serial.print(" ");
    Serial.print(knobPartValues[3]);
    Serial.print(" ");
    Serial.print(knobPartValues[4]);
    Serial.print(" ");
    Serial.print(knobPartValues[5]);
    Serial.print(" ");
    Serial.print(knobPartValues[6]);
    Serial.print(" ");
    Serial.print(Value);
    Serial.print(" ");
    }

Screen Shot 2017-11-23 at 17.52.44.png

Serial print
Screen Shot 2017-11-23 at 18.06.47.png

Also as you can see in the serial print, the numbers are stable but the output dac for square wave (for instance ), i encountered some values for which is not so stable.
What do you think about calculations on your first impression?
 
Last edited:
Status
Not open for further replies.
Back
Top