Hello, there! Whats up?
I've been developing an arduino (mega2560) drum kit which uses neither a midi controller or wave files. Instead, it uses some uint16_t arrays stored in flash memory. This data, however, is obtained from raw audio files that are manipulated with Audacity to reach a frequency of 16 kHz.
Sure it is a project with a bunch of limitations, but as it works on tests with Serial, i guess it is good. The problem is im unable to test it, as i dont have an oscilloscopes or an wave analyzer, and besides that, i can't get my hands on a low pass filter yet.
I would really appreciate some hints, and would be very glad with some one test this for me!
Thanks!!
// prescale to fast analogRead()
#define FASTADC 1
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#define PIN_TIMER1 12 // OC2A for mega2560
#define ARR_MAX 4
#define INPUT_MAX 7 // number of drum pads
#define INPUT_LIMIT 300
#define INPUT_PERIOD 100
#define SIGNED_MAX 32767
// drum kit stored as arrays in flash memory
extern const uint16_t CYCdh_K2room_ClHat_05[1889] PROGMEM;
extern const uint16_t CYCdh_K2room_Kick_05[4025] PROGMEM;
extern const uint16_t CYCdh_K2room_OpHat_05[6414] PROGMEM;
extern const uint16_t CYCdh_K2room_Rim_05[5633] PROGMEM;
extern const uint16_t CYCdh_K2room_SdSt_05[3992] PROGMEM;
extern const uint16_t CYCdh_K2room_Snr_05[5620] PROGMEM;
extern const uint16_t CYCdh_K2room_SnrOff_05[6453] PROGMEM;
uint16_t inputs[INPUT_MAX];
uint32_t lastMicroseconds = 0;
volatile uint32_t counter {0};
// for info on hit sensors and sound they trigger
class Entry {
public:
// counting control to set if an Entry is active or not
uint32_t eEnd {};
uint16_t eCounter {0};
bool eOFF {true};
// 16 bit pointer to array in PROGMEM
const uint16_t* map {};
// constructors
Entry() {}
Entry(const volatile uint32_t& _counter, const uint16_t& _size, const uint16_t _map[]): eEnd(_counter+_size), map(_map) {}
~Entry() {}
void turnOFF(const volatile uint32_t& _counter) {
if (_counter >= this->eEnd) {
this->eOFF = true;
}
}
// return indexed array value from PROGMEM
uint16_t getData(const volatile uint32_t& _counter) {
uint16_t index {this->eCounter};
this->eCounter++;
return pgm_read_word(this->map + index);
}
// overload to help Buffer object handle entries
Entry operator = (Entry const& _entry) {
this->eEnd = _entry.eEnd;
this->eCounter = _entry.eCounter;
this->map = _entry.map;
this->eOFF = false;
}
}; // class Entry
// control and output calculation
class Buffer {
private:
// array of entries
Entry entries[ARR_MAX];
// 16 bit pwm signal
uint16_t output {0};
public:
Buffer() {}
// update entries array with new beat
void setEntry(const volatile uint32_t& _temp, const uint16_t& _size, const uint16_t _arr[]) {
for (uint8_t i=0; i<ARR_MAX; i++) {
if (this->entries[i].eOFF == true) {
this->entries[i] = Entry(_temp, _size, _arr);
break;
}
// raise ARR_MAX if entries size is too low (it kinda depends on the player's speed)
}
}
// set buffer's output
void setOutput(const volatile uint32_t& _counter) {
uint16_t preOutput {SIGNED_MAX};
for (uint8_t i=0; i<ARR_MAX; i++) {
// shut down entries that reached their end
this->entries[i].turnOFF(_counter);
uint16_t temp {this->entries[i].getData(_counter)};
// sum and subtraction of unsigned int waves
if ((temp < SIGNED_MAX) && (this->entries[i].eOFF == false)) {
preOutput -= (SIGNED_MAX - temp);
continue;
}
if (this->entries[i].eOFF == false) {
preOutput += (temp - SIGNED_MAX);
}
}
this->output = preOutput;
}
// returns output value to update fast pwm's OCR1B pin
uint16_t getOutput() const { return this->output; }
}; // class Buffer
// initiate a buffer
Buffer buf;
void setup() {
setTimers();
}
void loop() {
uint32_t microseconds {micros()};
if (microseconds - lastMicroseconds >= INPUT_PERIOD) {
lastMicroseconds = microseconds;
for (uint8_t i=0; i<INPUT_MAX; i++) {
uint16_t reading = analogRead(i);
if (reading > INPUT_LIMIT) getInput(i);
}
}
}
void setTimers() {
pinMode(PIN_TIMER1, OUTPUT);
// Set fast analog read
#if FASTADC
// prescale to 8
cbi(ADCSRA,ADPS2) ;
sbi(ADCSRA,ADPS1) ;
sbi(ADCSRA,ADPS0) ;
#endif
cli();
// Timer 1: fast pwm - mode 15, 40.000 Hz OCR1A = 399, presc = 1
TCCR1A = 0;
TCCR1B = 0;
TCCR1A |= (1<<COM1B1) | (1<<COM1B0) | (1<<WGM11) | (1<<WGM10); // b10000011
TCCR1B |= (1<<CS10) | (1<<WGM13)| (1<<WGM12); // b00011100
OCR1A = 399;
OCR1B = 0;
// Timer 2: Ctc - mode 4, 16 kHz, presc. 8, OCR2A = 124
TCCR2A = 0;
TCCR2B = 0;
TCCR2A |= (1<<WGM21);
TCCR2B |= (1<<CS21);
OCR2A = 124;
TIMSK2 |= (1<<OCIE2A);
sei();
}
// 40 khz Timer1-PWM and 16 kHz Timer2-SAMPLING
ISR(TIMER2_COMPA_vect) {
OCR1B = buf.getOutput();
buf.setOutput(counter);
counter++;
}
void getInput(const uint8_t& index) {
switch (index) {
case 0:
buf.setEntry(counter, 1889, CYCdh_K2room_ClHat_05);
break;
case 1:
buf.setEntry(counter, 4025, CYCdh_K2room_Kick_05);
break;
case 2:
buf.setEntry(counter, 6414, CYCdh_K2room_OpHat_05);
break;
case 3:
buf.setEntry(counter, 5633, CYCdh_K2room_Rim_05);
break;
case 4:
buf.setEntry(counter, 3992, CYCdh_K2room_SdSt_05);
break;
case 5:
buf.setEntry(counter, 5620, CYCdh_K2room_Snr_05);
break;
case 6:
buf.setEntry(counter, 6453, CYCdh_K2room_SnrOff_05 );
break;
default:
break;
}
}
A link for the drum arrays on github:
https://github.com/BAL3IA/DrumMech/blob/6cc69bf17a34061a8d073bf2409003b9e6f09ef3/extern_arrays16kHz.ino