AdmiralCrunch
Well-known member
I try to build a simple looper.
There should be 4 samples that a user can trigger via button and the play/stop/rec-buttons for the looper. In rec-mode, the looper should record when the user is "playing" with the samples.
I have adopted the code from Paul's Record-Example and chaged it to my needs... seems to "work somehow" (lets say I get closer to what I need ) .. but now I have a strange effect.. somehow, after I record the first overdub, the output becomes loud and distorted.. it sound like a feedback-loop, but I can't find one in my logic.. what happens, when the teensy runs out of memory during runtime?
I am udung Teensy3.2 .. the .h/.cpp-samplefiles are from Examples->Audio->SamplePlayer
There should be 4 samples that a user can trigger via button and the play/stop/rec-buttons for the looper. In rec-mode, the looper should record when the user is "playing" with the samples.
I have adopted the code from Paul's Record-Example and chaged it to my needs... seems to "work somehow" (lets say I get closer to what I need ) .. but now I have a strange effect.. somehow, after I record the first overdub, the output becomes loud and distorted.. it sound like a feedback-loop, but I can't find one in my logic.. what happens, when the teensy runs out of memory during runtime?
I am udung Teensy3.2 .. the .h/.cpp-samplefiles are from Examples->Audio->SamplePlayer
Code:
// Record sound as raw data to a SD card, and play it back.
//
// Requires the audio shield:
// http://www.pjrc.com/store/teensy3_audio.html
//
// Three pushbuttons need to be connected:
// Record Button: pin 0 to GND
// Stop Button: pin 1 to GND
// Play Button: pin 2 to GND
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>
#include "AudioSampleKick.h"
#include "AudioSampleSnare.h"
#include "AudioSampleGong.h"
#include "AudioSampleHihat.h"
// GUItool: begin automatically generated code
AudioPlayMemory playMem3; //xy=285,443
AudioPlayMemory playMem4; //xy=285,483
AudioPlayMemory playMem2; //xy=286,402
AudioPlayMemory playMem1; //xy=287,360
AudioMixer4 mixer2; //xy=424,420
AudioPlaySdRaw playRaw1; //xy=426,300
AudioPlaySdRaw playRaw2; //xy=426,336
AudioMixer4 mixer1; //xy=598,320
AudioOutputI2S i2s1; //xy=743,319
AudioAnalyzePeak peak1; //xy=837,476
AudioRecordQueue queue1; //xy=839,439
AudioConnection patchCord1(playMem3, 0, mixer2, 2);
AudioConnection patchCord2(playMem4, 0, mixer2, 3);
AudioConnection patchCord3(playMem2, 0, mixer2, 1);
AudioConnection patchCord4(playMem1, 0, mixer2, 0);
AudioConnection patchCord5(mixer2, 0, mixer1, 2);
AudioConnection patchCord6(playRaw1, 0, mixer1, 0);
AudioConnection patchCord7(playRaw2, 0, mixer1, 1);
AudioConnection patchCord8(mixer1, 0, i2s1, 0);
AudioConnection patchCord9(mixer1, 0, i2s1, 1);
AudioConnection patchCord10(mixer1, queue1);
AudioConnection patchCord11(mixer1, peak1);
AudioControlSGTL5000 sgtl5000_1; //xy=863,319
// GUItool: end automatically generated code
// For a stereo recording version, see this forum thread:
// https://forum.pjrc.com/threads/46150?p=158388&viewfull=1#post158388
// A much more advanced sound recording and data logging project:
// https://github.com/WMXZ-EU/microSoundRecorder
// https://github.com/WMXZ-EU/microSoundRecorder/wiki/Hardware-setup
// https://forum.pjrc.com/threads/52175?p=185386&viewfull=1#post185386
// Bounce objects to easily and reliably read the buttons
Bounce buttonRecord = Bounce(0, 8);
Bounce buttonStop = Bounce(1, 8); // 8 = 8 ms debounce time
Bounce buttonPlay = Bounce(2, 8);
Bounce buttonSample0 = Bounce(3, 8);
Bounce buttonSample1 = Bounce(4, 8);
// which input on the audio shield will be used?
//const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;
// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 7
#define SDCARD_SCK_PIN 14
// Use these with the Teensy 3.5 & 3.6 SD card
//#define SDCARD_CS_PIN BUILTIN_SDCARD
//#define SDCARD_MOSI_PIN 11 // not actually used
//#define SDCARD_SCK_PIN 13 // not actually used
// Use these for the SD+Wiz820 or other adaptors
//#define SDCARD_CS_PIN 4
//#define SDCARD_MOSI_PIN 11
//#define SDCARD_SCK_PIN 13
// Remember which mode we're doing
int mode = 0; // 0=stopped, 1=recording, 2=playing
bool loopExists = false;
bool loopIsPlaying = false;
String modus = "stopped";
byte actFile = 1;
byte recFile = 0;
// The file where data is recorded
File frec;
void setup() {
// Configure the pushbutton pins
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
// Audio connections require memory, and the record queue
// uses this memory to buffer incoming audio.
AudioMemory(60);
// Enable the audio shield, select input, and enable output
sgtl5000_1.enable();
//sgtl5000_1.inputSelect(myInput);
sgtl5000_1.volume(0.5);
// Initialize the SD card
SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
if (!(SD.begin(SDCARD_CS_PIN))) {
// stop here if no SD card, but print a message
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
if (SD.exists("RECORD.RAW")) {
SD.remove("RECORD.RAW");
}
if (SD.exists("RECORD2.RAW")) {
SD.remove("RECORD2.RAW");
}
}
void loop() {
// First, read the buttons
buttonRecord.update();
buttonStop.update();
buttonPlay.update();
buttonSample0.update();
buttonSample1.update();
// if playMode make looping
if(loopIsPlaying == true) {
if(actFile == 0) {
if (playRaw1.isPlaying() == false) {
startPlaying(true);
}
}
if(actFile == 1) {
if (playRaw2.isPlaying() == false) {
startPlaying(true);
}
}
}
// SAMPLE BUTTONS
if (buttonSample0.fallingEdge()) {
playMem1.play(AudioSampleSnare);
}
if (buttonSample1.fallingEdge()) {
playMem2.play(AudioSampleGong);
}
// RECORD-BTN
if (buttonRecord.fallingEdge()) {
if (mode == 1) {
//Serial.print(recFile); Serial.print(" is recording "); Serial.print(" - stopRecording"); Serial.println(recFile);
stopRecording(true);
loopIsPlaying = true;
} //else {
if (mode == 2) {
//Serial.print(actFile); Serial.print(" is playing "); Serial.print(" - stopPlaying"); Serial.println(actFile);
stopPlaying();
}
if (mode == 0) {
//Serial.print(actFile); Serial.print(" is stopped "); Serial.print(" - startRecording"); Serial.println(recFile);
startRecording();
loopIsPlaying = false;
}
}
// STOP-BTN
if (buttonStop.fallingEdge()) {
if (mode == 1) {
//Serial.print(recFile); Serial.print(" is recording "); Serial.print(" - stopRecording"); Serial.println(recFile);
stopRecording(false);
loopIsPlaying = false;
}
if (mode == 2) {
//Serial.print(actFile); Serial.print(" is playing "); Serial.print(" - stopPlaying"); Serial.println(actFile);
stopPlaying();
loopIsPlaying = false;
}
}
// PLAY-BTN
if (buttonPlay.fallingEdge()) {
if (mode == 1) {
//Serial.print(recFile); Serial.print(" is recording "); Serial.print(" - stopRecording"); Serial.println(recFile);
stopRecording(true);
loopIsPlaying = true;
}
if (mode == 0) {
//Serial.print(actFile); Serial.print(" is stopped "); Serial.print(" - startPlaying"); Serial.println(actFile);
startPlaying(true);
loopIsPlaying = true;
}
if (mode == 2) {
//Serial.print(actFile); Serial.print(" is playing "); Serial.print(" - startPlaying"); Serial.println(actFile);
startPlaying(true);
loopIsPlaying = true;
}
}
// If we're playing or recording, carry on...
if (mode == 1) {
continueRecording();
}
if (mode == 2) {
continuePlaying();
}
int volPot = analogRead(A2);
float volVal = (float)volPot / 1023.0;
mixer1.gain(0, volVal);
mixer1.gain(1, volVal);
mixer1.gain(2, volVal);
}
void startRecording() {
if(actFile == 0) {
if (SD.exists("RECORD2.RAW")) {
// The SD library writes new data to the end of the
// file, so to start a new recording, the old file
// must be deleted before new data is written.
SD.remove("RECORD2.RAW");
}
//Serial.println("startRecording RECORD2");
frec = SD.open("RECORD2.RAW", FILE_WRITE);
if (frec) {
queue1.begin();
mode = 1;
}
}
if(actFile == 1) {
if (SD.exists("RECORD.RAW")) {
// The SD library writes new data to the end of the
// file, so to start a new recording, the old file
// must be deleted before new data is written.
SD.remove("RECORD.RAW");
}
//Serial.println("startRecording RECORD");
frec = SD.open("RECORD.RAW", FILE_WRITE);
if (frec) {
queue1.begin();
mode = 1;
}
}
}
void continueRecording() {
if(actFile == 0) {
if (playRaw1.isPlaying() == false) {
//Serial.println(".....---------------->> loop raw1");
startPlaying(false);
}
}
if(actFile == 1) {
if (playRaw2.isPlaying() == false) {
//Serial.println("....---------------->> loop raw2");
startPlaying(false);
}
}
if (queue1.available() >= 2) {
byte buffer[512];
// Fetch 2 blocks from the audio library and copy
// into a 512 byte buffer. The Arduino SD library
// is most efficient when full 512 byte sector size
// writes are used.
memcpy(buffer, queue1.readBuffer(), 256);
queue1.freeBuffer();
memcpy(buffer+256, queue1.readBuffer(), 256);
queue1.freeBuffer();
// write all 512 bytes to the SD card
elapsedMicros usec = 0;
frec.write(buffer, 512);
// Uncomment these lines to see how long SD writes
// are taking. A pair of audio blocks arrives every
// 5802 microseconds, so hopefully most of the writes
// take well under 5802 us. Some will take more, as
// the SD library also must write to the FAT tables
// and the SD card controller manages media erase and
// wear leveling. The queue1 object can buffer
// approximately 301700 us of audio, to allow time
// for occasional high SD card latency, as long as
// the average write time is under 5802 us.
//Serial.print("SD write, us=");
//Serial.println(usec);
}
}
void stopRecording(bool goOn) {
//Serial.print("stopRecording actFile: "); Serial.println(actFile);
queue1.end();
if (mode == 1) {
while (queue1.available() > 0) {
frec.write((byte*)queue1.readBuffer(), 256);
queue1.freeBuffer();
}
frec.close();
}
if(actFile == 0) {
actFile = 1;
recFile = 0;
} else if(actFile == 1) {
actFile = 0;
recFile = 1;
}
//Serial.print("new actFile: "); Serial.println(actFile);
mode = 0;
/*
if(goOn == true) {
startPlaying();
}
*/
}
void startPlaying(bool setMode) {
if(actFile == 0) {
playRaw1.play("RECORD.RAW");
if(setMode == true) { mode = 2; }
}
if(actFile == 1) {
playRaw2.play("RECORD2.RAW");
if(setMode == true) { mode = 2; }
}
}
void continuePlaying() {
if(actFile == 0) {
if (!playRaw1.isPlaying()) {
playRaw1.stop();
mode = 0;
}
}
if(actFile == 1) {
if (!playRaw2.isPlaying()) {
playRaw2.stop();
mode = 0;
}
}
}
void stopPlaying() {
//Serial.println("stopPlaying");
if(actFile == 0) {
if (mode == 2) playRaw1.stop();
loopIsPlaying = false;
mode = 0;
}
if(actFile == 1) {
if (mode == 2) playRaw2.stop();
loopIsPlaying = false;
mode = 0;
}
}
void adjustMicLevel() {
// TODO: read the peak1 object and adjust sgtl5000_1.micGain()
// if anyone gets this working, please submit a github pull request :-)
}