making Mozzi work with Teensy Audio Adaptor (SGTL5000)

Status
Not open for further replies.

jidagraphy

Active member
Hi guys
Recently got a teensy 3.2 and a audio adaptor along with it, thinking that i could program Mozzi and have a basic inputs and outputs bundled on the adaptor for easy access.

well, turns out I didn't do my research and the ports are NOT simply redirecting signals to A14 and whatnot, but they're digial inputs that works with SGTL5000. you know what, i'm still not sure. correct me if I'm talking unicorns.

I could just get a headphone jack connected to A14 but i want to actually use the adaptor.
so i did muh research and managed to have Mozzi's output streamed into I2S's AudioPlayQueue :cool:

References from
https://forum.pjrc.com/threads/2825...rom-A14-to-Headphones-Teensy-3-1-audio-Shield
https://forum.pjrc.com/threads/27736-variable-I2S-master-clock-(and-sampling)-frequency

So what I'm doing is :
- have a typical Mozzi project. Code below is a modification of a Mozzi example Control Echo Theremin. I modded it so a sine wave's pitch alters according to input pin 2.
- in each updateAudio(), I collected the samples before its returned, into a buffer(just a simple list of 128 int16_t)
- when the buffer hits the end, I'd put them in a memory from where the queue resides,
- playQueue() and restart the buffer
- also got the hint from the above thread to change the STGL5000's sample rate to 16384.
- tada

Took me long to fiddle with buffer size and where to put any functions.. but this is the best I could do.

Now where I need you guys help is the consistent clicks in the output. First guess is that this is caused by sample rate not 100% in sync with Mozzi by decimals. Meh... I think this is the limit of mixing two independant audio system... But i'd really like to use mozzi and the teensy audio shield, though.

I'd really appreciate it if anyone could help with this!

Thanks in advance guys


Code:
#include <MozziGuts.h>
#include <Oscil.h> // oscillator template
#include <tables/sin2048_int8.h> // sine table for oscillator
#include <RollingAverage.h>
#include <ControlDelay.h>



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

AudioPlayQueue           queue1;         //xy=92,334
AudioOutputI2S           i2s1;           //xy=342,313
AudioConnection          patchCord1(queue1, 0, i2s1, 0);
AudioConnection          patchCord2(queue1, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=298,544

#define FSAMPLE 16384

int16_t buffer[128];
int bufferIndex = 0;

// function prototype
bool setI2SmasterClock(uint32_t freq, uint32_t mult = 256);
float getI2SmasterClock(uint32_t mult = 256);
#define INPUT_PIN 2 // analog control input

unsigned int echo_cells_1 = 32;
unsigned int echo_cells_2 = 60;
unsigned int echo_cells_3 = 127;

#define CONTROL_RATE 64
ControlDelay <128, int> kDelay; // 2seconds

// oscils to compare bumpy to averaged control input
Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> aSin0(SIN2048_DATA);
Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> aSin1(SIN2048_DATA);
Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> aSin2(SIN2048_DATA);
Oscil <SIN2048_NUM_CELLS, AUDIO_RATE> aSin3(SIN2048_DATA);

// use: RollingAverage <number_type, how_many_to_average> myThing
RollingAverage <int, 32> kAverage; // how_many_to_average has to be power of 2
int averaged;

void setup(){
  kDelay.set(echo_cells_1);
  startMozzi();
    AudioMemory(60);
  setI2SmasterClock(FSAMPLE);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.6);
  Serial.begin(9600);
}


void updateControl(){
  int bumpy_input = mozziAnalogRead(INPUT_PIN);
  
  averaged = kAverage.next(bumpy_input);
  aSin0.setFreq(averaged);
  aSin1.setFreq(kDelay.next(averaged));
  aSin2.setFreq(kDelay.read(echo_cells_2));
  aSin3.setFreq(kDelay.read(echo_cells_3));
}


int updateAudio(){
  int16_t currentSample =  3*((int)aSin0.next()+aSin1.next()+(aSin2.next()>>1)+(aSin3.next()>>2)) << 3;

  if(bufferIndex < 128){
    buffer[bufferIndex] = currentSample;
    bufferIndex++;
  }else{
    int16_t *bufferPointer = queue1.getBuffer();
    memcpy(bufferPointer, buffer, 256);
    queue1.playBuffer();
    bufferIndex = 0;
  }
  return currentSample;
}


void loop(){
  

  audioHook();
}



/* Generate I2S master clock

   The MCLK output frequencies should be in the range from F_pll/4096 to 12.5 MHz.
   For convenience the frequency has been split into a sample frequency and 
   a sample clock multiplication factor.
   For direct control of the master clock frequency, set the multiplier to 1.
   For almost all combinations of sample clocks (8, 11.025, 12, 16, 22.05, 24,
   32, 44.1 and 48 kHz) and cpu clock frequencies it will find a ratio, that
   generates the exact frequency. For the other few, the output frequency
   lies within 0.5 Hz distance from the desired frequency.

   freq: sample frequency in Hz
   mult: sample clock multiplication factor, (default 256)
   return value: true if the frequency is within the valid range
*/

