...
 
Commits (4)
...@@ -18,7 +18,10 @@ The hardware consists of: ...@@ -18,7 +18,10 @@ The hardware consists of:
## Software ## Software
The software is work in progress. Currently it only supports 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 ## License
......
...@@ -25,6 +25,11 @@ void (*note_off[NUM_PGM])(uint8_t n) = { ...@@ -25,6 +25,11 @@ void (*note_off[NUM_PGM])(uint8_t n) = {
mono_note_off, mono_note_off,
}; };
void (*control_change[NUM_PGM])(uint8_t ctl, uint8_t val) = {
poly_control_change,
mono_control_change,
};
void void
io_init(void) io_init(void)
{ {
...@@ -89,7 +94,12 @@ main(void) ...@@ -89,7 +94,12 @@ main(void)
uint8_t note = midi_data() >> 8; uint8_t note = midi_data() >> 8;
(*note_off[current_program])(note); (*note_off[current_program])(note);
} else if (midi_status() == MIDI_PROGRAM_CHANGE) { } 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] = { ...@@ -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 // 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, 0,
699, 699,
1397, 1397,
...@@ -90,11 +90,28 @@ dac_init(void) ...@@ -90,11 +90,28 @@ dac_init(void)
send_dac(0b01000000, 0b1111000000001111); // power down dacs 0, 1, 6, 7 send_dac(0b01000000, 0b1111000000001111); // power down dacs 0, 1, 6, 7
} }
#define DAC_MAX 65535
#define MIDI_LOWEST 24 #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 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) { if (voice >= NUM_VOICES) {
return; return;
...@@ -113,14 +130,17 @@ dac_set_note(uint8_t voice, uint8_t note) ...@@ -113,14 +130,17 @@ dac_set_note(uint8_t voice, uint8_t note)
return; return;
} }
uint16_t data = dac_offset[voice] + octave * dac_scale[voice]; uint16_t data = safe_add(
dac_offset[voice] + octave * dac_scale[voice],
uint16_t semi_offset = dac_semitones[(note - MIDI_LOWEST) % 12]; dac_semitones[(note - MIDI_LOWEST) % 12]
if ((DAC_MAX - data) < semi_offset) { );
data = DAC_MAX; data = safe_add(data, detune);
} else {
data += semi_offset;
}
send_dac(0b00110000 | dac_chan[voice], data); 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 #ifndef dac_h
#define 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(uint8_t voice, uint8_t note);
void dac_set_note_detune(uint8_t voice, uint8_t note, int16_t detune);
void dac_init(void); void dac_init(void);
#endif #endif
#ifndef gate_h #ifndef gate_h
#define gate_h #define gate_h
#include <inttypes.h> #include <stdint.h>
void gate_on(uint8_t voice); void gate_on(uint8_t voice);
void gate_off(uint8_t voice); void gate_off(uint8_t voice);
......
#ifndef _list_h #ifndef _list_h
#define _list_h #define _list_h
#include <inttypes.h> #include <stdint.h>
struct list { struct list {
uint8_t head; uint8_t head;
......
...@@ -115,6 +115,33 @@ test_program_change(void) ...@@ -115,6 +115,33 @@ test_program_change(void)
assert_status(MIDI_PROGRAM_CHANGE, 1, 0x3400); 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 int
main(int argc, char** argv) main(int argc, char** argv)
{ {
...@@ -122,5 +149,7 @@ main(int argc, char** argv) ...@@ -122,5 +149,7 @@ main(int argc, char** argv)
test_note_on(); test_note_on();
test_note_off(); test_note_off();
test_program_change(); test_program_change();
} test_control_change();
return 0;
}
...@@ -25,6 +25,7 @@ const uint8_t statuses[MIDI_NUM_STATUSES] = { ...@@ -25,6 +25,7 @@ const uint8_t statuses[MIDI_NUM_STATUSES] = {
0b1001, // note on 0b1001, // note on
0b1000, // note off 0b1000, // note off
0b1100, // program change 0b1100, // program change
0b1011, // control change
}; };
uint8_t uint8_t
......
#ifndef midi_h #ifndef midi_h
#define midi_h #define midi_h
#include <inttypes.h> #include <stdint.h>
enum _statuses { enum _statuses {
MIDI_NOTE_ON, MIDI_NOTE_ON,
MIDI_NOTE_OFF, MIDI_NOTE_OFF,
MIDI_PROGRAM_CHANGE, MIDI_PROGRAM_CHANGE,
MIDI_CONTROL_CHANGE,
MIDI_NUM_STATUSES, MIDI_NUM_STATUSES,
}; };
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "voice.h" #include "voice.h"
#include "list.h" #include "list.h"
#include <util/delay.h>
#define NUM_NOTES 128 #define NUM_NOTES 128
static uint8_t notes_array[NUM_NOTES]; static uint8_t notes_array[NUM_NOTES];
static struct list notes = { static struct list notes = {
...@@ -13,10 +15,19 @@ static struct list notes = { ...@@ -13,10 +15,19 @@ static struct list notes = {
void set_pitch(uint8_t n); void set_pitch(uint8_t n);
const uint8_t detune_factor[NUM_VOICES] = {
1,
-1,
2,
-2,
};
static uint8_t detune_amount;
void void
mono_init(void) mono_init(void)
{ {
l_flush(&notes); l_flush(&notes);
detune_amount = 0;
} }
void void
...@@ -38,7 +49,7 @@ void ...@@ -38,7 +49,7 @@ void
set_pitch(uint8_t n) set_pitch(uint8_t n)
{ {
for (uint8_t v = 0; v < NUM_VOICES; v++) { 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) ...@@ -60,3 +71,17 @@ mono_note_off(uint8_t n)
set_pitch(l_first(&notes)); 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 #ifndef _mono_h
#define _mono_h #define _mono_h
#include <inttypes.h> #include <stdint.h>
void mono_init(void); void mono_init(void);
void mono_note_on(uint8_t note); void mono_note_on(uint8_t note);
void mono_note_off(uint8_t note); void mono_note_off(uint8_t note);
void mono_control_change(uint8_t ctl, uint8_t val);
#endif #endif
...@@ -35,3 +35,9 @@ poly_note_off(uint8_t note) ...@@ -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 #ifndef _poly_h
#define _poly_h #define _poly_h
#include <inttypes.h> #include <stdint.h>
void poly_init(void); void poly_init(void);
void poly_note_on(uint8_t note); void poly_note_on(uint8_t note);
void poly_note_off(uint8_t note); void poly_note_off(uint8_t note);
void poly_control_change(uint8_t ctl, uint8_t val);
#endif #endif
#ifndef voice_h #ifndef voice_h
#define voice_h #define voice_h
#include <inttypes.h> #include <stdint.h>
enum _voices { enum _voices {
VOICE1, VOICE1,
......