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