Polyphony problem in a synth project using Teensy 3.6

Status
Not open for further replies.

theSmith

New member
Hi everyone,
I'm working on a synth project using a Teensy 3.6 and the Audio Board, right now I have a first working prototype of the circuit (by now just a MIDI interface, the Teensy and some led), so I'm working on the software part, though I'm NOT using the complete Teensy Audio Library, with all the objects that Paul kindly provided us with. I'm trying to develop my own objects for sound synthesis.
I had a previous experience with the library and in that occasion I changed the value of the constant AUDIO_BLOCK_SAMPLES from 128 to 16, in order to have less latency. For this project I'm keeping the same value and in my custom objects I'm using buffers which are of size 16.
The current setup for reproducing audio in output is just an AudioPlayQueue (into which I copy my buffer) with the output connected to an AudioOutputI2S object (stereo audio will be a future feature).
Up to now I managed to reproduce a sinusoidal wave and a square wave of any frequency without any problem, so today I tried to implement polyphony.

This is where things become problematic. Apparently, when more than a simple wave is played, some samples of zero value are added to my composite wave. This seems to me the case in which the buffer I give to the AudioPlayQueue is too small and the remaining part is set to 0. The weird thing is that when I play a single simple waveform, this phenomenon does not occur; and if I play a richer waveform (e.g. three or four sinusoids summed together) the quantity of zero samples increases while the number of samples of my original wave decreases.
Here is a picture taken with an oscilloscope of my output given the sum of two sinusoids, the fundamental of frequency 392Hz and the harmonic one octave above.
NewFile1.jpg

Here I've copied a sample code with which I generated this image (you can see that the composition of sinusoids is just mathematical sum):
Arduino file:
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <MIDI.h>
#include "SoundEngine.h"

SoundEngine sound; // Sound generation module (TODO: include also AudioIn?)
AudioOutputI2S outputDAC; // Output port for DAC
AudioPlayQueue outQueue; // Buffer to the output
AudioControlSGTL5000 audioBoard;

AudioConnection patchCord1(outQueue, 0, outputDAC, 0);

const int led  = 3;
const int midiLed = 2;

int cntLed = 0;

int type, note, velocity, channel, d1, d2;
double* buf;
int16_t* bufInt;
const int bufDim = AUDIO_BLOCK_SAMPLES*2;
int sample=0;

void setup()
{
    Serial.begin(57600);
    buf = new double[AUDIO_BLOCK_SAMPLES];
    bufInt = new int16_t[AUDIO_BLOCK_SAMPLES];

    pinMode(led, OUTPUT);
    pinMode(midiLed, OUTPUT);
    AudioMemory(45);
    
    AudioNoInterrupts();

    audioBoard.enable();
    audioBoard.lineOutLevel(13);
    audioBoard.volume(0.5);

    audioBoard.muteHeadphone();
    audioBoard.muteLineout();
    audioBoard.adcHighPassFilterEnable();
    audioBoard.unmuteLineout();
    
    MIDI.begin(MIDI_CHANNEL_OMNI);
    
    digitalWrite(led, HIGH);
    delay(1000);
    digitalWrite(led, LOW);
    delay(1000);
    digitalWrite(led, HIGH);
    delay(1000);
    digitalWrite(led, LOW);
    delay(1000);
    digitalWrite(led, HIGH);
    
    AudioInterrupts();
}

void convert(double* in, int16_t* out, int n) {
    for (int i=0; i<n; ++i) {
        out[i] = (int16_t)32768*in[i];
    }
    return;
}

void loop()
{
    if (millis() == 8000) sound.newMidiMessage(9, 67, 127);
    
    if (MIDI.read()) {
        digitalWrite(midiLed, HIGH);
        type = MIDI.getType();
        switch(type) {
            case NoteOn:
                note = MIDI.getData1();
                velocity = MIDI.getData2();
                channel = MIDI.getChannel();
                if (velocity > 0) {
                    sound.newMidiMessage(9, note, velocity);
                    //MIDI.sendNoteOn(note, velocity, channel);
                    Serial.println(String("Note On:  ch=") + channel + ", note=" + note + ", velocity=" + velocity);
                } else {
                    sound.newMidiMessage(8, note, velocity);
                    //MIDI.sendNoteOff(note, velocity, channel);
                    Serial.println(String("Note Off: ch=") + channel + ", note=" + note);
                }
                break;
            case NoteOff:
                note = MIDI.getData1();
                velocity = MIDI.getData2();
                channel = MIDI.getChannel();
                sound.newMidiMessage(8, note, velocity);
                //MIDI.sendNoteOff(note, velocity, channel);
                Serial.println(String("Note Off: ch=") + channel + ", note=" + note + ", velocity=" + velocity);
                break;
            default:
                d1 = MIDI.getData1();
                d2 = MIDI.getData2();
                Serial.println(String("Message, type=") + type + ", data = " + d1 + " " + d2);
        }
    }
    
    cntLed++;
    if (cntLed==200) {
        cntLed = 0;
        digitalWrite(midiLed, LOW);
    }
    buf = sound.play();
    convert(buf, bufInt, bufDim/2);
    memcpy(outQueue.getBuffer(), bufInt, bufDim);
    outQueue.playBuffer();
}

SoundEngine class:
Code:
#ifndef CODE_SOUNDENGINE_H
#define CODE_SOUNDENGINE_H

#include <stdint.h>
#include <math.h>

