Polyphonic Audio Sampler Memory

Status
Not open for further replies.

torin

Member
Hey all,

I'm trying to build a instrument of sorts using capacitive sensors and sample playback using teensy 3.1 and the audio board. I'm using 9 bowls of water as my triggers, and want to (ideally) have 9 voice polyphony (all 9 samples can be played back at the same time) as it is meant to be played by multiple people. I'm running into issues with memory. I'm using the provided audio playback code from the library, using raw files played off the flash memory, which as I understand it is great for latency (which is important for a musical application!). But my 9 samples go way over the flash capacity. Is there a way to keep the 9 voice polyphony and 16 bit/44.1k quality while still maintaining good latency? Is playing raw off the SD card a viable option for this?

Thanks in advance!

Here's the code (I'm a newbie be gentle):

//V1.1
// AUDIO BOARD STUFF, from library

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

// WAV files converted to code by wav2sketch
#include "AudioSampleZero.h"
#include "AudioSampleOne.h"
#include "AudioSampleTwo.h"
#include "AudioSampleThree.h"
#include "AudioSampleFour.h"
#include "AudioSampleFive.h"
#include "AudioSampleSix.h"
#include "AudioSampleSeven.h"
#include "AudioSampleEight.h"


// Create the Audio components. These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioPlayMemory sound0;
AudioPlayMemory sound1; // six memory players, so we can play
AudioPlayMemory sound2; // all six sounds simultaneously
AudioPlayMemory sound3;
AudioPlayMemory sound4;
AudioPlayMemory sound5;
AudioPlayMemory sound6;
AudioPlayMemory sound7;
AudioPlayMemory sound8;
AudioMixer4 mix1; // three 4-channel mixers are needed in
AudioMixer4 mix2; // tandem to combine 9 audio sources
AudioMixer4 mix3;
AudioOutputI2S headphones;
AudioOutputAnalog dac; // play to both I2S audio board and on-chip DAC

// Create Audio connections between the components
//
AudioConnection c1(sound0, 0, mix1, 0);
AudioConnection c2(sound1, 0, mix1, 1);
AudioConnection c3(sound2, 0, mix1, 2);
AudioConnection c4(sound3, 0, mix1, 3);
AudioConnection c5(mix1, 0, mix2, 0); // output of mix1 into 1st input on mix2
AudioConnection c6(sound4, 0, mix2, 1);
AudioConnection c7(sound5, 0, mix2, 2);
AudioConnection c8(sound6, 0, mix2, 3);
AudioConnection c9(mix2, 0, mix3, 0); // output of mix2 into 1st input on mix3
AudioConnection c10(sound7, 0, mix3, 1);
AudioConnection c11(sound8, 0, mix3, 2);
AudioConnection c12(mix3, 0, headphones, 0);
AudioConnection c13(mix3, 0, headphones, 1);
AudioConnection c14(mix3, 0, dac, 0);

// Create an object to control the audio shield.
//
AudioControlSGTL5000 audioShield;

// S E N S O R S T U F F

//MUX stuff
int touchPin = A2;
int val = 0;
int maxCount = 9;
int muxCount = 0;
int mux0 = 0;
int mux1 = 1;
int mux2 = 2;
int mux3 = 3;

//Sensor Trigger Stuff
const int numPins = 9;
int total = 0;
int average = 0;
const int numReadings= 10;
int readings[numReadings];
int readIndex = 0;
int triggerThreshold[numPins];
int calibVal = 800;

//booleans
boolean noteSend[numPins];
boolean debug = true;



