...
 
Commits (4)
PRG = crowbx
OBJ = crowbx.o midi.o dac.o voice.o uart.o
OBJ = crowbx.o midi.o dac.o voice.o uart.o gate.o poly.o
MCU_TARGET = atmega328p
#MCU_TARGET = at90s2313
#MCU_TARGET = at90s2333
......
#include <avr/io.h>
#include <util/delay.h>
#include "dac.h"
#include "midi.h"
#include "voice.h"
#include "dac.h"
void
gate_on(uint8_t v)
{
if (v > NUM_VOICES) {
return;
}
PORTC |= _BV(v);
}
void
gate_off(uint8_t v)
{
if (v > NUM_VOICES) {
return;
}
PORTC &= ~_BV(v);
}
#include "poly.h"
void
io_init(void)
......@@ -38,31 +19,6 @@ io_init(void)
DDRD = _BV(PORTD1) | _BV(PORTD2) | _BV(PORTD3) | _BV(PORTD4) | _BV(PORTD5) | _BV(PORTD6) | _BV(PORTD7);
}
void
note_on(uint8_t note)
{
uint8_t v = voice_acquire(note);
if (v == NUM_VOICES) {
// This should never happen if the voice assigner is working correctly.
return;
}
dac_set_note(v, note);
gate_on(v);
}
void
note_off(uint8_t note)
{
uint8_t v = voice_release(note);
if (v == NUM_VOICES) {
// This can happen if we get a spurious note off message from MIDI
return;
}
gate_off(v);
}
int
main(void)
{
......@@ -81,13 +37,13 @@ main(void)
uint8_t velocity = midi_data() & 0xff;
if (velocity > 0) {
note_on(note);
poly_note_on(note);
} else {
note_off(note);
poly_note_off(note);
}
} else if (midi_status() == MIDI_NOTE_OFF) {
uint8_t note = midi_data() >> 8;
note_off(note);
poly_note_off(note);
}
}
......
#ifndef dac_h
#define dac_h
#include <inttypes.h>
void dac_set_note(uint8_t voice, uint8_t note);
void dac_init(void);
......
......@@ -89,11 +89,38 @@ test_note_off(void)
assert_status(MIDI_NOTE_OFF, 1, 0x1200);
}
void
test_program_change(void)
{
desc("test program change messages");
status_init();
assert_status(MIDI_NUM_STATUSES, 0, 0);
midi_parse(0xc0);
midi_parse(0x12);
assert_status(MIDI_PROGRAM_CHANGE, 1, 0x1200);
// a little silly, running status for program change messages
midi_parse(0);
assert_status(MIDI_PROGRAM_CHANGE, 1, 0);
// throw in a different message
midi_parse(0x80); // note off
midi_parse(0x12);
midi_parse(0);
assert_status(MIDI_NOTE_OFF, 1, 0x1200);
midi_parse(0xc0);
midi_parse(0x34);
assert_status(MIDI_PROGRAM_CHANGE, 1, 0x3400);
}
int
main(int argc, char** argv)
{
test_init();
test_note_on();
test_note_off();
test_program_change();
}
......@@ -24,12 +24,16 @@ uint8_t parsed_len;
const uint8_t statuses[MIDI_NUM_STATUSES] = {
0b1001, // note on
0b1000, // note off
0b1100, // program change
};
uint8_t
data_len(uint8_t st)
{
// Some channel messages have only 1 data byte, but we don't support any of those yet.
if (st == MIDI_PROGRAM_CHANGE) {
return 1;
}
return 2;
}
......@@ -89,7 +93,7 @@ midi_parse(uint8_t midi_byte)
return;
}
if (parsed_len >= 2) {
if (parsed_len >= data_len(status)) {
clear_data();
}
......
......@@ -6,6 +6,7 @@
enum _statuses {
MIDI_NOTE_ON,
MIDI_NOTE_OFF,
MIDI_PROGRAM_CHANGE,
MIDI_NUM_STATUSES,
};
......
......@@ -40,8 +40,8 @@
// playing). It is a queue in order to get 'rotating' voice assignment.
#define AVAIL_SIZE (NUM_VOICES + 1)
uint8_t avail[AVAIL_SIZE];
uint8_t avail_head = 0;
uint8_t avail_tail = 0;
uint8_t avail_head;
uint8_t avail_tail;
uint8_t
avail_advance(uint8_t i)
......@@ -90,7 +90,7 @@ avail_dequeue(void)
// and to be able to remove voices from anywhere in the list. A voice may
// only occur once in the list.
uint8_t playing[NUM_VOICES];
uint8_t playing_head = NUM_VOICES;
uint8_t playing_head;
// playing_head is a number between 0 and NUM_VOICES. if playing_head < NUM_VOICES it
// indicates the last voice that started playing. If playing_head == NUM_VOICES,
......@@ -145,9 +145,13 @@ uint8_t notes[NUM_VOICES];
void
voice_init(void)
{
avail_head = 0;
avail_tail = 0;
for (uint8_t i = 0; i < NUM_VOICES; i++) {
avail_enqueue(i);
}
playing_head = NUM_VOICES;
}
// Release the oldest playing voice, regardless of what note it is playing.
......