...
 
Commits (3)
PRG = crowbx
OBJ = crowbx.o midi.o dac.o voice.o uart.o gate.o poly.o mono.o
OBJ = crowbx.o midi.o dac.o voice.o uart.o gate.o poly.o mono.o queue.o list.o
MCU_TARGET = atmega328p
#MCU_TARGET = at90s2313
#MCU_TARGET = at90s2333
......@@ -80,6 +80,8 @@ TESTCC = gcc -Wall -std=c99
test:
$(TESTCC) -o midi-test midi-test.c midi.c
./midi-test
$(TESTCC) -o list-test list-test.c list.c
./list-test
clean:
rm -rf *.o $(PRG).elf *.eps *.png *.pdf *.bak
......
File added
#include <stdio.h>
#include "test.h"
#include "list.h"
#define TEST_ARRAY_SIZE 10
uint8_t test_array[TEST_ARRAY_SIZE];
struct list l = {
.array = test_array,
.sup = TEST_ARRAY_SIZE,
};
void
test_flush(void)
{
desc("test l_flush()");
l_flush(&l);
assert(l.head == l.sup);
assert(l.sup == TEST_ARRAY_SIZE);
}
void
test_empty(void)
{
desc("test l_empty()");
l_flush(&l);
assert(l_empty(&l));
l_push(&l, 5);
assert(!l_empty(&l));
l_delete(&l, 5);
assert(l_empty(&l));
}
void
test_push(void)
{
desc("test l_push(), l_first(), l_last()");
l_flush(&l);
l_push(&l, 2);
l_push(&l, 5);
l_push(&l, 3);
assert(l_first(&l) == 3);
assert(l_last(&l) == 2);
l_delete(&l, 5);
assert(l_first(&l) == 3);
assert(l_last(&l) == 2);
l_delete(&l, 2);
assert(l_first(&l) == 3);
assert(l_last(&l) == 3);
l_push(&l, 1);
l_push(&l, 9);
l_push(&l, 9);
assert(l_first(&l) == 9);
l_delete(&l, 9);
assert(l_first(&l) == 1);
assert(l_last(&l) == 3);
l_delete(&l, 3);
assert(l_first(&l) == 1);
assert(l_last(&l) == 1);
l_delete(&l, 1);
assert(l_empty(&l));
}
int
main(int argc, char** argv)
{
test_flush();
test_empty();
test_push();
return 0;
}
#include "list.h"
uint8_t
succ(struct list *l, uint8_t x)
{
return l->array[x];
}
void
l_push(struct list *l, uint8_t x)
{
if (x >= l->sup) {
return;
}
l_delete(l, x);
l->array[x] = l->head;
l->head = x;
}
void
l_delete(struct list *l, uint8_t x)
{
if (l_empty(l) || x >= l->sup) {
return;
}
if (l->head == x) {
l->head = succ(l, x);
return;
}
for (uint8_t i = l->head; i != l->sup; i = succ(l, i)) {
if (succ(l, i) == x) {
l->array[i] = succ(l, x);
return;
}
}
}
uint8_t
l_first(struct list *l)
{
return l->head;
}
uint8_t
l_last(struct list *l)
{
if (l_empty(l)) {
return l->sup;
}
uint8_t x = l->head;
for (; succ(l, x) != l->sup; x = succ(l, x)) {
// walk the list
}
return x;
}
void
l_flush(struct list *l)
{
l->head = l->sup;
}
uint8_t
l_empty(struct list *l)
{
return l->head == l->sup;
}
#ifndef _list_h
#define _list_h
#include <inttypes.h>
struct list {
uint8_t head;
uint8_t *array;
uint8_t sup;
};
void l_push(struct list *l, uint8_t x);
void l_delete(struct list *l, uint8_t x);
uint8_t l_first(struct list *l);
uint8_t l_last(struct list *l);
void l_flush(struct list *l);
uint8_t l_empty(struct list *l);
#endif
#ifndef midi_test_h
#define midi_test_h
#include "test.h"
// Access to midi.c internals
void status_init(void);
void midi_parse(uint8_t);
uint8_t midi_available(void);
#define desc(s) printf("TEST: %s\n", s)
#define assert_status(status, avail, data) assert(midi_status() == status); \
assert(!midi_available() == !avail); \
if (avail) assert(midi_data() == data)
......
......@@ -2,26 +2,21 @@
#include "dac.h"
#include "gate.h"
#include "voice.h"
uint8_t current_note;
#include "list.h"
#define NUM_NOTES 128
uint8_t notes[NUM_NOTES];
uint8_t playing_note;
uint8_t notes_array[NUM_NOTES];
struct list notes = {
.array = notes_array,
.sup = NUM_NOTES,
};
void set_pitch(uint8_t n);
void
mono_init(void)
{
playing_note = NUM_NOTES;
}
void
push_note(uint8_t n)
{
notes[n] = playing_note;
playing_note = n;
l_flush(&notes);
}
void
......@@ -31,7 +26,7 @@ mono_note_on(uint8_t n)
return;
}
push_note(n);
l_push(&notes, n);
set_pitch(n);
for (uint8_t v = 0; v < NUM_VOICES; v++) {
......@@ -39,22 +34,6 @@ mono_note_on(uint8_t n)
}
}
void
drop_note(uint8_t n)
{
if (playing_note == n) {
playing_note = notes[playing_note];
return;
}
for (uint8_t i = playing_note; notes[i] != NUM_NOTES; i = notes[i]) {
if (notes[i] == n) {
notes[i] = notes[n];
return;
}
}
}
void
set_pitch(uint8_t n)
{
......@@ -70,14 +49,14 @@ mono_note_off(uint8_t n)
return;
}
drop_note(n);
l_delete(&notes, n);
if (playing_note == NUM_NOTES) {
if (l_empty(&notes)) {
for (uint8_t v = 0; v < NUM_VOICES; v++) {
gate_off(v);
}
return;
}
set_pitch(playing_note);
set_pitch(l_first(&notes));
}
#include "queue.h"
uint8_t
q_advance(struct queue *q, uint8_t i)
{
return (i + 1) % (q->size + 1);
}
uint8_t
q_full(struct queue *q)
{
return q_advance(q, q->tail) == q->head;
}
void
q_enqueue(struct queue *q, uint8_t x)
{
if (q_full(q)) {
return;
}
q->array[q->tail] = x;
q->tail = q_advance(q, q->tail);
}
uint8_t
q_empty(struct queue *q)
{
return q->tail == q->head;
}
uint8_t
q_dequeue(struct queue *q)
{
if (q_empty(q)) {
return q->size;
}
uint8_t x = q->array[q->head];
q->head = q_advance(q, q->head);
return x;
}
void
q_flush(struct queue *q)
{
q->head = 0;
q->tail = 0;
}
#ifndef _queue_h
#define _queue_h
#include <inttypes.h>
struct queue {
uint8_t head;
uint8_t tail;
uint8_t *array;
uint8_t size;
};
uint8_t q_full(struct queue *q);
void q_enqueue(struct queue *q, uint8_t x);
uint8_t q_empty(struct queue *q);
uint8_t q_dequeue(struct queue *q);
void q_flush(struct queue *q);
#endif
#ifndef _test_h
#define _test_h
#include <assert.h>
#define desc(s) printf("TEST: %s\n", s)
#endif
#include "voice.h"
#include "queue.h"
#include "list.h"
// The voice assigner moves voices between the 'avail' (available) queue
// and the 'playing' list. The 'avail' data structure is a queue to get a
......@@ -38,50 +40,29 @@
// avail is a fixed-size queue implemented with an array and two
// pointers. It is used to track which voices are available (i.e. not
// 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;
uint8_t avail_tail;
uint8_t
avail_advance(uint8_t i)
{
return (i + 1) % AVAIL_SIZE;
}
uint8_t
avail_is_full(void)
{
return avail_advance(avail_tail) == avail_head;
}
uint8_t avail_array[NUM_VOICES + 1];
struct queue avail = {
.array = avail_array,
.size = NUM_VOICES,
};
void
avail_enqueue(uint8_t v)
{
if (avail_is_full()) {
return;
}
avail[avail_tail] = v;
avail_tail = avail_advance(avail_tail);
q_enqueue(&avail, v);
}
uint8_t
avail_is_empty(void)
{
return avail_tail == avail_head;
return q_empty(&avail);
}
uint8_t
avail_dequeue(void)
{
if (avail_is_empty()) {
return NUM_VOICES;
}
uint8_t v = avail[avail_head];
avail_head = avail_advance(avail_head);
return v;
return q_dequeue(&avail);
}
// playing is an ordered set implemented as a sort of bounded singly linked
......@@ -89,8 +70,12 @@ avail_dequeue(void)
// list aspect is used to be able to determine the oldest playing note,
// 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;
uint8_t playing_array[NUM_VOICES];
struct list playing = {
.array = playing_array,
.sup = NUM_VOICES,
};
// 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,
......@@ -108,34 +93,19 @@ uint8_t playing_head;
void
playing_play(uint8_t v)
{
playing[v] = playing_head;
playing_head = v;
l_push(&playing, v);
}
void
playing_release(uint8_t v)
{
if (playing_head == v) {
playing_head = playing[v];
return;
}
for (uint8_t i = playing_head; playing[i] != NUM_VOICES; i = playing[i]) {
if (playing[i] == v) {
playing[i] = playing[v];
return;
}
}
l_delete(&playing, v);
}
uint8_t
playing_oldest(void)
{
uint8_t i = playing_head;
for (; playing[i] != NUM_VOICES; i = playing[i]) {
// walk the list
}
return i;
return l_last(&playing);
}
// notes maps voices to the MIDI note they are currently playing, if they
......@@ -145,13 +115,12 @@ uint8_t notes[NUM_VOICES];
void
voice_init(void)
{
avail_head = 0;
avail_tail = 0;
q_flush(&avail);
for (uint8_t i = 0; i < NUM_VOICES; i++) {
avail_enqueue(i);
}
playing_head = NUM_VOICES;
l_flush(&playing);
}
// Release the oldest playing voice, regardless of what note it is playing.
......@@ -190,7 +159,7 @@ voice_release(uint8_t note)
{
// Find oldest voice playing this note
uint8_t v = NUM_VOICES;
for (uint8_t i = playing_head; i != NUM_VOICES; i = playing[i]) {
for (uint8_t i = playing.head; i != playing.sup; i = playing.array[i]) {
if (notes[i] == note) {
v = i;
}
......