Wavetable Synth Distortion issues with Audio Shield

Status
Not open for further replies.

topicbird

Active member
Hello!

Currently I am working on a 16-step sequencer with a touch display, to draw the individual waves on them, which you can put on any of the steps. For some reason, the sound coming from the Audio Shield is distorted, even if I choose the WAVEFORM_SINE. I did not have the problem, as I wrote my code in one main code, but since I changed to VSC and try to order my code by using classes, this weird problem with distortion just wont go away.. I would appreciate any kind of help! :)

Code:
main.cpp:
#include <Arduino.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Encoder.h>
#include "Display.h"
#include "Sequencer.h"

Display display;
Sequencer sequencer;

int b1 = 0;
int b2 = 0;
int b3 = 0;
int b4 = 0;

void ADSR(float att, float dec, float sus, float rel)
{
  // b1 = digitalRead(20);
  b2 = digitalRead(2);
  b3 = digitalRead(3);
  b4 = digitalRead(4);
}

void setup()
{
  display.init();
}

void loop()
{
  display.update();
  sequencer.update();
}

Code:
Display.h:
#ifndef Display_h
#define Display_h
#include <array>
#include <SPI.h>
#include <ILI9341_t3.h>
#include <font_Arial.h>
#include <XPT2046_Touchscreen.h>

class Display
{
public:
  Display();
  // int getX();
  // int getY();
  void fillWaveArray();
  void clear();
  void init();
  void drawButtons();
  void start();
  void touchButton();
  int16_t *getArray();
  float loPass(float xInput, float xOutput);
  boolean pointInRect(int x, float y, float rectX, float rectY, float rectW, float rectH);
  boolean isTouched();
  TS_Point getPoint();
  void update();
  void linearInterpolation(TS_Point pTS);
  boolean previousIsTouched;
 // float mapper(float x, float in_min, float in_max, float out_min, float out_max);
  int X_old;
  int X;
  int maximumX = 256;
  int minimumX = 0;
  int maximumY = 32767;
  int minimumY = -32767;
  void draw(TS_Point pTS);
  int _heightSquare = 20;
  int _widthSquare = 75;
  int _gap = 4;
  int _beyondDisplay = 400; // Value to use for the else statement of the getX / getY methods
  int _radiusOfRoundCorner = 3;
  int16_t initValue;
  int16_t wave1Values[257] = {initValue};
  int _x;
  int _y;
  boolean _touched;

private:
  XPT2046_Touchscreen ts;
  ILI9341_t3 tft;
  int pointRadius = 2;
  char buttonNames[4][10] = { //First bracket indicates the amount of words
      "Clear",                // second bracket indicates the amount of maximal letters for one string
      "<----",
      "---->",
      "Waves"};
};

#endif

Display.cpp:
Code:
#include "Display.h"

// XPT2046_Touchscreen ts(CS_PIN); // Instanziierung eines ts Objekts der Klasse XPT2046_Touchscreen
// ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);
#define TIRQ_PIN 2
#define CS_PIN 8
#define TFT_DC 9
#define TFT_CS 10

Display::Display() : ts(CS_PIN), tft(TFT_CS, TFT_DC)
{
}

// Original raw minimum and maximum values:
// X --> 340 bis 3900
// Y --> 200 bis 3850

boolean Display::isTouched()
{
    return ts.touched();
}

TS_Point Display::getPoint()
{
    int16_t xScaled{0};
    int16_t yScaled{0};
    TS_Point pUnscaled = ts.getPoint(); // point getter function
    if (map(pUnscaled.x, 340, 3900, maximumX, minimumX) < minimumX)
    {
        xScaled = minimumX;
    }
    else if (map(pUnscaled.x, 340, 3900, maximumX, minimumX) > maximumX)
    {
        xScaled = maximumX;
    }
    else
    {
        xScaled = map(pUnscaled.x, 340, 3900, maximumX, minimumX);
    }

    if (map(pUnscaled.y, 200, 3850, maximumY, minimumY) < minimumY)
    {
        yScaled = minimumY;
    }

    else if (map(pUnscaled.y, 200, 3850, maximumY, minimumY) > maximumY)
    {
        yScaled = maximumY;
    }
    else
    {
        yScaled = map(pUnscaled.y, 200, 3850, maximumY, minimumY);
    }
    TS_Point pScaled{xScaled, yScaled, 0};
    return pScaled;
}

