Commit 75a66c48 authored by Jacob Vosmaer's avatar Jacob Vosmaer

Merge branch 'fewer-files'

parents 98512076 820ce99a
PRG = crowbx
OBJ = crowbx.o midi.o dac.o gate.o poly.o mono.o list.o vibrato.o poly2.o clamp_add.o pitch.o pitch_env.o vibrato_rate.o voice.o
OBJ = crowbx.o midi.o list.o pitch_env.o vibrato_rate.o voice.o
MCU_TARGET = atmega328p
OPTIMIZE = -O3
......
#include "clamp_add.h"
uint16_t clamp_add(uint16_t x, int16_t delta) {
if (delta < 0) {
uint16_t sub = -delta;
if (x < sub) {
return 0;
}
return x - sub;
}
uint16_t add = delta;
if (UINT16_MAX - x < add) {
return UINT16_MAX;
}
return x + add;
}
#ifndef CLAMP_ADD_H
#define CLAMP_ADD_H
#include <stdint.h>
uint16_t clamp_add(uint16_t x, int16_t delta);
#endif
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/atomic.h>
#include <util/delay.h>
#include "array_size.h"
#include "dac.h"
#include "gate.h"
#include "list.h"
#include "midi.h"
#include "mono.h"
#include "pitch.h"
#include "pitch_env.h"
#include "poly.h"
#include "poly2.h"
#include "uart.h"
#include "vibrato.h"
#include "vibrato_rate.h"
#include "voice.h"
static struct {
uint8_t data[16];
uint8_t head;
uint8_t tail;
} uart_buffer;
uint16_t clamp_add(uint16_t x, int16_t delta) {
if (delta < 0) {
uint16_t sub = -delta;
if (x < sub) {
return 0;
}
return x - sub;
}
void uart_buffer_push(uint8_t x) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
uart_buffer.data[uart_buffer.tail] = x;
uart_buffer.tail =
(uart_buffer.tail + 1) % ARRAY_SIZE(uart_buffer.data);
uint16_t add = delta;
if (UINT16_MAX - x < add) {
return UINT16_MAX;
}
return x + add;
}
uint8_t uart_buffer_pop(uint8_t *x) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
if (uart_buffer.head == uart_buffer.tail) {
return false; // error: uart_buffer is empty
uint16_t dac_pitch_delta(uint8_t semitones) {
if (semitones >= NUM_SEMITONES) {
semitones = NUM_SEMITONES - 1;
}
return dac_semitone_offset(semitones);
}
struct {
volatile uint8_t *port;
volatile uint8_t *ddr;
uint8_t bit;
} dac_sync = {.port = &PORTB, .ddr = &DDRB, .bit = PORTB1};
void dac_sync_enable(void) { *dac_sync.port &= ~_BV(dac_sync.bit); }
void dac_sync_disable(void) { *dac_sync.port |= _BV(dac_sync.bit); }
void dac_sync_init(void) {
*dac_sync.ddr |= _BV(dac_sync.bit);
dac_sync_disable();
_delay_ms(1); // let dac boot up
}
uint8_t dac_command_set(uint8_t v) {
struct dac *d = dac(v);
if (!d) {
return 0;
};
return 0b00110000 | d->channel;
}
void spi_send(uint8_t data) {
SPDR = data;
loop_until_bit_is_set(SPSR, SPIF);
}
void dac_send(uint8_t command, uint16_t data) {
dac_sync_enable();
spi_send(command);
spi_send(data >> 8);
spi_send(data & 0xff);
dac_sync_disable();
}
void spi_init(void) {
enum { MOSI = PORTB3, MISO = PORTB4, SCK = PORTB5, SS = PORTB2 };
// slave select, sck, mosi all as output
DDRB |= _BV(SS) | _BV(MOSI) | _BV(SCK);
PORTB |= _BV(MISO); // MISO pull-up to save power
// spi enable
SPCR |= _BV(SPE) | _BV(MSTR) | _BV(CPOL);
SPSR |= _BV(SPI2X);
}
void dac_init(void) {
dac_sync_init();
spi_init();
}
uint16_t dac_note_pitch(uint8_t v, uint8_t note) {
enum { MIDI_LOWEST = 24 };
struct dac *d = dac(v);
if (!d) {
return 0;
};
// Don't go lower than "C0"
if (note < MIDI_LOWEST) {
note = MIDI_LOWEST;
}
if (note > 127) {
note = 127;
}
uint8_t octave = (note - MIDI_LOWEST) / NUM_SEMITONES;
if (octave > 7) {
return 0;
}
return clamp_add(
d->offset + octave * d->scale,
dac_semitone_offset((note - MIDI_LOWEST) % NUM_SEMITONES));
}
void gate_on_legato(uint8_t v) {
struct gate *g = gate(v);
if (!g) {
return;
}
*(g->port) |= g->mask;
}
void gate_off(uint8_t v) {
struct gate *g = gate(v);
if (!g) {
return;
}
*(g->port) &= ~(g->mask);
}
void gate_on(uint8_t v) {
if (v >= NUM_VOICES) {
return;
}
gate_off(v);
pitch_env_trigger(v);
gate_on_legato(v);
}
void gate_init(void) {
for_each_voice (v) { gate_off(v); }
}
enum { NUM_NOTES = 128 };
struct {
uint32_t phase;
int16_t amount;
uint8_t rate;
uint8_t depth;
} vibrato;
void vibrato_set_rate(uint8_t val) { vibrato.rate = val; }
void vibrato_set_depth(uint8_t val) { vibrato.depth = val; }
int16_t vibrato_detune(void) { return vibrato.amount; }
void vibrato_update(uint8_t delta) {
enum { VIB_MID = INT16_MAX >> 1 };
vibrato.phase +=
delta * pgm_read_float(&(vibrato_rate_table[vibrato.rate]));
uint16_t phase16 = vibrato.phase >> 16;
if (phase16 < INT16_MAX) {
vibrato.amount = phase16 - VIB_MID;
} else {
vibrato.amount = phase16 - INT16_MAX;
vibrato.amount = INT16_MAX - vibrato.amount;
vibrato.amount -= VIB_MID;
}
float pitch_factor = dac_pitch_delta(7) / ((float)INT16_MAX);
vibrato.amount *= pitch_factor;
float depth_factor = vibrato.depth / 127.0;
vibrato.amount *= depth_factor;
if (!vibrato.depth) {
vibrato.amount = 0;
}
}
struct {
int16_t bend_data;
uint16_t base_pitch[NUM_VOICES];
} pitch;
void pitch_set_note(uint8_t voice, uint8_t note, int16_t detune) {
if ((voice >= NUM_VOICES) || (note > 127)) {
return;
}
pitch.base_pitch[voice] =
clamp_add(dac_note_pitch(voice, note), detune);
}
void pitch_set_bend(uint16_t bend) {
enum { BEND_CENTER = 8192, BEND_MAX = 16383 };
float bend_factor =
((int16_t)(bend & BEND_MAX) - BEND_CENTER) / ((float)BEND_MAX);
pitch.bend_data = dac_pitch_delta(4) * bend_factor;
}
void pitch_update_clock(uint8_t delta) {
pitch_env_update_clock(delta);
for_each_voice (v) {
uint16_t data =
clamp_add(pitch.base_pitch[v], vibrato_detune());
data = clamp_add(data, pitch_env_delta(v) *
(float)dac_pitch_delta(5));
data = clamp_add(data, pitch.bend_data);
dac_send(dac_command_set(v), data);
}
}
struct {
uint8_t avail_array[NUM_VOICES];
uint8_t in_use_array[NUM_VOICES];
struct list avail;
struct list in_use;
uint8_t current_note[NUM_VOICES];
} poly = {
.avail = {.array = poly.avail_array, .sup = ARRAY_SIZE(poly.avail_array)},
.in_use = {.array = poly.in_use_array,
.sup = ARRAY_SIZE(poly.in_use_array)},
};
uint8_t poly_acquire(void) {
if (l_empty(&poly.avail)) {
l_pushl(&poly.avail, l_popr(&poly.in_use));
}
uint8_t v = l_popr(&poly.avail);
l_pushl(&poly.in_use, v);
return v;
}
void poly_release(uint8_t v) {
if (l_contains(&poly.in_use, v)) {
l_delete(&poly.in_use, v);
l_pushl(&poly.avail, v);
}
}
void poly_init(void) {
l_flush(&poly.in_use);
l_flush(&poly.avail);
for_each_voice (v) {
l_pushl(&poly.avail, v);
poly.current_note[v] = NUM_NOTES;
}
}
void poly_note_on(uint8_t key) {
if (key >= NUM_NOTES) {
return;
}
uint8_t v = poly_acquire();
if (v >= NUM_VOICES) {
return;
}
pitch_set_note(v, key, 0);
gate_on(v);
poly.current_note[v] = key;
}
void poly_note_off(uint8_t key) {
if (key >= NUM_NOTES) {
return;
}
for_each_voice (v) {
if (poly.current_note[v] == key) {
poly.current_note[v] = NUM_NOTES;
poly_release(v);
gate_off(v);
}
}
}
*x = uart_buffer.data[uart_buffer.head];
uart_buffer.head =
(uart_buffer.head + 1) % ARRAY_SIZE(uart_buffer.data);
struct {
uint8_t available_voices_array[NUM_VOICES];
uint8_t note_queue_array[NUM_NOTES];
struct list available_voices;
struct list note_queue;
uint8_t current_note[NUM_VOICES];
} poly2 = {
.available_voices = {.array = poly2.available_voices_array,
.sup = ARRAY_SIZE(poly2.available_voices_array)},
.note_queue = {.array = poly2.note_queue_array,
.sup = ARRAY_SIZE(poly2.note_queue_array)},
};
void poly2_init(void) {
l_flush(&poly2.note_queue);
l_flush(&poly2.available_voices);
for_each_voice (v) {
poly2.current_note[v] = poly2.note_queue.sup;
l_pushl(&poly2.available_voices, v);
}
}
return true;
void poly2_assign_available_voices(void) {
for (; !l_empty(&poly2.available_voices);) {
uint8_t v = l_popr(&poly2.available_voices);
uint8_t note = l_popr(&poly2.note_queue);
if (note == poly2.note_queue.sup) {
l_pushr(&poly2.available_voices, v);
return;
}
gate_on(v);
pitch_set_note(v, note, 0);
poly2.current_note[v] = note;
}
}
ISR(USART_RX_vect) {
uint8_t uart_status = UCSR0A;
if (!(uart_status & _BV(RXC0))) {
// No UART data available
void poly2_note_on(uint8_t note) {
if (note >= poly2.note_queue.sup) {
return;
}
// Read data to clear all status bits
uint8_t uart_data = UDR0;
l_pushl(&poly2.note_queue, note);
poly2_assign_available_voices();
}
if (uart_status & (_BV(FE0) | _BV(DOR0) | _BV(UPE0))) {
// There was a UART error
void poly2_note_off(uint8_t note) {
if (note >= poly2.note_queue.sup) {
return;
}
uart_buffer_push(uart_data);
l_delete(&poly2.note_queue, note);
for_each_voice (v) {
if (poly2.current_note[v] == note) {
poly2.current_note[v] = poly2.note_queue.sup;
gate_off(v);
l_pushl(&poly2.available_voices, v);
}
}
poly2_assign_available_voices();
}
uint8_t uart_read(uint8_t *data) { return uart_buffer_pop(data); }
struct {
uint8_t held_notes_array[NUM_NOTES];
struct list held_notes;
const float detune_factor[MAX_VOICES];
uint8_t detune_amount;
uint8_t stacked_voices;
} mono = {
.held_notes = {.array = mono.held_notes_array,
.sup = ARRAY_SIZE(mono.held_notes_array)},
.detune_factor = {1.2, -1.2, 0.6, -0.6, 0.9, -0.9, 0.3, -0.3},
};
uint8_t mono_active_voices(void) {
if (mono.stacked_voices && mono.stacked_voices < NUM_VOICES) {
return mono.stacked_voices;
} else {
return NUM_VOICES;
}
}
void mono_init(void) { l_flush(&mono.held_notes); }
void mono_set_pitch(void) {
if (l_empty(&mono.held_notes)) {
return;
}
uint8_t current_note = l_last(&mono.held_notes);
for (uint8_t v = 0; v < mono_active_voices(); v++) {
int16_t detune = mono.detune_factor[v] * mono.detune_amount;
pitch_set_note(v, current_note, detune);
}
}
void mono_note_on(uint8_t n) {
if (n >= NUM_NOTES) {
return;
}
l_pushr(&mono.held_notes, n);
mono_set_pitch();
for (uint8_t v = 0; v < mono_active_voices(); v++) {
gate_on(v);
}
}
void mono_note_off(uint8_t n) {
if (n >= NUM_NOTES) {
return;
}
if (l_last(&mono.held_notes) != n) {
l_delete(&mono.held_notes, n);
return;
}
l_delete(&mono.held_notes, n);
mono_set_pitch();
void (*f)(uint8_t) = l_empty(&mono.held_notes) ? gate_off : gate_on;
for (uint8_t v = 0; v < mono_active_voices(); v++) {
f(v);
}
}
void mono_control_change(uint8_t ctl, uint8_t val) {
switch (ctl) {
case CC_DETUNE:
mono.detune_amount = val;
break;
case CC_STACKED_VOICES:
mono.stacked_voices = val;
for (uint8_t v = mono_active_voices(); v < NUM_VOICES; v++) {
gate_off(v);
}
break;
}
}
struct {
void (*init)(void);
......@@ -69,8 +440,8 @@ struct {
void (*note_off)(uint8_t n);
void (*control_change)(uint8_t ctl, uint8_t val);
} programs[] = {
{poly_init, poly_note_on, poly_note_off, poly_control_change},
{poly2_init, poly2_note_on, poly2_note_off, poly2_control_change},
{poly_init, poly_note_on, poly_note_off, 0},
{poly2_init, poly2_note_on, poly2_note_off, 0},
{mono_init, mono_note_on, mono_note_off, mono_control_change},
};
......@@ -156,14 +527,15 @@ void handle_cc(uint8_t cc, uint8_t val) {
break;
}
programs[current_program].control_change(cc, val);
if (programs[current_program].control_change) {
programs[current_program].control_change(cc, val);
}
}
int main(void) {
io_init();
dac_init();
midi_init();
pitch_init();
program_change(0);
for (;;) {
......@@ -199,3 +571,51 @@ int main(void) {
return 0;
}
static struct {
uint8_t data[16];
uint8_t head;
uint8_t tail;
} uart_buffer;
void uart_buffer_push(uint8_t x) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
uart_buffer.data[uart_buffer.tail] = x;
uart_buffer.tail =
(uart_buffer.tail + 1) % ARRAY_SIZE(uart_buffer.data);
}
}
uint8_t uart_buffer_pop(uint8_t *x) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
if (uart_buffer.head == uart_buffer.tail) {
return false; // error: uart_buffer is empty
}
*x = uart_buffer.data[uart_buffer.head];
uart_buffer.head =
(uart_buffer.head + 1) % ARRAY_SIZE(uart_buffer.data);
}
return true;
}
ISR(USART_RX_vect) {
uint8_t uart_status = UCSR0A;
if (!(uart_status & _BV(RXC0))) {
// No UART data available
return;
}
// Read data to clear all status bits
uint8_t uart_data = UDR0;
if (uart_status & (_BV(FE0) | _BV(DOR0) | _BV(UPE0))) {
// There was a UART error
return;
}
uart_buffer_push(uart_data);
}
uint8_t uart_read(uint8_t *data) { return uart_buffer_pop(data); }
#include "dac.h"
#include "clamp_add.h"
#include "voice.h"
#include <avr/io.h>
#include <util/delay.h>
uint16_t dac_pitch_delta(uint8_t semitones) {
if (semitones >= NUM_SEMITONES) {
semitones = NUM_SEMITONES - 1;
}
return dac_semitone_offset(semitones);
}
uint8_t spi_io(uint8_t data) {
SPDR = data;
loop_until_bit_is_set(SPSR, SPIF);
return SPDR;
}
#define DAC_SYNC PORTB1
#define MOSI PORTB3
#define MISO PORTB4
#define SCK PORTB5
#define SS PORTB2