#define CHUNK_SIZE 16
#define SAMPLING_FREQUENCY 44100
#define POLYPHONY 12
#define NOTE_ON 9
#define NOTE_OFF 8
#define NOTES 128

class SoundEngine {
public:
    SoundEngine();
    virtual ~SoundEngine();
    double* play();
    void newMidiMessage(uint8_t type, uint8_t data1, uint8_t data2);
private:
    double buffer[CHUNK_SIZE];
    bool isPlaying;
    float currentFrequency;
    float currentGain;
    uint16_t sample;
    const float frequencies[NOTES] = {8.1757989156f,
                                8.6619572180f,
                                9.1770239974f,
                                9.7227182413f,
                                10.3008611535f,
                                10.9133822323f,
                                11.5623257097f,
                                12.2498573744f,
                                12.9782717994f,
                                13.7500000000f,
                                14.5676175474f,
                                15.4338531643f,
                                16.3515978313f,
                                17.3239144361f,
                                18.3540479948f,
                                19.4454364826f,
                                20.6017223071f,
                                21.8267644646f,
                                23.1246514195f,
                                24.4997147489f,
                                25.9565435987f,
                                27.5000000000f,
                                29.1352350949f,
                                30.8677063285f,
                                32.7031956626f,
                                34.6478288721f,
                                36.7080959897f,
                                38.8908729653f,
                                41.2034446141f,
                                43.6535289291f,
                                46.2493028390f,
                                48.9994294977f,
                                51.9130871975f,
                                55.0000000000f,
                                58.2704701898f,
                                61.7354126570f,
                                65.4063913251f,
                                69.2956577442f,
                                73.4161919794f,
                                77.7817459305f,
                                82.4068892282f,
                                87.3070578583f,
                                92.4986056779f,
                                97.9988589954f,
                                103.8261743950f,
                                110.0000000000f,
                                116.5409403795f,
                                123.4708253140f,
                                130.8127826503f,
                                138.5913154884f,
                                146.8323839587f,
                                155.5634918610f,
                                164.8137784564f,
                                174.6141157165f,
                                184.9972113558f,
                                195.9977179909f,
                                207.6523487900f,
                                220.0000000000f,
                                233.0818807590f,
                                246.9416506281f,
                                261.6255653006f,
                                277.1826309769f,
                                293.6647679174f,
                                311.1269837221f,
                                329.6275569129f,
                                349.2282314330f,
                                369.9944227116f,
                                391.9954359817f,
                                415.3046975799f,
                                440.0000000000f,
                                466.1637615181f,
                                493.8833012561f,
                                523.2511306012f,
                                554.3652619537f,
                                587.3295358348f,
                                622.2539674442f,
                                659.2551138257f,
                                698.4564628660f,
                                739.9888454233f,
                                783.9908719635f,
                                830.6093951599f,
                                880.0000000000f,
                                932.3275230362f,
                                987.7666025122f,
                                1046.5022612024f,
                                1108.7305239075f,
                                1174.6590716696f,
                                1244.5079348883f,
                                1318.5102276515f,
                                1396.9129257320f,
                                1479.9776908465f,
                                1567.9817439270f,
                                1661.2187903198f,
                                1760.0000000000f,
                                1864.6550460724f,
                                1975.5332050245f,
                                2093.0045224048f,
                                2217.4610478150f,
                                2349.3181433393f,
                                2489.0158697766f,
                                2637.0204553030f,
                                2793.8258514640f,
                                2959.9553816931f,
                                3135.9634878540f,
                                3322.4375806396f,
                                3520.0000000000f,
                                3729.3100921447f,
                                3951.0664100490f,
                                4186.0090448096f,
                                4434.9220956300f,
                                4698.6362866785f,
                                4978.0317395533f,
                                5274.0409106059f,
                                5587.6517029281f,
                                5919.9107633862f,
                                6271.9269757080f,
                                6644.8751612791f,
                                7040.0000000000f,
                                7458.6201842894f,
                                7902.1328200980f,
                                8372.0180896192f,
                                8869.8441912599f,
                                9397.2725733570f,
                                9956.0634791066f,
                                10548.0818212118f,
                                11175.3034058561f,
                                11839.8215267723f,
                                12543.8539514160f};
};
#endif //CODE_SOUNDENGINE_H

Code:
#include "SoundEngine.h"

SoundEngine::SoundEngine() {
    currentFrequency = 0;
    isPlaying = false;
    currentGain = 1;
}

SoundEngine::~SoundEngine() {
}

double* SoundEngine::play() {
    for (int i=0; i<CHUNK_SIZE; ++i) buffer[i] = 0;
    for (int i=0; i<CHUNK_SIZE; ++i) {
        if (isPlaying) {
            buffer[i] = currentGain*(0.5*sin(2*3.1415*currentFrequency*sample/44100) + 0.5*sin(2*3.1415*2*currentFrequency*sample/44100));
            sample++;
            if (sample==44100/currentFrequency) sample=0;
        }
    }
    return buffer;
}

void SoundEngine::newMidiMessage(uint8_t type, uint8_t data1, uint8_t data2) {
    if (type==NOTE_ON && data2>0) {
        isPlaying = true;
        currentFrequency = frequencies[data1];
        currentGain = (float)data2/127;
    } else if (type==NOTE_OFF || (type==NOTE_ON && data2==0)) {
        isPlaying = false;
        sample = 0;
    }
}

Anyone has a hint on why is this happening?
 
Status
Not open for further replies.
Back
Top