int16_t *Display::getArray()
{
    return wave1Values;
}

void Display::drawButtons()
{
    for (int i{0}; i < 4; ++i)
    {
        tft.fillRoundRect(_gap + (_gap + _widthSquare) * i, 220, _widthSquare, _heightSquare, _radiusOfRoundCorner, CL(250, 0, 0));
        tft.setCursor(26 + ((_gap + _widthSquare) * i), 227);
        tft.print(buttonNames[i]);
    }
    tft.drawLine(0, 120, 320, 120, CL(255, 255, 255));
    tft.drawLine(0, 121, 320, 121, CL(255, 255, 255));
    tft.drawLine(0, 119, 320, 119, CL(255, 255, 255));
}

void Display::clear()
{
    tft.fillScreen(ILI9341_BLACK);
    drawButtons();
}

void Display::start()
{
    Serial.begin(38600);
    tft.begin();
    tft.setRotation(1);
    tft.fillScreen(ILI9341_BLACK);
    ts.begin();
    ts.setRotation(1);
    while (!Serial && (millis() <= 1000))
        ; // weiß nicht was das ist
}

boolean Display::pointInRect(int x, float y, float rectX, float rectY, float rectW, float rectH)
{
    boolean ret = false;
    if ((x >= rectX) && (x <= (rectX + rectW)) && (y >= rectY) && (y <= (rectY + rectH)))
    {
        ret = true;
    }
    return ret;
}

void Display::touchButton()
{
    if (pointInRect(_x, _y, _gap, (240 - _heightSquare), (_widthSquare), (_heightSquare)))
    {
        tft.fillRoundRect(_gap, (240 - _heightSquare), _widthSquare, _heightSquare, _radiusOfRoundCorner, CL(250, 100, 0));
        clear();
        // clearArray();
    }
}

void Display::draw(TS_Point pTS)
{
    wave1Values[pTS.x] = pTS.y;
 //   Serial.println(*getArray());

    for (int i{0}; i < 257; ++i)
    {
        tft.fillCircle(map(i, 0, 256, 0, 320), map(wave1Values[i], -32767, 32767, 0, 240), pointRadius, CL(255, 0, 0));
    }
}

/* float Display::mapper(float x, float in_min, float in_max, float out_min, float out_max)
{
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
} */

void Display::linearInterpolation(TS_Point pTS)
{
    if (X_old <= pTS.x)
    {
        for (int i = X_old; i < pTS.x; ++i)
        {
            wave1Values[i] = map(i, X_old, pTS.x, wave1Values[X_old], wave1Values[pTS.x]);
        }
    }
    else
    {
        for (int i = pTS.x; i < X_old; ++i)
        {
            wave1Values[i] = map(i, X_old, pTS.x, wave1Values[X_old], wave1Values[pTS.x]);
        }
    }
    X_old = pTS.x;
}

void Display::init()
{
    start();
    clear();
    drawButtons();
}

void Display::update()
{
    if (isTouched())
    {
        TS_Point pTS = getPoint();
        if (previousIsTouched)
        {
            linearInterpolation(pTS);
        }
        touchButton();
        clear();
        draw(pTS);
    }
    previousIsTouched = isTouched();
}

Sequencer.h;
Code:
#ifndef Sequencer_h
#define Sequencer_h
#include <array>
#include <Audio.h>
#include <Wire.h>
#include "Display.h"
#include <MegunoLink.h>
#include <Filter.h>
#include "Encoder.h"