void setup() {
// A U D I O S E T U P
// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
AudioMemory(10);

// turn on the output
audioShield.enable();
audioShield.volume(0.5);

// by default the Teensy 3.1 DAC uses 3.3Vp-p output
// if your 3.3V power has noise, switching to the
// internal 1.2V reference can give you a clean signal
//dac.analogReference(INTERNAL);

// reduce the gain on mixer channels, so more than 1
// sound can play simultaneously without clipping
mix1.gain(0, 0.4);
mix1.gain(1, 0.4);
mix1.gain(2, 0.4);
mix1.gain(3, 0.4);
mix2.gain(0, 0.4);
mix2.gain(1, 0.4);
mix2.gain(2, 0.4);
mix2.gain(3, 0.4);
mix3.gain(0, 0.4);
mix3.gain(1, 0.4);
mix3.gain(2, 0.4);

// S E N S O R S E T U P
if(debug) Serial.begin(9600);
//initialize all readings to 0...can we put this in a function?
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0;
}
//pin mode for MUX counter
for(int i=0;i<4;i++){
pinMode(i, OUTPUT);
}
//calibrate triggerThreshold
for(int i=0; i<numPins; i++){
total = totalFunc(i);
average = averageFunc(total);
readIndex = 0;
triggerThreshold = calibFunc(i, average, calibVal);
}
//initialize noteSend boolean
for(int i=0; i<numPins; i++){
noteSend = false;
}

}

void loop() {

// Binary Counter
digitalWrite(mux0, muxCount & 1);
digitalWrite(mux1, muxCount & 2);
digitalWrite(mux2, muxCount & 4);
digitalWrite(mux3, muxCount & 8);

readIndex = 0;
total = totalFunc(muxCount);
average = averageFunc(total);


if(average > triggerThreshold[muxCount]){
if(noteSend[muxCount] == false) {

if (muxCount == 0){
sound0.play(AudioSampleZero);
}

if (muxCount == 1){
sound1.play(AudioSampleOne);
}

if (muxCount == 2){
sound2.play(AudioSampleTwo);
}
if (muxCount == 3){
sound3.play(AudioSampleThree);
}

if (muxCount == 4){
sound4.play(AudioSampleFour);
}
if (muxCount == 5){
sound5.play(AudioSampleFive);
}
if (muxCount == 6){
sound6.play(AudioSampleSix);
}
if (muxCount == 7){
sound7.play(AudioSampleSeven);
}
if (muxCount == 8){
sound8.play(AudioSampleEight);
}

noteSend[muxCount] = true;
Serial.print(" Sensor ");
Serial.println(muxCount);
Serial.print(" Reading ");
Serial.println(average);


average = 0;
}
delay(1);
}
else {
noteSend[muxCount] = false;
average = 0;
}



muxCount++;
if(muxCount == maxCount){
muxCount = 0;
}


}

// F U N C T I O N S

//Find the total...
int totalFunc (int _i){
int sum=0;
while (readIndex < numReadings){
// read from the sensor:
readings[readIndex] = touchRead(touchPin);
// add the reading to the total:
sum = sum + readings[readIndex];
// advance to the next position in the array:
readIndex = readIndex + 1;
}
return sum;

}

//Find the average:
int averageFunc (int _total){
int averagecalc;
// calculate the average:
averagecalc = _total / numReadings;
// send it to the computer as ASCII digits
//Serial.println(averagecalc);
return averagecalc;
}

//Calibrate using average and set trigger threshold
int calibFunc (int _i, int _average, int _calibVal) {
triggerThreshold[_i] = _average + _calibVal;
/*Serial.println(triggerThreshold[_i]);
Serial.println(_average);
Serial.println(_i);
*/
return triggerThreshold[_i];

}
 
But my 9 samples go way over the flash capacity. Is there a way to keep the 9 voice polyphony and 16 bit/44.1k quality while still maintaining good latency? Is playing raw off the SD card a viable option for this?

in terms of latency, the next best thing will be to use the SPI flash stuff (ie "AudioPlaySerialRaw"); so, depending on what's "way over", that might be an option. 128 Mbit fit on the audio board (W25Q128FV). there's similar ICs with more capacity (1 Gbit or so) and/or you could use several of them, but in that case things probably won't work out of the box, software-wise, and you'd need to make your own "memory board" or use what's available (IIRC, both onehorse and frank B made available their designs).

that might just work out for 9 voices, too (i dimly recall people reporting 8 parallel voices or so with SPI flash?); i don't think 9 voices is doable with SD, at least not in the present state.
 
i read something like 15 for the FIFO Version.
did not try it myself.

@troin. please use the code-tags ("#"-button) next time. thank you.
 
Last edited:
Status
Not open for further replies.
Back
Top