// // BeatBox -- Simple Arduino rhythm player // // Author: P.J. Drongowski // Date: 15 July 2016 // Copyright (c) 2016 Paul J. Drongowski // Permission granted to distribute and modify #include <avr/pgmspace.h> #include <avr/interrupt.h> #include "waveforms.h" #include "kits.h" #include "patterns.h" // Define input pins int ResetButton = 0 ; // Unused (digital) int tempoInputPin = 0 ; // Set BPM (analog) int patternInputPin = 1 ; // Choose pattern and kit (analog) // Define output pins int gatePin = 1 ; // Unused int blahPin = 5 ; // Unused int pwmPin = 9 ; // PWM (DAC) output // Rhythm instrument control variables #define INSTRUMENTS 8 const int8_t* sampleArrays[INSTRUMENTS] ; int16_t sampleSizes[INSTRUMENTS] ; int sampleCounts[INSTRUMENTS] ; int sampleIndices[INSTRUMENTS] ; // Tempo control variables int tempo = 120 ; int delayValue = 125 ; // Pattern and pattern control variables int patternId = 0 ; int kitId = 0 ; int patternLength = 16 ; int patternIndex = 0 ; uint8_t pattern[64] ; // // Configure TIMER1 to produce an interupt at a ~22050 Hz // sampling rate. TIMER1 is a 16-bit timer. No PWM. // void TimerSetup() { cli() ; // Normal operation - no PWM TCCR1A = 0 ; // No prescaling (CS10), CTC mode (WGM12) TCCR1B = _BV(WGM12) | _BV(CS10) ; // Sample rate (CPU clock: 16MHz, Sample rate: 22050Hz) OCR1A = 16000000 / 22050 ; // SD fill interrupt happens at TCNT1 == 1 // OCR1B = 1 ; // Enable timer interrupt for DAC ISR TIMSK1 |= _BV(OCIE1A) ; sei() ; } // // TIMER1 PWM. Single PWM, phase correct, 22050KHz. // PWM_FREQ = 16,000,000 / 22,050 = 726 = 0x2D5 // PWM_FREQ = 16,000,000 / 11,025 = 1451 = 0x5AB // #define PWM_FREQ 363 void PwmSetup() { // Clear OC1 on compare match, 8-bit PWM //TCCR1A = _BV(COM1A1) | _BV(WGM10) ; TCCR1A = _BV(COM1A1) ; // PWM TOP is OCR1A, No prescaler TCCR1B = _BV(WGM13) | _BV(CS10) ; // Generate interrupt on input capture TIMSK1 = _BV(ICIE1) ; // Set input capture register to sampling frequency ICR1H = (PWM_FREQ >> 8) ; ICR1L = (PWM_FREQ & 0xff) ; // Turn on the output pin D9 DDRB |= _BV(5) ; sei() ; } void DisableInterrupts() { cli() ; TIMSK1 &= ~_BV(OCIE1A) ; // Disable DAC interrupts sei() ; } void EnableInterrupts() { cli(); TIMSK1 |= _BV(OCIE1A) ; // Enable DAC interrupts sei(); } // // Interrupt service routine (ISR) // ISR(TIMER1_CAPT_vect) { register int16_t dacValue = 0 ; register int16_t sample = 0 ; if (sampleCounts[0] > 0) { sample = (int8_t)pgm_read_byte_near( sampleArrays[0]+sampleIndices[0]) ; dacValue += sample ; sampleIndices[0]++ ; sampleCounts[0]-- ; } if (sampleCounts[1] > 0) { sample = (int8_t)pgm_read_byte_near( sampleArrays[1]+sampleIndices[1]) ; dacValue += sample ; sampleIndices[1]++ ; sampleCounts[1]-- ; } if (sampleCounts[2] > 0) { sample = (int8_t)pgm_read_byte_near( sampleArrays[2]+sampleIndices[2]) ; dacValue += sample ; sampleIndices[2]++ ; sampleCounts[2]-- ; } if (sampleCounts[3] > 0) { sample = (int8_t)pgm_read_byte_near( sampleArrays[3]+sampleIndices[3]) ; dacValue += sample ; sampleIndices[3]++ ; sampleCounts[3]-- ; } if (sampleCounts[4] > 0) { sample = (int8_t)pgm_read_byte_near( sampleArrays[4]+sampleIndices[4]) ; dacValue += sample ; sampleIndices[4]++ ; sampleCounts[4]-- ; } if (sampleCounts[5] > 0) { sample = (int8_t)pgm_read_byte_near( sampleArrays[5]+sampleIndices[5]) ; dacValue += sample ; sampleIndices[5]++ ; sampleCounts[5]-- ; } if (sampleCounts[6] > 0) { sample = (int8_t)pgm_read_byte_near( sampleArrays[6]+sampleIndices[6]) ; dacValue += sample ; sampleIndices[6]++ ; sampleCounts[6]-- ; } if (sampleCounts[7] > 0) { sample = (int8_t)pgm_read_byte_near( sampleArrays[7]+sampleIndices[7]) ; dacValue += sample ; sampleIndices[7]++ ; sampleCounts[7]-- ; } // Output through OC1A dacValue += 127 ; OCR1AH = (uint8_t) (dacValue >> 8) & 0xFF ; OCR1AL = (uint8_t) dacValue & 0xFF ; } #define SHKIT 0 #define CTKIT 1 #define ADKIT 2 #define VRKIT 3 void loadKit(int kitId) { register int i ; register int8_t** samples ; register int16_t** sizes ; switch(kitId) { case SHKIT: default: { // SH kit samples = (int8_t**)shWaveforms ; sizes = (int16_t**)shWaveformSizes ; break ; } case CTKIT: { // CT kit samples = (int8_t**)ctWaveforms ; sizes = (int16_t**)ctWaveformSizes ; break ; } case ADKIT: { // AD kit samples = (int8_t**)adWaveforms ; sizes = (int16_t**)adWaveformSizes ; break ; } case VRKIT: { // VR kit samples = (int8_t**)vrWaveforms ; sizes = (int16_t**)vrWaveformSizes ; break ; } } for (i = 0 ; i < 8 ; i++) { sampleArrays[i] = (int8_t*)pgm_read_word_near(samples+i) ; sampleSizes[i] = (int16_t)pgm_read_word_near(sizes+i) ; } } void changePatternAndKit(int patternInput) { register int i ; register int kit ; register uint8_t* newpattern ; if (patternInput < 6) { // healingA pattern patternLength = HEALINGASIZE ; newpattern = (uint8_t*)healingA ; kit = SHKIT ; } else if (patternInput < 12) { // cybotranA pattern patternLength = CYBOTRONASIZE ; newpattern = (uint8_t*)cybotronA ; kit = CTKIT ; } else if (patternInput < 18) { // planetA pattern patternLength = PLANETASIZE ; newpattern = (uint8_t*)planetA ; kit = CTKIT ; } else if (patternInput < 24) { // adonisA pattern patternLength = ADONISASIZE ; newpattern = (uint8_t*)adonisA ; kit = ADKIT ; } else { // voodoo pattern patternLength = VOODOOSIZE ; newpattern = (uint8_t*)voodoo ; kit = VRKIT ; } loadKit(kit) ; if (patternLength > 64) patternLength = 64 ; for (i = 0 ; i < patternLength ; i++) { pattern[i] = (uint8_t)pgm_read_byte_near(newpattern+i) ; } } void setup() { register int i ; // Initialize the serial port (only for debugging) Serial.begin(9600) ; // Set up pin modes pinMode(ResetButton, INPUT_PULLUP) ; //pinMode(gatePin, OUTPUT) ; // Initialize pin values //digitalWrite(gatePin, LOW) ; changePatternAndKit(analogRead(patternInputPin) >> 5) ; tempo = 120 ; delayValue = (60000 / tempo) / 4 ; patternIndex = 0 ; for (i = 0 ; i < INSTRUMENTS ; i++) sampleCounts[i] = 0 ; PwmSetup() ; } void loop() { register uint8_t pbits ; register int difference ; static int oldTempoInput = 0 ; static int tempoInput = 0 ; static int oldPatternInput = 0 ; static int patternInput = 0 ; static int bpmCount = 3 ; // Turn TXLED on during the first eighth note of every beat if (bpmCount & 0x0002) { TXLED1 ; } else { TXLED0 ; } if (bpmCount == 0) bpmCount = 3; else bpmCount-- ; // Change tempo if necessary // Dimmer connected to pin A0 sweeps tempo from 60 to 188 BPM tempoInput = analogRead(tempoInputPin) >> 3 ; difference = abs(tempoInput - oldTempoInput) ; if (difference > 0) { // Change the tempo tempo = 60 + tempoInput ; delayValue = (60000 / tempo) / 4 ; oldTempoInput = tempoInput ; } // Change pattern/kit if necessary // Dimmer connected to pin A1 selects one of 5 patterns patternInput = analogRead(patternInputPin) >> 5 ; difference = abs(patternInput - oldPatternInput) ; if (difference > 0) { // Change the pattern and its associated kit changePatternAndKit(patternInput) ; oldPatternInput = patternInput ; } // Delay for the equivalent of one sixteenth note delay(delayValue) ; // Wrap around the pattern index if necessary and // get the bit pattern for the current step if (patternIndex >= patternLength) patternIndex = 0 ; pbits = pattern[patternIndex] ; for (int i = 0 ; i < 8 ; i++) { if (pbits & 0x01) { sampleCounts[i] = sampleSizes[i] ; sampleIndices[i] = 0 ; } pbits = pbits >> 1 ; } patternIndex++ ; }