class Sequencer
{
public:
  Sequencer();
  void start();
  void stop();
  unsigned int counter(float interval);
  void stepButtons();
  void buttonsAbfragen(int zaehler, int digitalPin);
  void stepLED();
  void update();
  float getBPMInterval();
  void muxUpdate();
  long encoder();
  float mapper(float x, float in_min, float in_max, float out_min, float out_max);
  int _deflectionRate;
  int frequency1 = {160};
  int frequency2 = {200};
  int frequency3 = {140};
  int frequency4 = (110);
  ExponentialFilter<long> ADCFilter;
  std::array<float, 4> attack;
  std::array<float, 4> decay;
  std::array<float, 4> sustain;
  std::array<float, 4> release;
  std::array<float, 4> amplitude;
  std::array<int, 4> frequency{{frequency1, frequency2, frequency3, frequency4}};
  std::array<int, 4> m_stepLEDPin{{3, 4, 5, 6}};
  std::array<int, 4> m_stepButtonPin{{0, 1, 14, 15}};
  std::array<boolean, 4> m_stepState{{true, true, true, true}};
  std::array<boolean, 4> digitalReadValues;
  int16_t *wave1Values;
  float defaultAttackValue = {50};
  float defaultDecayValue = {200};
  float defaultSustainValue = {200};
  float defaultReleaseValue = {200};
  float defaultVolume = {0.3};
  unsigned long m_lastMillis;
  float m_interval = (60.0 / average_bpm) * 1000.0;
  unsigned long m_stepStateInterval = 20;
  unsigned int m_STEPNUM = 4;
  int16_t AudioWaveformSineExample[257] = {
      0, 804, 1608, 2410, 3212, 4011, 4808, 5602, 6393, 7179,
      7962, 8739, 9512, 10278, 11039, 11793, 12539, 13279, 14010, 14732,
      15446, 16151, 16846, 17530, 18204, 18868, 19519, 20159, 20787, 21403,
      22005, 22594, 23170, 23731, 24279, 24811, 25329, 25832, 26319, 26790,
      27245, 27683, 28105, 28510, 28898, 29268, 29621, 29956, 30273, 30571,
      30852, 31113, 31356, 31580, 31785, 31971, 32137, 32285, 32412, 32521,
      32609, 32678, 32728, 32757, 32767, 32757, 32728, 32678, 32609, 32521,
      32412, 32285, 32137, 31971, 31785, 31580, 31356, 31113, 30852, 30571,
      30273, 29956, 29621, 29268, 28898, 28510, 28105, 27683, 27245, 26790,
      26319, 25832, 25329, 24811, 24279, 23731, 23170, 22594, 22005, 21403,
      20787, 20159, 19519, 18868, 18204, 17530, 16846, 16151, 15446, 14732,
      14010, 13279, 12539, 11793, 11039, 10278, 9512, 8739, 7962, 7179,
      6393, 5602, 4808, 4011, 3212, 2410, 1608, 804, 0, -804,
      -1608, -2410, -3212, -4011, -4808, -5602, -6393, -7179, -7962, -8739,
      -9512, -10278, -11039, -11793, -12539, -13279, -14010, -14732, -15446, -16151,
      -16846, -17530, -18204, -18868, -19519, -20159, -20787, -21403, -22005, -22594,
      -23170, -23731, -24279, -24811, -25329, -25832, -26319, -26790, -27245, -27683,
      -28105, -28510, -28898, -29268, -29621, -29956, -30273, -30571, -30852, -31113,
      -31356, -31580, -31785, -31971, -32137, -32285, -32412, -32521, -32609, -32678,
      -32728, -32757, -32767, -32757, -32728, -32678, -32609, -32521, -32412, -32285,
      -32137, -31971, -31785, -31580, -31356, -31113, -30852, -30571, -30273, -29956,
      -29621, -29268, -28898, -28510, -28105, -27683, -27245, -26790, -26319, -25832,
      -25329, -24811, -24279, -23731, -23170, -22594, -22005, -21403, -20787, -20159,
      -19519, -18868, -18204, -17530, -16846, -16151, -15446, -14732, -14010, -13279,
      -12539, -11793, -11039, -10278, -9512, -8739, -7962, -7179, -6393, -5602,
      -4808, -4011, -3212, -2410, -1608, -804, 0};
  unsigned int m_step = 0;
  const int numReadings = 5; // Anzahl der Readings
  int readings[5];           // the readings from the analog input
  int readIndex = 0;         // the index of the current reading
  int total = 0;             // the running total
  float average = 0;         // the average
  int inputPin = A3;         // Analog input
  int average_bpm = 120;
  int average_bpm_alt = 120;
  unsigned long m_BPMInterval = 400;
  int m_AnalogThreshold = 3;
  int ADCFilterBefore;
  float interval;
  float minBPM = 40.0;
  float maxBPM = 800.0;
  int pin_Out_S0 = 0;
  int pin_Out_S1 = 1;
  int pin_Out_S2 = 2;
  int buttonState[48];
  int bit1 = 0;
  int bit2 = 0;
  int bit3 = 0;
  long positionEnc1 = -999;
  Encoder Enc1;
  AudioSynthWaveform *waveform = new AudioSynthWaveform[4];
  AudioEffectEnvelope *envelope = new AudioEffectEnvelope[4];
  AudioMixer4 mixer1;
  AudioOutputI2S i2s1;
  AudioConnection patchCord1;
  AudioConnection patchCord2;
  AudioConnection patchCord3;
  AudioConnection patchCord4;
  AudioConnection patchCord5;
  AudioConnection patchCord6;
  AudioConnection patchCord7;
  AudioConnection patchCord8;
  AudioConnection patchCord9;
  AudioConnection patchCord10;
  AudioControlSGTL5000 sgtl5000_1;
  Display _display;

private:
};

