...
 
Commits (4)
......@@ -18,7 +18,10 @@ The hardware consists of:
## Software
The software is work in progress. Currently it only supports
polyphonic MIDI on channel 1. No pitch bends, monophonic mode, etc.
polyphonic and unison MIDI on channel 1. No pitch bends etc.
- MIDI program 0: poly mode
- MIDI program 1: unison (use CC 14 for detune)
## License
......
......@@ -25,6 +25,11 @@ void (*note_off[NUM_PGM])(uint8_t n) = {
mono_note_off,
};
void (*control_change[NUM_PGM])(uint8_t ctl, uint8_t val) = {
poly_control_change,
mono_control_change,
};
void
io_init(void)
{
......@@ -89,7 +94,12 @@ main(void)
uint8_t note = midi_data() >> 8;
(*note_off[current_program])(note);
} else if (midi_status() == MIDI_PROGRAM_CHANGE) {
program_change(midi_data() >> 8);
uint8_t pgm = midi_data() >> 8;
program_change(pgm);
} else if (midi_status() == MIDI_CONTROL_CHANGE) {
uint8_t ctl = midi_data() >> 8;
uint8_t val = midi_data() & 0xff;
(*control_change[current_program])(ctl, val);
}
}
......
......@@ -22,7 +22,7 @@ const uint16_t dac_scale[NUM_VOICES] = {
};
// This tuning is chosen to be 'good enough' for the 8382-8384 scale range above
const uint16_t dac_semitones[12] = {
const int16_t dac_semitones[12] = {
0,
699,
1397,
......@@ -90,11 +90,28 @@ dac_init(void)
send_dac(0b01000000, 0b1111000000001111); // power down dacs 0, 1, 6, 7
}
#define DAC_MAX 65535
#define MIDI_LOWEST 24
uint16_t
safe_add(uint16_t x, int16_t delta)
{
if (delta < 0) {
uint16_t sub = -delta;
if (x < -delta) {
return 0;
}
return x - sub;
}
uint16_t add = delta;
if (UINT16_MAX - x < add) {
return UINT16_MAX;
}
return x + add;
}
void
dac_set_note(uint8_t voice, uint8_t note)
dac_set_note_detune(uint8_t voice, uint8_t note, int16_t detune)
{
if (voice >= NUM_VOICES) {
return;
......@@ -113,14 +130,17 @@ dac_set_note(uint8_t voice, uint8_t note)
return;
}
uint16_t data = dac_offset[voice] + octave * dac_scale[voice];
uint16_t semi_offset = dac_semitones[(note - MIDI_LOWEST) % 12];
if ((DAC_MAX - data) < semi_offset) {
data = DAC_MAX;
} else {
data += semi_offset;
}
uint16_t data = safe_add(
dac_offset[voice] + octave * dac_scale[voice],
dac_semitones[(note - MIDI_LOWEST) % 12]
);
data = safe_add(data, detune);
send_dac(0b00110000 | dac_chan[voice], data);
}
void
dac_set_note(uint8_t voice, uint8_t note)
{
dac_set_note_detune(voice, note, 0);
}
#ifndef dac_h
#define dac_h
#include <inttypes.h>
#include <stdint.h>
void dac_set_note(uint8_t voice, uint8_t note);
void dac_set_note_detune(uint8_t voice, uint8_t note, int16_t detune);
void dac_init(void);
#endif
#ifndef gate_h
#define gate_h
#include <inttypes.h>
#include <stdint.h>
void gate_on(uint8_t voice);
void gate_off(uint8_t voice);
......
#ifndef _list_h
#define _list_h
#include <inttypes.h>
#include <stdint.h>
struct list {
uint8_t head;
......
......@@ -115,6 +115,33 @@ test_program_change(void)
assert_status(MIDI_PROGRAM_CHANGE, 1, 0x3400);
}
void
test_control_change(void)
{
desc("test control change messages");
status_init();
assert_status(MIDI_NUM_STATUSES, 0, 0);
midi_parse(0xb0);
midi_parse(0x34);
midi_parse(0x45);
assert_status(MIDI_CONTROL_CHANGE, 1, 0x3445);
midi_parse(0x90);
midi_parse(0x60);
midi_parse(0x64);
assert_status(MIDI_NOTE_ON, 1, 0x6064);
midi_parse(0xb0);
midi_parse(0x55);
midi_parse(0x67);
assert_status(MIDI_CONTROL_CHANGE, 1, 0x5567);
midi_parse(0x71);
midi_parse(0x62);
assert_status(MIDI_CONTROL_CHANGE, 1, 0x7162);
}
int
main(int argc, char** argv)
{
......@@ -122,5 +149,7 @@ main(int argc, char** argv)
test_note_on();
test_note_off();
test_program_change();
}
test_control_change();
return 0;
}
......@@ -25,6 +25,7 @@ const uint8_t statuses[MIDI_NUM_STATUSES] = {
0b1001, // note on
0b1000, // note off
0b1100, // program change
0b1011, // control change
};
uint8_t
......
#ifndef midi_h
#define midi_h
#include <inttypes.h>
#include <stdint.h>
enum _statuses {
MIDI_NOTE_ON,
MIDI_NOTE_OFF,
MIDI_PROGRAM_CHANGE,
MIDI_CONTROL_CHANGE,
MIDI_NUM_STATUSES,
};
......
......@@ -4,6 +4,8 @@
#include "voice.h"
#include "list.h"
#include <util/delay.h>
#define NUM_NOTES 128
static uint8_t notes_array[NUM_NOTES];
static struct list notes = {
......@@ -13,10 +15,19 @@ static struct list notes = {
void set_pitch(uint8_t n);
const uint8_t detune_factor[NUM_VOICES] = {
1,
-1,
2,
-2,
};
static uint8_t detune_amount;
void
mono_init(void)
{
l_flush(&notes);
detune_amount = 0;
}
void
......@@ -38,7 +49,7 @@ void
set_pitch(uint8_t n)
{
for (uint8_t v = 0; v < NUM_VOICES; v++) {
dac_set_note(v, n);
dac_set_note_detune(v, n, (detune_factor[v] * detune_amount)/128);
}
}
......@@ -60,3 +71,17 @@ mono_note_off(uint8_t n)
set_pitch(l_first(&notes));
}
#define CC_DETUNE 14
void
mono_control_change(uint8_t ctl, uint8_t val)
{
if (ctl == CC_DETUNE) {
detune_amount = val;
if (!l_empty(&notes)) {
set_pitch(l_first(&notes));
}
}
}
#ifndef _mono_h
#define _mono_h
#include <inttypes.h>
#include <stdint.h>
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);
#endif
......@@ -35,3 +35,9 @@ poly_note_off(uint8_t note)
}
}
}
void
poly_control_change(uint8_t ctl, uint8_t val)
{
// do nothing
}
#ifndef _poly_h
#define _poly_h
#include <inttypes.h>
#include <stdint.h>
void poly_init(void);
void poly_note_on(uint8_t note);
void poly_note_off(uint8_t note);
void poly_control_change(uint8_t ctl, uint8_t val);
#endif
#ifndef voice_h
#define voice_h
#include <inttypes.h>
#include <stdint.h>
enum _voices {
VOICE1,
......