bool setI2SmasterClock(uint32_t freq, uint32_t mult)
{
        // determine fractional ratio for: p / q = freq * mult / f_pll
        // f_pll = 16 MHz * ((MCG_C6 & 0x1f) + 24) / ((MCG_C5 & 0x1f) + 1)
  uint32_t p = freq * mult * ((MCG_C5 & 0x1f) + 1);
        uint32_t q = 16000000ul * ((MCG_C6 & 0x1f) + 24);
  uint32_t fract = 0, divide = 1; // I2Sx_MDR register values
  uint32_t fract1, fract2 = 1;  // values of iterations n-1 and n-2.
  uint32_t divide1, divide2 = 0;  // idem.
        uint8_t src = 3;    // clock source selection: 3 = pll

        // Master clock should be slower than reference clock
        if (p > q)
                return false;
        else if (p == q)
                fract = divide = 1;
        else {
          // Approximate p/q by a continued fraction expansion.
            // (typically takes only 4 to 8 iterations)
            while (p) {
            // find next element of continued fraction
        uint32_t a = q / p;
        uint32_t oldp = p;
        p = q - a * p;
        q = oldp;

            // update fract/divide by Wallis method
        fract1 = fract;
        divide1 = divide;
        fract = fract1 * a + fract2;
        divide = divide1 * a + divide2;
        fract2 = fract1;
        divide2 = divide1;

        // Exit loop if fraction gets too big for the registers
        if (fract > 256 || divide > 4096) {
          // Revert back to last approximation that still fitted
          fract = fract2;
          divide = divide2;
          break;
        }
      }
            // Check if frequency too low or too high
            if (!fract)
                    return false;
        }

  // Actually set registers and enable MCLK.
        SIM_SCGC6 |= SIM_SCGC6_I2S;
  I2S0_MDR = I2S_MDR_FRACT((fract - 1)) | I2S_MDR_DIVIDE((divide - 1));
        I2S0_MCR = I2S_MCR_MICS(src) | I2S_MCR_MOE;

  // 'Connect' MCLK output to an actual output pin
        CORE_PIN11_CONFIG = PORT_PCR_MUX(6);
        //CORE_PIN28_CONFIG = PORT_PCR_MUX(4); // Pin on bottom

        return true;
}

float getI2SmasterClock(uint32_t mult)
{
        float fpll = 16.0e6f * ((MCG_C6 & 0x1f) + 24) / ((MCG_C5 & 0x1f) + 1);
        uint32_t fract = ((I2S0_MDR >> 12) & 0xff) + 1;
        uint32_t divide = (I2S0_MDR & 0xfff) + 1;
        return fpll * fract / (divide * mult);
}
 
lolololololololololololololololol

imma correct my own stupidity.

the incremental bufferIndex should go from 0 to 127, not 0 to 128 hahahahahahahahahaha

Code:
  buffer[bufferIndex] = currentSample;
  if(bufferIndex < 127){
    bufferIndex++;
  }else{
    int16_t *bufferPointer = queue1.getBuffer();
    memcpy(bufferPointer, buffer, 256);
    queue1.playBuffer();
    bufferIndex = 0;
  }


I guess this thread became a tip thread rather than a question thread. oh well I'm happy, hope this could help anyone in need!

happy coding guys :D
 
Not writing or reading beyond array boundaries is self-understanding and common sense, especially in C/C++ where the read or write address pointer just advances without asking for your initial array dimensions. Higher (but slower) programming languages check such potential issues at runtime and throw qualified errors at the expense of additional memory and cpu cycle consumption.

But it's perhaps important, though, to mentions such problems from time to time, since there are also a few beginners walking in here occasionally.
 
Yeah, understanding memory allocations or managing pointers is really awkward for me, having been comfortable all my developing life with javascript. Infact, memcpy itself was pretty baffling to me, but I guess I got past it.

Correct me if I'm wrong - I suspected that the pops and clicks happened because I was collecting 129 samples (0 to 128) but audioPlayQueue only plays 128 samples, thus losing a sample and making a consistent clicks.
 
Yeah, understanding memory allocations or managing pointers is really awkward for me, having been comfortable all my developing life with javascript. Infact, memcpy itself was pretty baffling to me, but I guess I got past it.

Correct me if I'm wrong - I suspected that the pops and clicks happened because I was collecting 129 samples (0 to 128) but audioPlayQueue only plays 128 samples, thus losing a sample and making a consistent clicks.
Sounds like a logical explanation... ;)

What I don't understand is why you try to use the Mozzi stuff instead of using explicitly the Teensy audio library which has higher quality and performance. Compared to the Teensy audio objects, Mozzi looks like the poor cousin next village...
 
Status
Not open for further replies.
Back
Top