#endif

Sequencer.cpp:
Code:
#include "Sequencer.h"
#include "Display.h"

Sequencer::Sequencer() : patchCord1(waveform[1], envelope[1]),
                         patchCord2(waveform[2], envelope[2]),
                         patchCord3(waveform[3], envelope[3]),
                         patchCord4(waveform[0], envelope[0]),
                         patchCord5(envelope[3], 0, mixer1, 3),
                         patchCord6(envelope[2], 0, mixer1, 2),
                         patchCord7(envelope[0], 0, mixer1, 0),
                         patchCord8(envelope[1], 0, mixer1, 1),
                         patchCord9(mixer1, 0, i2s1, 0),
                         patchCord10(mixer1, 0, i2s1, 1),
                         _display(),
                         ADCFilter(60, 0),
                         Enc1(21, 22)

{

    for (int i{0}; i < 4; ++i)
    {
        envelope[i].attack(attack[i]);
        envelope[i].decay(decay[i]);
        envelope[i].sustain(sustain[i]);
        envelope[i].release(release[i]);
    }
    AudioMemory(20);
    sgtl5000_1.enable();
    sgtl5000_1.volume(defaultVolume);
    attack.fill(defaultDecayValue);
    decay.fill(defaultDecayValue);
    sustain.fill(defaultSustainValue);
    release.fill(defaultSustainValue);
    amplitude.fill(defaultVolume);

    for (unsigned int i{0}; i < 4; ++i)
    {
        mixer1.gain(i, defaultVolume);
        waveform[i].begin(WAVEFORM_SINE); // WAVEFORM_SINE expands to 0 WAVEFORM_ARBITRARY expands to 4
        waveform[i].amplitude(amplitude[i]);
        waveform[i].frequency(frequency[i]);
        pinMode(m_stepLEDPin[i], OUTPUT);
        pinMode(m_stepButtonPin[i], INPUT_PULLUP);
    }
}

float Sequencer::getBPMInterval()
{
    int RawValue = analogRead(A2);
    ADCFilter.Filter(RawValue);
    if ((millis() - m_lastMillis) > m_BPMInterval && ADCFilterBefore != ADCFilter.Current() && (ADCFilterBefore - ADCFilter.Current() > m_AnalogThreshold))
    {
        m_lastMillis = millis();
        ADCFilterBefore = ADCFilter.Current();
    }

    // subtract the last reading:
    total = total - readings[readIndex]; // Alle analoge Inputwerte zusammenaddiert, wobei der Wert der neuen Loop abgezogen wird (zieht den nullten Wert ab)
    // read from the sensor:
    readings[readIndex] = RawValue;

    // add the reading to the total:
    total = total + readings[readIndex];
    // advance to the next position in the array:
    readIndex = readIndex + 1;

    if (readIndex >= numReadings)
    {
        // ...wrap around to the beginning:
        readIndex = 0;

        // calculate the average:
        average = total / numReadings;
        average_bpm = mapper(average, 1.0, 1023.0, minBPM, maxBPM);
    }
    if (average_bpm != average_bpm_alt)
    {
        average_bpm_alt = average_bpm;
    }
    float m_interval = (60.0 / average_bpm) * 1000.0;
    return m_interval;
}

