//
// 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++ ;
}