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
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
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
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);
}