unsigned int Sequencer::counter(float interval) // Returns the current position
{
    if ((millis() - m_lastMillis) > getBPMInterval()) // m_interval = 1000 ---> 60 bpm , m_interval = 500 ---> 120bpm
    {
        m_lastMillis = millis();
        m_step++;
        if (m_step == m_STEPNUM)
        {
            m_step = 0;
        }
    }
    return m_step;
}

void Sequencer::stepButtons()
{
    std::array<boolean, 4> digitalReadValues = {digitalRead(0), digitalRead(1), digitalRead(14), digitalRead(15)};

    for (unsigned int i{0}; i < digitalReadValues.size(); ++i)
    {
        if ((millis() - m_lastMillis) > m_stepStateInterval)
        {
            if (digitalReadValues[i] == LOW) // LOW = GEDRÜCKT
            {
                m_stepState[i] = !m_stepState[i];
            }
        }
    }
}

void Sequencer::stepLED()
{
    if (_display.isTouched())
    {
        wave1Values = _display.getArray();
    }
    waveform[0].arbitraryWaveform(wave1Values, 20000);
    counter(getBPMInterval()); //No constructor needed because you already wrote Sequencer::stepLED()
    for (unsigned int i{0}; i < m_stepLEDPin.size(); ++i)
    {
        if (counter(getBPMInterval()) == 0)
        {
            digitalWrite(m_stepLEDPin[0], m_stepState[0]);
            digitalWrite(m_stepLEDPin[3], LOW);

            envelope[0].noteOn();
            waveform[0].amplitude(m_stepState[0]);
            waveform[0].frequency(frequency[0]);
        }
        else if (counter(getBPMInterval()) == i)
        {
            digitalWrite(m_stepLEDPin[i], m_stepState[i]);
            digitalWrite(m_stepLEDPin[i] - 1, LOW);
            envelope[i].noteOn();
            waveform[i].amplitude(m_stepState[i]);
            waveform[i].frequency(frequency[i]);
        }
    }
}

float Sequencer::mapper(float x, float in_min, float in_max, float out_min, float out_max)
{
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void Sequencer::buttonsAbfragen(int zaehler, int digitalPin)
{
    buttonState[zaehler] = digitalRead(digitalPin);
    // delayMicroseconds(50);
   // Serial.print(buttonState[zaehler]);
   // Serial.print(",");
}

long Sequencer::encoder()
{
  long newEnc1;
  newEnc1 = Enc1.read();
  Serial.println(newEnc1);

  if (newEnc1 != positionEnc1)
  {
    positionEnc1 = newEnc1;
  }
  return newEnc1;
}

void Sequencer::muxUpdate()
{
    for (int i = 0; i <= 7; i++)
    {
        bit1 = bitRead(i, 0);
        bit2 = bitRead(i, 1);
        bit3 = bitRead(i, 2);

        digitalWrite(pin_Out_S0, bit1);
        digitalWrite(pin_Out_S1, bit2);
        digitalWrite(pin_Out_S2, bit3);

        buttonsAbfragen(i, 14);      // Mux 1
        buttonsAbfragen(i + 8, 15);  // Mux 2
        buttonsAbfragen(i + 16, 16); // Mux 3
        buttonsAbfragen(i + 24, 17); // Mux 4
        buttonsAbfragen(i + 32, 18); // Mux 5
        buttonsAbfragen(i + 40, 19); // Mux 6
    }
}

void Sequencer::update()
{
    encoder();
    stepButtons();
    stepLED();
    muxUpdate();
}
 
Maybe move stuff like this back to setup?

Code:
    AudioMemory(20);
    sgtl5000_1.enable();
    sgtl5000_1.volume(defaultVolume);

If that doesn't fix the problem, just keep moving more stuff back to the main program until you find the thing that makes it work as it once did.
 
The default volume is 0.3 and 4 x 0.3 > 1.0 so your mixer output is clipping I think. Try 0.25.

Ah, but I think your oscillators are at 0.3 already so that won't be it....

Ah, looked again, you are setting the amplitude to m_stepState as well, which is boolean so
will be 1.0, so if more that 3 oscillators active the mixer will clip.
 
Status
Not open for further replies.
Back
Top