die hard.
+ i tried so eagerly to achieve somewhat stability. yet, to be frustrated. + nothing gained. wanna go back. + but just wanna leave some trace.
This commit is contained in:
parent
dec2431084
commit
c839359313
7 changed files with 336 additions and 153 deletions
|
|
@ -6,23 +6,26 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
// COSMO40 @ Incheon w/ Factory2
|
|
||||||
// RTA @ Seoul w/ Post Territory Ujeongguk
|
// RTA @ Seoul w/ Post Territory Ujeongguk
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
// 2019 12 11
|
// 2020 1 2
|
||||||
//
|
//
|
||||||
// (part-2) teensy35 : 'client:osc' (osc over slip --> mesh post)
|
// (part-2) teensy35 : 'client:osc' (osc over slip --> mesh post)
|
||||||
//
|
//
|
||||||
// especially, MIDI-like.
|
// collect following MIDI-like. OSC msg.
|
||||||
// collect following OSC msg.
|
// + extra. direction msg. ('/timing/offset', '/timing/loop', '/timing/interval')
|
||||||
//
|
//
|
||||||
// "/note/onoff"
|
// "/note/onoff"
|
||||||
// "/note/velocity"
|
// "/note/velocity"
|
||||||
// "/note/key"
|
// "/note/pitch"
|
||||||
|
// +
|
||||||
|
// "/timing/offset"
|
||||||
|
// "/timing/loop"
|
||||||
|
// "/timing/interval"
|
||||||
//
|
//
|
||||||
// and build a MIDI-like letter post.
|
// and build one letter post.
|
||||||
// and give it to the postman
|
// and give it to the postman
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
@ -33,13 +36,22 @@
|
||||||
// : [123456789012345678901234567890]
|
// : [123456789012345678901234567890]
|
||||||
// 'MIDI' letter frame
|
// 'MIDI' letter frame
|
||||||
// : [123456789012345678901234567890]
|
// : [123456789012345678901234567890]
|
||||||
// : [KKKVVVG.......................]
|
// : [KKKVVVGOOOOOLIIIII............]
|
||||||
// : KKK - Key
|
// : KKK - Key
|
||||||
// .substring(1, 4);
|
// .substring(1, 4);
|
||||||
// : VVV - Velocity (volume/amp.)
|
// : VVV - Velocity (volume/amp.)
|
||||||
// .substring(4, 7);
|
// .substring(4, 7);
|
||||||
// : G - Gate (note on/off)
|
// : G - Gate (note on/off)
|
||||||
// .substring(7, 8);
|
// .substring(7, 8);
|
||||||
|
// : O - timing offset (plz start after this milli-sec)
|
||||||
|
// .substring(8, 13);
|
||||||
|
// : L - looping mode (plz do no/auto/manual-looping)
|
||||||
|
// .substring(13, 14);
|
||||||
|
// L == 1 -> no-looping. play once.
|
||||||
|
// L == 2 -> auto-restart when the playback ends.
|
||||||
|
// L == 3 -> restart after 'timing interval' milli-sec.
|
||||||
|
// : I - looping interval (only valid for 'manual-looping')
|
||||||
|
// .substring(14, 19); ==> check 'special cases' first.
|
||||||
//
|
//
|
||||||
|
|
||||||
//arduino
|
//arduino
|
||||||
|
|
@ -91,6 +103,37 @@ void setup() {
|
||||||
SLIPSerial.begin(57600);
|
SLIPSerial.begin(57600);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
static int t_offset = 0;
|
||||||
|
static int t_loop = 0;
|
||||||
|
static int t_interval = 0;
|
||||||
|
void timingnote(OSCMessage& msg, int offset) {
|
||||||
|
// matches will happen in the order. that the bundle is packed.
|
||||||
|
// (1) --> /offset
|
||||||
|
if (msg.fullMatch("/offset", offset)) {
|
||||||
|
//
|
||||||
|
t_offset = 0;
|
||||||
|
t_loop = 0;
|
||||||
|
t_interval = 0;
|
||||||
|
//
|
||||||
|
t_offset = msg.getInt(0);
|
||||||
|
if (t_offset < 0) t_offset = 0;
|
||||||
|
if (t_offset > 99999) t_offset = 99999;
|
||||||
|
}
|
||||||
|
// (2) --> /loop
|
||||||
|
if (msg.fullMatch("/loop", offset)) {
|
||||||
|
t_loop = msg.getInt(0);
|
||||||
|
if (t_loop < 1) t_loop = 1;
|
||||||
|
if (t_loop > 9) t_loop = 9;
|
||||||
|
}
|
||||||
|
// (3) --> /interval
|
||||||
|
if (msg.fullMatch("/interval", offset)) {
|
||||||
|
//
|
||||||
|
t_interval = msg.getInt(0);
|
||||||
|
if (t_interval < 0) t_interval = 0;
|
||||||
|
if (t_interval > 99999) t_interval = 99999;
|
||||||
|
}
|
||||||
|
}
|
||||||
//
|
//
|
||||||
void midinote(OSCMessage& msg, int offset) {
|
void midinote(OSCMessage& msg, int offset) {
|
||||||
// matches will happen in the order. that the bundle is packed.
|
// matches will happen in the order. that the bundle is packed.
|
||||||
|
|
@ -120,7 +163,7 @@ void midinote(OSCMessage& msg, int offset) {
|
||||||
if (pitch > 127) pitch = 127;
|
if (pitch > 127) pitch = 127;
|
||||||
//
|
//
|
||||||
// while (new_letter != false) {}; // <-- sort of semaphore.. but it doesn't work yet.. buggy.
|
// while (new_letter != false) {}; // <-- sort of semaphore.. but it doesn't work yet.. buggy.
|
||||||
sprintf(letter_outro, "[%03d%03d%01d.......................]", pitch, velocity, onoff);
|
sprintf(letter_outro, "[%03d%03d%01d%05d%01d%05d............]", pitch, velocity, onoff, t_offset, t_loop, t_interval);
|
||||||
new_letter = true;
|
new_letter = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -140,6 +183,7 @@ void loop() {
|
||||||
}
|
}
|
||||||
if(!bundleIN.hasError()) {
|
if(!bundleIN.hasError()) {
|
||||||
bundleIN.route("/note", midinote);
|
bundleIN.route("/note", midinote);
|
||||||
|
bundleIN.route("/timing", timingnote);
|
||||||
}
|
}
|
||||||
// else {
|
// else {
|
||||||
// str = "error! : " + String(bundleIN.getError());
|
// str = "error! : " + String(bundleIN.getError());
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public:
|
||||||
uint32_t lengthMillis(void);
|
uint32_t lengthMillis(void);
|
||||||
virtual void update(void);
|
virtual void update(void);
|
||||||
private:
|
private:
|
||||||
File wavfile;
|
FsFile wavfile;
|
||||||
bool consume(uint32_t size);
|
bool consume(uint32_t size);
|
||||||
bool parse_format(void);
|
bool parse_format(void);
|
||||||
uint32_t header[10]; // temporary storage of wav header data
|
uint32_t header[10]; // temporary storage of wav header data
|
||||||
|
|
|
||||||
1
sampler/lib/SdFat-beta
Submodule
1
sampler/lib/SdFat-beta
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 172c6856246738f538a1e48746c32318408dac5c
|
||||||
|
|
@ -15,7 +15,7 @@ default_envs = teensy35
|
||||||
lib_ignore = Audio, SD
|
lib_ignore = Audio, SD
|
||||||
lib_deps =
|
lib_deps =
|
||||||
721 ; TaskScheduler
|
721 ; TaskScheduler
|
||||||
322 ; SdFat
|
; 322 ; SdFat
|
||||||
401 ; Adafruit SleepyDog Library
|
401 ; Adafruit SleepyDog Library
|
||||||
|
|
||||||
[env:teensy35]
|
[env:teensy35]
|
||||||
|
|
|
||||||
|
|
@ -6,22 +6,13 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
// COSMO40 @ Incheon w/ Factory2
|
|
||||||
// RTA @ Seoul w/ Post Territory Ujeongguk
|
// RTA @ Seoul w/ Post Territory Ujeongguk
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// 2019 12 11
|
|
||||||
//
|
//
|
||||||
// (part-3) teensy35 : 'client:sampler' (mesh post --> play sounds)
|
// (part-3) teensy35 : 'client:sampler' (mesh post --> play sounds)
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// 2019 12 29
|
|
||||||
//
|
|
||||||
// multiple sound playback -> 4 voices -- TESTING
|
|
||||||
//
|
|
||||||
|
|
||||||
//----------<configuration>----------
|
//----------<configuration>----------
|
||||||
//
|
//
|
||||||
// 'ANALOG_REF_EXTERNAL_3P3V'
|
// 'ANALOG_REF_EXTERNAL_3P3V'
|
||||||
|
|
@ -31,21 +22,57 @@
|
||||||
// teensy35 was okay since they are stronger (5V compatible I/O)
|
// teensy35 was okay since they are stronger (5V compatible I/O)
|
||||||
//
|
//
|
||||||
// #define ANALOG_REF_EXTERNAL_3P3V
|
// #define ANALOG_REF_EXTERNAL_3P3V
|
||||||
|
//
|
||||||
|
// 'LED_INDICATOR'
|
||||||
|
// --> this will enable red LED on/off according to the file playback status.
|
||||||
|
//
|
||||||
|
#define LED_INDICATOR
|
||||||
|
//
|
||||||
|
// 'USE_SD'
|
||||||
|
// --> a original sd card driver..
|
||||||
|
//
|
||||||
|
// #define USE_SD
|
||||||
|
//
|
||||||
|
// 'USE_SDFATSDIO'
|
||||||
|
// --> a faster sd card driver..
|
||||||
|
//
|
||||||
|
// #define USE_SDFATSDIO
|
||||||
|
//
|
||||||
|
// 'USE_SDFATBETA'
|
||||||
|
// --> a faster sd card driver..
|
||||||
|
//
|
||||||
|
#define USE_SDFATBETA
|
||||||
//----------</configuration>----------
|
//----------</configuration>----------
|
||||||
|
|
||||||
//watchdog
|
//watchdog
|
||||||
#include <Adafruit_SleepyDog.h>
|
#include <Adafruit_SleepyDog.h>
|
||||||
|
|
||||||
//teensy audio
|
//teensy audio
|
||||||
|
#if defined(USE_SD)
|
||||||
|
#include <Audio.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <SD.h>
|
||||||
|
#include <SerialFlash.h>
|
||||||
|
#define xFile File
|
||||||
|
//
|
||||||
|
#elif defined(USE_SDFATSDIO)
|
||||||
#include <Audio.h>
|
#include <Audio.h>
|
||||||
#include <SdFat.h>
|
#include <SdFat.h>
|
||||||
SdFatSdioEX SD;
|
SdFatSdio SD;
|
||||||
|
// SdFatSdioEX SD;
|
||||||
#include <SerialFlash.h>
|
#include <SerialFlash.h>
|
||||||
|
#define xFile File
|
||||||
//teensy 3.5 with SD card
|
//
|
||||||
#define SDCARD_CS_PIN BUILTIN_SDCARD
|
#elif defined(USE_SDFATBETA)
|
||||||
#define SDCARD_MOSI_PIN 11 // not actually used
|
#include <Audio.h>
|
||||||
#define SDCARD_SCK_PIN 13 // not actually used
|
#include <SdFat.h>
|
||||||
|
#include <SerialFlash.h>
|
||||||
|
SdFs SD;
|
||||||
|
#define xFile FsFile
|
||||||
|
// SdExFat SD;
|
||||||
|
// #define xFile ExFile
|
||||||
|
#endif
|
||||||
|
|
||||||
// GUItool: begin automatically generated code
|
// GUItool: begin automatically generated code
|
||||||
AudioPlaySdWav playSdWav1; //xy=183,90
|
AudioPlaySdWav playSdWav1; //xy=183,90
|
||||||
|
|
@ -83,8 +110,54 @@ AudioConnection patchCord17(mixer2, 0, dacs1, 1);
|
||||||
AudioConnection patchCord18(mixer1, 0, dacs1, 0);
|
AudioConnection patchCord18(mixer1, 0, dacs1, 0);
|
||||||
// GUItool: end automatically generated code
|
// GUItool: end automatically generated code
|
||||||
|
|
||||||
|
//threads
|
||||||
|
#include <TeensyThreads.h>
|
||||||
|
//following class is from --> https://github.com/ftrias/TeensyThreads/blob/master/examples/Runnable/
|
||||||
|
class Runnable {
|
||||||
|
private:
|
||||||
|
protected:
|
||||||
|
virtual void runTarget(void *arg) = 0;
|
||||||
|
public:
|
||||||
|
virtual ~Runnable(){
|
||||||
|
}
|
||||||
|
|
||||||
|
static void runThread(void *arg) {
|
||||||
|
Runnable *_runnable = static_cast<Runnable*> (arg);
|
||||||
|
_runnable->runTarget(arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//NOTE: using TeensyThreads == nightmare of 'volatile' problems.
|
||||||
|
// ==> wrap all the variables that is used to comm. between threads into a 'global' & 'volatile' object..
|
||||||
|
class vVoice {
|
||||||
|
public:
|
||||||
|
int note_now;
|
||||||
|
int velocity_now;
|
||||||
|
int start_offset;
|
||||||
|
bool note_on_req;
|
||||||
|
bool note_off_req;
|
||||||
|
bool is_playing;
|
||||||
|
|
||||||
|
vVoice() {
|
||||||
|
note_now = 0;
|
||||||
|
velocity_now = 0;
|
||||||
|
start_offset = 0;
|
||||||
|
note_on_req = false;
|
||||||
|
note_off_req = false;
|
||||||
|
is_playing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vVoice(const vVoice &vv) {
|
||||||
|
note_now = vv.note_now;
|
||||||
|
velocity_now = vv.velocity_now;
|
||||||
|
start_offset = vv.start_offset;
|
||||||
|
note_on_req = vv.note_on_req;
|
||||||
|
note_off_req = vv.note_off_req;
|
||||||
|
is_playing = vv.is_playing;
|
||||||
|
}
|
||||||
|
};
|
||||||
//
|
//
|
||||||
class Voice {
|
class Voice : public Runnable {
|
||||||
//private
|
//private
|
||||||
|
|
||||||
//teensy audio
|
//teensy audio
|
||||||
|
|
@ -92,83 +165,105 @@ class Voice {
|
||||||
AudioAmplifier& ampL;
|
AudioAmplifier& ampL;
|
||||||
AudioAmplifier& ampR;
|
AudioAmplifier& ampR;
|
||||||
|
|
||||||
|
//teensythreads
|
||||||
|
std::thread* thrd;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//teensythreads
|
||||||
|
void runTarget(void *arg) {
|
||||||
|
|
||||||
// a filename buffer
|
// a filename buffer
|
||||||
char filename[13];
|
char filename[13] = "NN.WAV";
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
//
|
//
|
||||||
int note_now;
|
while(1) {
|
||||||
int velocity_now;
|
|
||||||
|
|
||||||
//
|
|
||||||
Voice(AudioPlaySdWav& player_, AudioAmplifier& ampL_, AudioAmplifier& ampR_)
|
|
||||||
: player(player_)
|
|
||||||
, ampL(ampL_)
|
|
||||||
, ampR(ampR_)
|
|
||||||
{
|
{
|
||||||
//initializations
|
Threads::Scope m(mx);
|
||||||
note_now = 0;
|
|
||||||
velocity_now = 0;
|
|
||||||
strcpy(filename, "NN.WAV");
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
// 'note off' request
|
||||||
void noteOn(int note) {
|
if (vv.note_off_req == true) {
|
||||||
// present my 'note' -> 'occupied'.
|
vv.note_off_req = false;
|
||||||
note_now = note;
|
player.stop();
|
||||||
|
vv.note_now = 0; // declare that [i'm free.]
|
||||||
|
vv.is_playing = false;
|
||||||
|
}
|
||||||
|
// 'note on' request
|
||||||
|
else if (vv.note_on_req == true) {
|
||||||
|
vv.note_on_req = false;
|
||||||
// set filename to play...
|
// set filename to play...
|
||||||
int nn = (note % 100); // 0~99
|
int nn = (vv.note_now % 100); // 0~99
|
||||||
filename[0] = '0' + (nn / 10); // [N]N.WAV
|
filename[0] = '0' + (nn / 10); // [N]N.WAV
|
||||||
filename[1] = '0' + (nn % 10); // N[N].WAV
|
filename[1] = '0' + (nn % 10); // N[N].WAV
|
||||||
// the filename to play is...
|
// the filename to play is...
|
||||||
Serial.println(filename);
|
Serial.println(filename);
|
||||||
// go! (re-triggering)
|
// go! (re-triggering)
|
||||||
// if (player.isPlaying()) player.stop();
|
// threads.delay(vv.start_offset); // <-- so.. this also means 'yield' so.. sth. unexpected happening here? so, we cannot do this? -> SAD.
|
||||||
player.play(filename);
|
AudioNoInterrupts(); // maybe .. 'AudioNoInterrupts' helps for the stability?
|
||||||
Serial.println("1");
|
// apply gains
|
||||||
|
int val = vv.velocity_now;
|
||||||
|
if (val < 0) val = 0;
|
||||||
|
float gg = (float)val / 127; // allowing +gain for values over 127.
|
||||||
|
ampL.gain(gg);
|
||||||
|
ampR.gain(gg);
|
||||||
|
bool res = player.play(filename);
|
||||||
|
Serial.print("player.play -> "); Serial.println(res); // maybe.. meaningless? play() is kinda blocking call... -> SAD
|
||||||
|
AudioInterrupts();
|
||||||
// --> we just believe that this 'file' is existing & available. NO additional checking.
|
// --> we just believe that this 'file' is existing & available. NO additional checking.
|
||||||
delay(10);
|
// threads.delay(10);
|
||||||
Serial.println("2");
|
|
||||||
// --> let's wait a bit before exit, to give more room to work for background workers(==filesystem|audio-interrupts)
|
// --> let's wait a bit before exit, to give more room to work for background workers(==filesystem|audio-interrupts)
|
||||||
// --> if we get too fast 'player.play' twice, then the system might get broken/stalled. ?
|
// --> if we get too fast 'player.play' twice, then the system might get broken/stalled. ?
|
||||||
|
vv.is_playing = true;
|
||||||
}
|
}
|
||||||
//
|
|
||||||
void noteOff() {
|
|
||||||
player.stop();
|
|
||||||
delay(10); // wait to close file?
|
|
||||||
// present my 'note' -> 'free'.
|
|
||||||
note_now = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
void setVelocity(int val) {
|
threads.yield();
|
||||||
if (val < 0) val = 0;
|
// threads.delay(5);
|
||||||
float vv = (float)val / 127; // allowing +gain for values over 127.
|
|
||||||
ampL.gain(vv);
|
|
||||||
ampR.gain(vv);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//mutex
|
||||||
|
Threads::Mutex& mx; // BETTER hav only 1 mutex. for all? so, only 1 change can happen at a time.
|
||||||
|
|
||||||
|
//variables for Voice
|
||||||
|
volatile vVoice& vv;
|
||||||
|
|
||||||
//
|
//
|
||||||
bool isPlaying() {
|
Voice(AudioPlaySdWav& player_, AudioAmplifier& ampL_, AudioAmplifier& ampR_, Threads::Mutex& mx_, volatile vVoice& vv_)
|
||||||
return player.isPlaying();
|
: player(player_), ampL(ampL_), ampR(ampR_), mx(mx_), vv(vv_) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//threading
|
||||||
|
void start() {
|
||||||
|
thrd = new std::thread(&Runnable::runThread, this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// voice banks
|
// voice banks
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
static Voice __voice_1(playSdWav1, amp1, amp2);
|
//NOTE: stronger locking? there's only 1 locking mutex.
|
||||||
static Voice __voice_2(playSdWav2, amp3, amp4);
|
Threads::Mutex __mx;
|
||||||
static Voice __voice_3(playSdWav3, amp5, amp6);
|
//NOTE: using TeensyThreads == nightmare of 'volatile' problems.
|
||||||
static Voice __voice_4(playSdWav4, amp7, amp8);
|
// ==> wrap all the variables that is used to comm. between threads into a 'global' & 'volatile' object..
|
||||||
|
static volatile vVoice __vv1;
|
||||||
|
static volatile vVoice __vv2;
|
||||||
|
// static volatile vVoice __vv3;
|
||||||
|
// static volatile vVoice __vv4;
|
||||||
|
// ==> and link global volatile objects to the actual objects like :
|
||||||
|
static Voice __voice_1(playSdWav1, amp1, amp2, __mx, __vv1);
|
||||||
|
static Voice __voice_2(playSdWav2, amp3, amp4, __mx, __vv2);
|
||||||
|
// static Voice __voice_3(playSdWav3, amp5, amp6, __mx, __vv3);
|
||||||
|
// static Voice __voice_4(playSdWav4, amp7, amp8, __mx, __vv4);
|
||||||
static std::vector<Voice> poly_bank;
|
static std::vector<Voice> poly_bank;
|
||||||
static std::deque< std::pair<int, int> > poly_queue;
|
static std::deque< std::pair<int, int> > poly_queue;
|
||||||
|
|
||||||
//task
|
|
||||||
#include <TaskScheduler.h>
|
|
||||||
Scheduler runner;
|
|
||||||
// polyphonics
|
// polyphonics
|
||||||
static int note_sched = 0;
|
static int note_sched = 0;
|
||||||
static int velocity_sched = 0;
|
static int velocity_sched = 0;
|
||||||
|
static int offset_sched = 0;
|
||||||
void scheduleNoteOn()
|
void scheduleNoteOn()
|
||||||
{
|
{
|
||||||
//filename buffer - 8.3 naming convension! 8+1+3+1 = 13
|
//filename buffer - 8.3 naming convension! 8+1+3+1 = 13
|
||||||
|
|
@ -197,9 +292,13 @@ void scheduleNoteOn()
|
||||||
// (1) re-trigger (stop-and-restart)
|
// (1) re-trigger (stop-and-restart)
|
||||||
is_already = true;
|
is_already = true;
|
||||||
Voice& v = poly_bank[poly_queue[idx].first];
|
Voice& v = poly_bank[poly_queue[idx].first];
|
||||||
v.noteOff();
|
{
|
||||||
v.noteOn(note);
|
Threads::Scope m(v.mx);
|
||||||
v.setVelocity(velocity_sched);
|
v.vv.note_now = note;
|
||||||
|
v.vv.velocity_now = velocity_sched;
|
||||||
|
v.vv.start_offset = offset_sched;
|
||||||
|
v.vv.note_on_req = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
// (2) do nothing (just let it play till end)
|
// (2) do nothing (just let it play till end)
|
||||||
// is_already = true;
|
// is_already = true;
|
||||||
|
|
@ -216,30 +315,40 @@ void scheduleNoteOn()
|
||||||
//fine, is there idle voice?
|
//fine, is there idle voice?
|
||||||
bool is_found_idle = false;
|
bool is_found_idle = false;
|
||||||
for (uint32_t idx = 0; idx < poly_bank.size(); idx++) {
|
for (uint32_t idx = 0; idx < poly_bank.size(); idx++) {
|
||||||
if (poly_bank[idx].note_now == 0) {
|
Voice& v = poly_bank[idx];
|
||||||
|
{
|
||||||
|
Threads::Scope m(v.mx);
|
||||||
|
if (v.vv.note_now == 0) {
|
||||||
//cool, got one.
|
//cool, got one.
|
||||||
is_found_idle = true;
|
is_found_idle = true;
|
||||||
//play start-up
|
//play start-up
|
||||||
Voice& v = poly_bank[idx];
|
v.vv.note_now = note;
|
||||||
v.noteOn(note);
|
v.vv.velocity_now = velocity_sched;
|
||||||
v.setVelocity(velocity_sched);
|
v.vv.start_offset = offset_sched;
|
||||||
|
v.vv.note_on_req = true;
|
||||||
//leave a record : (# of voice bank, playing note #)
|
//leave a record : (# of voice bank, playing note #)
|
||||||
poly_queue.push_back(std::pair<int, int>(idx, note));
|
poly_queue.push_back(std::pair<int, int>(idx, note));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//oh, no idle one!
|
//oh, no idle one!
|
||||||
if (is_found_idle == false) {
|
if (is_found_idle == false) {
|
||||||
//then, who's the oldest?
|
//then, who's the oldest?
|
||||||
int oldest = poly_queue.front().first;
|
int oldest = poly_queue.front().first;
|
||||||
poly_bank[oldest].noteOff();
|
//poly_bank[oldest].noteOff();
|
||||||
poly_queue.pop_front();
|
poly_queue.pop_front();
|
||||||
//
|
//
|
||||||
int newentry = oldest;
|
int newentry = oldest;
|
||||||
//
|
//
|
||||||
Voice& v = poly_bank[newentry];
|
Voice& v = poly_bank[newentry];
|
||||||
v.noteOn(note);
|
{
|
||||||
v.setVelocity(velocity_sched);
|
Threads::Scope m(v.mx);
|
||||||
|
v.vv.note_now = note;
|
||||||
|
v.vv.velocity_now = velocity_sched;
|
||||||
|
v.vv.start_offset = offset_sched;
|
||||||
|
v.vv.note_on_req = true;
|
||||||
|
}
|
||||||
//leave a record : (# of voice bank, playing note #)
|
//leave a record : (# of voice bank, playing note #)
|
||||||
poly_queue.push_back(std::pair<int, int>(newentry, note));
|
poly_queue.push_back(std::pair<int, int>(newentry, note));
|
||||||
}
|
}
|
||||||
|
|
@ -259,8 +368,6 @@ void scheduleNoteOn()
|
||||||
Serial.println();
|
Serial.println();
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
Task scheduleNoteOn_task(0, TASK_ONCE, scheduleNoteOn);
|
|
||||||
//
|
|
||||||
void scheduleNoteOff() {
|
void scheduleNoteOff() {
|
||||||
for (auto it = poly_queue.begin(); it != poly_queue.end(); ++it) {
|
for (auto it = poly_queue.begin(); it != poly_queue.end(); ++it) {
|
||||||
//is this meaningful, btw?
|
//is this meaningful, btw?
|
||||||
|
|
@ -268,7 +375,11 @@ void scheduleNoteOff() {
|
||||||
//okay. we've got that.
|
//okay. we've got that.
|
||||||
Serial.println("okay. we've got that.");
|
Serial.println("okay. we've got that.");
|
||||||
//a record : (# of voice bank, playing note #)
|
//a record : (# of voice bank, playing note #)
|
||||||
poly_bank[(*it).first].noteOff(); // stop the bank
|
Voice& v = poly_bank[(*it).first];
|
||||||
|
{
|
||||||
|
Threads::Scope m(v.mx);
|
||||||
|
v.vv.note_off_req = true; // stop the bank
|
||||||
|
}
|
||||||
poly_queue.erase(it); // remove the record
|
poly_queue.erase(it); // remove the record
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -285,15 +396,17 @@ void scheduleNoteOff() {
|
||||||
}
|
}
|
||||||
Serial.println();
|
Serial.println();
|
||||||
}
|
}
|
||||||
//
|
|
||||||
Task scheduleNoteOff_task(0, TASK_ONCE, scheduleNoteOff);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
void playcheck() {
|
void playcheck() {
|
||||||
|
while(1) {
|
||||||
|
#if defined(LED_INDICATOR)
|
||||||
bool is_nosound = true;
|
bool is_nosound = true;
|
||||||
for (uint32_t idx = 0; idx < poly_bank.size(); idx++) {
|
for (uint32_t idx = 0; idx < poly_bank.size(); idx++) {
|
||||||
if (poly_bank[idx].isPlaying()) {
|
Voice& v = poly_bank[idx];
|
||||||
is_nosound = false;
|
{
|
||||||
|
Threads::Scope m(v.mx);
|
||||||
|
if (v.vv.is_playing) is_nosound = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_nosound) {
|
if (is_nosound) {
|
||||||
|
|
@ -303,15 +416,18 @@ void playcheck() {
|
||||||
//mark the indicator : HIGH: ON
|
//mark the indicator : HIGH: ON
|
||||||
digitalWrite(13, HIGH);
|
digitalWrite(13, HIGH);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// //
|
// //
|
||||||
// Serial.print("AM_max:");
|
// Serial.print("AM_max:");
|
||||||
// Serial.println(AudioMemoryUsageMax());
|
// Serial.println(AudioMemoryUsageMax());
|
||||||
|
|
||||||
//watchdog
|
//watchdog
|
||||||
Watchdog.reset();
|
Watchdog.reset();
|
||||||
|
|
||||||
|
threads.delay(100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//
|
|
||||||
Task playcheck_task(100, TASK_FOREVER, playcheck, &runner, true);
|
|
||||||
|
|
||||||
//i2c
|
//i2c
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
|
@ -340,71 +456,91 @@ void receiveEvent(int numBytes) {
|
||||||
|
|
||||||
// 'MIDI' letter frame
|
// 'MIDI' letter frame
|
||||||
// : [123456789012345678901234567890]
|
// : [123456789012345678901234567890]
|
||||||
// : [KKKVVVG.......................]
|
// : [KKKVVVGOOOOOLIIIII............]
|
||||||
// : KKK - Key
|
// : KKK - Key
|
||||||
// .substring(1, 4);
|
// .substring(1, 4);
|
||||||
// : VVV - Velocity (volume/amp.)
|
// : VVV - Velocity (volume/amp.)
|
||||||
// .substring(4, 7);
|
// .substring(4, 7);
|
||||||
// : G - Gate (note on/off)
|
// : G - Gate (note on/off)
|
||||||
// .substring(7, 8);
|
// .substring(7, 8);
|
||||||
|
// : O - timing offset (plz start after this milli-sec)
|
||||||
|
// .substring(8, 13);
|
||||||
|
// : L - looping mode (plz do no/auto/manual-looping)
|
||||||
|
// .substring(13, 14);
|
||||||
|
// L == 1 -> no-looping. play once.
|
||||||
|
// L == 2 -> auto-restart when the playback ends.
|
||||||
|
// L == 3 -> restart after 'timing interval' milli-sec.
|
||||||
|
// : I - looping interval (only valid for 'manual-looping')
|
||||||
|
// .substring(14, 19); ==> check 'special cases' first.
|
||||||
|
|
||||||
String str_key = msg.substring(1, 4);
|
String str_key = msg.substring(1, 4);
|
||||||
String str_velocity = msg.substring(4, 7);
|
String str_velocity = msg.substring(4, 7);
|
||||||
String str_gate = msg.substring(7, 8);
|
String str_gate = msg.substring(7, 8);
|
||||||
// Serial.println(str_key);
|
String str_offset = msg.substring(8, 13);
|
||||||
// Serial.println(str_velocity);
|
String str_loop = msg.substring(13, 14);
|
||||||
// Serial.println(str_gate);
|
String str_interval = msg.substring(14, 19);
|
||||||
|
|
||||||
//
|
//
|
||||||
int key = str_key.toInt();
|
int key = str_key.toInt();
|
||||||
int velocity = str_velocity.toInt(); // 0 ~ 127
|
int velocity = str_velocity.toInt(); // 0 ~ 127
|
||||||
int gate = str_gate.toInt();
|
int gate = str_gate.toInt();
|
||||||
|
int offset = str_offset.toInt();
|
||||||
|
int loop = str_loop.toInt();
|
||||||
|
int interval = str_interval.toInt();
|
||||||
|
|
||||||
//
|
//
|
||||||
if (gate == 0) {
|
if (gate == 0) {
|
||||||
note_sched = key;
|
note_sched = key;
|
||||||
scheduleNoteOff_task.restart();
|
scheduleNoteOff();
|
||||||
} else {
|
} else {
|
||||||
note_sched = key;
|
note_sched = key;
|
||||||
velocity_sched = velocity;
|
velocity_sched = velocity;
|
||||||
scheduleNoteOn_task.restart();
|
offset_sched = offset;
|
||||||
|
scheduleNoteOn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SD TEST
|
// SD TEST
|
||||||
void printDirectory(File dir, int numTabs) {
|
void printDirectory(xFile dir, int numTabs) {
|
||||||
|
char filename[256] = "";
|
||||||
while(true) {
|
while(true) {
|
||||||
|
|
||||||
File entry = dir.openNextFile();
|
xFile entry = dir.openNextFile();
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
// no more files
|
|
||||||
Serial.println("**nomorefiles**");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (uint8_t i=0; i<numTabs; i++) {
|
for (uint8_t i=0; i<numTabs; i++) {
|
||||||
Serial.print('\t');
|
Serial.print('\t');
|
||||||
}
|
}
|
||||||
|
#if defined(USE_SD)
|
||||||
Serial.print(entry.name());
|
Serial.print(entry.name());
|
||||||
|
#elif defined(USE_SDFATSDIO)
|
||||||
|
entry.getName(filename, 256);
|
||||||
|
Serial.print(filename);
|
||||||
|
#elif defined(USE_SDFATBETA)
|
||||||
|
entry.getName(filename, 256);
|
||||||
|
Serial.print(filename);
|
||||||
|
#endif
|
||||||
if (entry.isDirectory()) {
|
if (entry.isDirectory()) {
|
||||||
Serial.println("/");
|
Serial.println("/");
|
||||||
printDirectory(entry, numTabs+1);
|
printDirectory(entry, numTabs+1);
|
||||||
} else {
|
} else {
|
||||||
// files have sizes, directories do not
|
// files have sizes, directories do not
|
||||||
Serial.print("\t\t");
|
Serial.print("\t\t");
|
||||||
Serial.println(entry.size(), DEC);
|
Serial.println((unsigned int)entry.size(), DEC);
|
||||||
}
|
}
|
||||||
entry.close();
|
entry.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
File root;
|
xFile root;
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
||||||
//serial monitor
|
//serial monitor
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
delay(50);
|
while (!Serial) {}
|
||||||
|
|
||||||
//i2c
|
//i2c
|
||||||
Wire.begin(I2C_ADDR);
|
Wire.begin(I2C_ADDR);
|
||||||
|
|
@ -413,7 +549,14 @@ void setup() {
|
||||||
// Wire.onRequest(requestEvent);
|
// Wire.onRequest(requestEvent);
|
||||||
|
|
||||||
//SD
|
//SD
|
||||||
if (!SD.begin()) {
|
#if defined(USE_SD)
|
||||||
|
if (!(SD.begin(BUILTIN_SDCARD)))
|
||||||
|
#elif defined(USE_SDFATSDIO)
|
||||||
|
if (!SD.begin())
|
||||||
|
#elif defined(USE_SDFATBETA)
|
||||||
|
if (!SD.begin(SdioConfig(FIFO_SDIO)))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
Serial.println("[sd] initialization failed!");
|
Serial.println("[sd] initialization failed!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -422,10 +565,10 @@ void setup() {
|
||||||
printDirectory(root, 0);
|
printDirectory(root, 0);
|
||||||
|
|
||||||
//polyphonics - 4 voices
|
//polyphonics - 4 voices
|
||||||
poly_bank.push_back(__voice_1);
|
poly_bank.push_back(__voice_1); __voice_1.start();
|
||||||
poly_bank.push_back(__voice_2);
|
poly_bank.push_back(__voice_2); __voice_2.start();
|
||||||
poly_bank.push_back(__voice_3);
|
// poly_bank.push_back(__voice_3); __voice_3.start();
|
||||||
poly_bank.push_back(__voice_4);
|
// poly_bank.push_back(__voice_4); __voice_4.start();
|
||||||
|
|
||||||
//audio
|
//audio
|
||||||
AudioMemory(20); // <-- used AudioMemoryUsageMax() to check out!
|
AudioMemory(20); // <-- used AudioMemoryUsageMax() to check out!
|
||||||
|
|
@ -455,12 +598,8 @@ void setup() {
|
||||||
amp7.gain(1.0);
|
amp7.gain(1.0);
|
||||||
amp8.gain(1.0);
|
amp8.gain(1.0);
|
||||||
|
|
||||||
//tasks
|
//teensythreads
|
||||||
// runner.addTask(playcheck_task);
|
threads.addThread(playcheck);
|
||||||
// playcheck_task.enable();
|
|
||||||
//
|
|
||||||
runner.addTask(scheduleNoteOn_task);
|
|
||||||
runner.addTask(scheduleNoteOff_task);
|
|
||||||
|
|
||||||
//led
|
//led
|
||||||
pinMode(13, OUTPUT);
|
pinMode(13, OUTPUT);
|
||||||
|
|
@ -470,9 +609,8 @@ void setup() {
|
||||||
Serial.println("[setup] done.");
|
Serial.println("[setup] done.");
|
||||||
|
|
||||||
//watchdog
|
//watchdog
|
||||||
Watchdog.enable(1000);
|
Watchdog.enable(3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
runner.execute();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@
|
||||||
|
|
||||||
//==========<preset>===========
|
//==========<preset>===========
|
||||||
// (1) the backbone AP
|
// (1) the backbone AP
|
||||||
#if 1
|
#if 0
|
||||||
#define DISABLE_I2C_REQ
|
#define DISABLE_I2C_REQ
|
||||||
#define SET_CONTAINSROOT
|
#define SET_CONTAINSROOT
|
||||||
// (2) osc client (the ROOT)
|
// (2) osc client (the ROOT)
|
||||||
|
|
@ -72,7 +72,7 @@
|
||||||
#define SET_ROOT
|
#define SET_ROOT
|
||||||
#define SET_CONTAINSROOT
|
#define SET_CONTAINSROOT
|
||||||
// (3) sampler client
|
// (3) sampler client
|
||||||
#elif 0
|
#elif 1
|
||||||
#define DISABLE_AP
|
#define DISABLE_AP
|
||||||
#define DISABLE_I2C_REQ
|
#define DISABLE_I2C_REQ
|
||||||
//
|
//
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue