Commit 6a355548 authored by Jacob Vosmaer's avatar Jacob Vosmaer

Remove forward declarations

parent 70a997ec
......@@ -11,185 +11,235 @@
#include "vibrato_rate.h"
#include "voice.h"
uint16_t clamp_add(uint16_t x, int16_t delta);
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 dac_init(void);
uint16_t dac_pitch_delta(uint8_t semitones);
void dac_send(uint8_t command, uint16_t data);
uint16_t dac_note_pitch(uint8_t voice, uint8_t note);
uint8_t dac_command_set(uint8_t voice);
uint16_t add = delta;
if (UINT16_MAX - x < add) {
return UINT16_MAX;
}
return x + add;
}
void gate_init(void);
void gate_on(uint8_t voice);
void gate_on_legato(uint8_t voice);
void gate_off(uint8_t voice);
void gate_update(uint8_t delta);
uint16_t dac_pitch_delta(uint8_t semitones) {
if (semitones >= NUM_SEMITONES) {
semitones = NUM_SEMITONES - 1;
}
enum { NUM_NOTES = 128 };
return dac_semitone_offset(semitones);
}
int16_t vibrato_detune(void);
void vibrato_update(uint8_t delta);
void vibrato_set_rate(uint8_t val);
void vibrato_set_depth(uint8_t val);
uint8_t spi_io(uint8_t data) {
SPDR = data;
loop_until_bit_is_set(SPSR, SPIF);
return SPDR;
}
void pitch_set_note(uint8_t voice, uint8_t note, int16_t detune);
void pitch_set_bend(uint16_t bend);
uint16_t pitch_delta(uint8_t semitones);
void pitch_update_clock(uint8_t delta);
void pitch_env_set_decay(uint8_t decay);
void pitch_env_set_amount(uint8_t amount);
void pitch_env_trigger(uint8_t voice);
#define DAC_SYNC PORTB1
#define MOSI PORTB3
#define MISO PORTB4
#define SCK PORTB5
#define SS PORTB2
void poly_init(void);
void poly_note_on(uint8_t note);
void poly_note_off(uint8_t note);
uint8_t dac_command_set(uint8_t v) {
struct dac *d = dac(v);
if (!d) {
return 0;
};
void poly2_init(void);
void poly2_note_on(uint8_t note);
void poly2_note_off(uint8_t note);
return 0b00110000 | d->channel;
}
void mono_init(void);
void mono_note_on(uint8_t note);
void mono_note_off(uint8_t note);
void mono_control_change(uint8_t ctl, uint8_t val);
void dac_send(uint8_t command, uint16_t data) {
PORTB &= ~_BV(DAC_SYNC);
spi_io(command);
spi_io(data >> 8);
spi_io(data & 0xff);
PORTB |= _BV(DAC_SYNC);
}
struct {
void (*init)(void);
void (*note_on)(uint8_t n);
void (*note_off)(uint8_t n);
void (*control_change)(uint8_t ctl, uint8_t val);
} programs[] = {
{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},
};
void spi_init(void) {
// slave select, sck, mosi all as output
DDRB |= _BV(SS) | _BV(MOSI) | _BV(SCK);
// Make sure current_program is not 0, so program_change(0) below is not a
// no-op.
uint8_t current_program = ARRAY_SIZE(programs);
PORTB |= _BV(MISO); // MISO pull-up to save power
void io_init(void) {
// More outputs to save power; PORTB6/7 is XTAL
DDRB = _BV(PORTB0) | _BV(PORTB1) | _BV(PORTB2) | _BV(PORTB3) |
_BV(PORTB4) | _BV(PORTB5);
// spi enable
SPCR |= _BV(SPE) | _BV(MSTR) | _BV(CPOL);
SPSR |= _BV(SPI2X);
}
// More outputs to save power; PORTC6 is reset, "PORTC7" does not exist
// (?)
DDRC = _BV(PORTC0) | _BV(PORTC1) | _BV(PORTC2) | _BV(PORTC3) |
_BV(PORTC4) | _BV(PORTC5);
void dac_init(void) {
DDRB |= _BV(DAC_SYNC);
// PORTD0 is UART RX. Set all other PORTD pins output, to save power.
DDRD = _BV(PORTD1) | _BV(PORTD2) | _BV(PORTD3) | _BV(PORTD4) |
_BV(PORTD5) | _BV(PORTD6) | _BV(PORTD7);
spi_init();
// Run Timer0 (8-bit timer) at F_CPU/1024 -- faster incurs roll arounds.
// Use 16 bit timer?
TCCR0B |= _BV(CS00) | _BV(CS02);
// dac sync high for power saving
PORTB |= _BV(DAC_SYNC);
// Assuming f_cpu is 16 MHz, this gives us 31250 baud
UBRR0H = 0;
UBRR0L = 31;
_delay_ms(1); // let dac boot up
}
// Enable UART0 RX and RX interrupt
UCSR0B = _BV(RXEN0) | _BV(RXCIE0);
uint16_t dac_note_pitch(uint8_t v, uint8_t note) {
enum { MIDI_LOWEST = 24 };
struct dac *d = dac(v);
if (!d) {
return 0;
};
// Enable pull-up resistor on RX pin
PORTD |= _BV(PORTD0);
// Don't go lower than "C0"
if (note < MIDI_LOWEST) {
note = MIDI_LOWEST;
}
if (note > 127) {
note = 127;
}
// Globally enable interrupts
sei();
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 program_change(uint8_t pgm) {
if ((pgm >= ARRAY_SIZE(programs)) || (pgm == current_program)) {
static uint8_t gate_counters[NUM_VOICES];
void gate_on_legato(uint8_t v) {
struct gate *g = gate(v);
if (!g) {
return;
}
current_program = pgm;
gate_init();
programs[current_program].init();
*(g->port) |= g->mask;
}
void do_clock_update(void) {
static uint8_t prev_clock = 0;
void gate_off(uint8_t v) {
struct gate *g = gate(v);
if (!g) {
return;
}
uint8_t clock = TCNT0;
uint8_t delta = clock - prev_clock;
prev_clock = clock;
gate_counters[v] = 0;
*(g->port) &= ~(g->mask);
}
if (delta == 0) {
void gate_on(uint8_t v) {
if (v >= NUM_VOICES) {
return;
}
vibrato_update(delta);
gate_update(delta);
pitch_update_clock(delta);
gate_off(v);
// RETRIG_DELAY should be as low as possible to reduce MIDI latency, but
// high enough to allow the hardware envelope generators in the CrowBX
// to reset. I know 0x3f to be too low.
enum { RETRIG_DELAY = 0x5f };
gate_counters[v] = RETRIG_DELAY;
}
void handle_cc(uint8_t cc, uint8_t val) {
if ((cc > 127) || (val > 127)) {
return;
void gate_update(uint8_t delta) {
for_each_voice (v) {
if (gate_counters[v] == 0) {
continue;
}
if (delta < gate_counters[v]) {
gate_counters[v] -= delta;
continue;
}
gate_counters[v] = 0;
gate_on_legato(v);
pitch_env_trigger(v);
}
}
switch (cc) {
case CC_PITCH_ENV_DECAY:
pitch_env_set_decay(val);
break;
case CC_PITCH_ENV_AMOUNT:
pitch_env_set_amount(val);
break;
case CC_MOD_WHEEL:
vibrato_set_depth(val);
break;
case CC_VIB_RATE:
vibrato_set_rate(val);
break;
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;
}
if (programs[current_program].control_change) {
programs[current_program].control_change(cc, val);
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;
}
}
int main(void) {
io_init();
dac_init();
midi_init();
program_change(0);
struct {
int16_t bend_data;
uint16_t base_pitch[NUM_VOICES];
} pitch;
for (;;) {
do_clock_update();
void pitch_set_note(uint8_t voice, uint8_t note, int16_t detune) {
if ((voice >= NUM_VOICES) || (note > 127)) {
return;
}
uint8_t status, data1, data2;
if (!midi_read(&status, &data1, &data2)) {
continue;
}
pitch.base_pitch[voice] =
clamp_add(dac_note_pitch(voice, note), detune);
}
switch (status) {
case MIDI_NOTE_ON:
if (data2) {
programs[current_program].note_on(data1);
} else {
programs[current_program].note_off(data1);
}
break;
case MIDI_NOTE_OFF:
programs[current_program].note_off(data1);
break;
case MIDI_PROGRAM_CHANGE:
program_change(data1);
break;
case MIDI_CONTROL_CHANGE:
handle_cc(data1, data2);
break;
case MIDI_PITCH_BEND:
pitch_set_bend((data2 << 7) | data1);
break;
}
}
void pitch_set_bend(uint16_t bend) {
enum { BEND_CENTER = 8192, BEND_MAX = 16383 };
return 0;
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 {
......@@ -310,73 +360,16 @@ void poly2_note_off(uint8_t note) {
return;
}
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();
}
static uint8_t gate_counters[NUM_VOICES];
void gate_init(void) {
for_each_voice (v) { gate_off(v); }
}
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;
}
gate_counters[v] = 0;
*(g->port) &= ~(g->mask);
}
void gate_on(uint8_t v) {
if (v >= NUM_VOICES) {
return;
}
gate_off(v);
// RETRIG_DELAY should be as low as possible to reduce MIDI latency, but
// high enough to allow the hardware envelope generators in the CrowBX
// to reset. I know 0x3f to be too low.
enum { RETRIG_DELAY = 0x5f };
gate_counters[v] = RETRIG_DELAY;
}
void gate_update(uint8_t delta) {
for_each_voice (v) {
if (gate_counters[v] == 0) {
continue;
}
if (delta < gate_counters[v]) {
gate_counters[v] -= delta;
continue;
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);
}
gate_counters[v] = 0;
gate_on_legato(v);
pitch_env_trigger(v);
}
poly2_assign_available_voices();
}
struct {
......@@ -461,177 +454,142 @@ void mono_control_change(uint8_t ctl, uint8_t val) {
}
struct {
uint32_t phase;
int16_t amount;
uint8_t rate;
uint8_t depth;
} vibrato;
void (*init)(void);
void (*note_on)(uint8_t n);
void (*note_off)(uint8_t n);
void (*control_change)(uint8_t ctl, uint8_t val);
} programs[] = {
{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},
};
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; }
// Make sure current_program is not 0, so program_change(0) below is not a
// no-op.
uint8_t current_program = ARRAY_SIZE(programs);
void vibrato_update(uint8_t delta) {
enum { VIB_MID = INT16_MAX >> 1 };
void io_init(void) {
// More outputs to save power; PORTB6/7 is XTAL
DDRB = _BV(PORTB0) | _BV(PORTB1) | _BV(PORTB2) | _BV(PORTB3) |
_BV(PORTB4) | _BV(PORTB5);
vibrato.phase +=
delta * pgm_read_float(&(vibrato_rate_table[vibrato.rate]));
// More outputs to save power; PORTC6 is reset, "PORTC7" does not exist
// (?)
DDRC = _BV(PORTC0) | _BV(PORTC1) | _BV(PORTC2) | _BV(PORTC3) |
_BV(PORTC4) | _BV(PORTC5);
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;
}
// PORTD0 is UART RX. Set all other PORTD pins output, to save power.
DDRD = _BV(PORTD1) | _BV(PORTD2) | _BV(PORTD3) | _BV(PORTD4) |
_BV(PORTD5) | _BV(PORTD6) | _BV(PORTD7);
float pitch_factor = dac_pitch_delta(7) / ((float)INT16_MAX);
vibrato.amount *= pitch_factor;
// Run Timer0 (8-bit timer) at F_CPU/1024 -- faster incurs roll arounds.
// Use 16 bit timer?
TCCR0B |= _BV(CS00) | _BV(CS02);
float depth_factor = vibrato.depth / 127.0;
vibrato.amount *= depth_factor;
// Assuming f_cpu is 16 MHz, this gives us 31250 baud
UBRR0H = 0;
UBRR0L = 31;
if (!vibrato.depth) {
vibrato.amount = 0;
}
}
// Enable UART0 RX and RX interrupt
UCSR0B = _BV(RXEN0) | _BV(RXCIE0);
enum { BEND_CENTER = 8192,
BEND_MAX = 16383,
};
// Enable pull-up resistor on RX pin
PORTD |= _BV(PORTD0);
struct {
int16_t bend_data;
uint16_t base_pitch[NUM_VOICES];
} pitch;
// Globally enable interrupts
sei();
}
void pitch_set_note(uint8_t voice, uint8_t note, int16_t detune) {
if ((voice >= NUM_VOICES) || (note > 127)) {
void program_change(uint8_t pgm) {
if ((pgm >= ARRAY_SIZE(programs)) || (pgm == current_program)) {
return;
}
pitch.base_pitch[voice] =
clamp_add(dac_note_pitch(voice, note), detune);
}
current_program = pgm;
void pitch_set_bend(uint16_t bend) {
float bend_factor =
((int16_t)(bend & BEND_MAX) - BEND_CENTER) / ((float)BEND_MAX);
pitch.bend_data = dac_pitch_delta(4) * bend_factor;
gate_init();
programs[current_program].init();
}
void pitch_update_clock(uint8_t delta) {
pitch_env_update_clock(delta);
void do_clock_update(void) {
static uint8_t prev_clock = 0;
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);
}
}
uint8_t clock = TCNT0;
uint8_t delta = clock - prev_clock;
prev_clock = clock;
uint16_t dac_pitch_delta(uint8_t semitones) {
if (semitones >= NUM_SEMITONES) {
semitones = NUM_SEMITONES - 1;
if (delta == 0) {
return;
}
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
uint8_t dac_command_set(uint8_t v) {
struct dac *d = dac(v);
if (!d) {
return 0;
};
return 0b00110000 | d->channel;
}
void dac_send(uint8_t command, uint16_t data) {
PORTB &= ~_BV(DAC_SYNC);
spi_io(command);
spi_io(data >> 8);
spi_io(data & 0xff);
PORTB |= _BV(DAC_SYNC);
}
void spi_init(void) {
// slave select, sck, mosi all as output
DDRB |= _BV(SS) | _BV(MOSI) | _BV(SCK);