-
Notifications
You must be signed in to change notification settings - Fork 0
/
midi.hpp
147 lines (128 loc) · 4.23 KB
/
midi.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
* midi_message.h
*/
#ifndef MIDI_MESSAGE_HPP_
#define MIDI_MESSAGE_HPP_
#include <avr/io.h>
#include "eeprom.hpp"
#include "sequencer.hpp"
#include "system.hpp"
// serial interface setup
static constexpr uint32_t USART_BAUD_RATE = 31250;
static constexpr uint8_t USART_BUF_SIZE = 16; // must be power of 2
static constexpr uint16_t USART_BAUD_SELECT = (F_OSC / (USART_BAUD_RATE * 16) - 1);
// MIDI messages
static constexpr uint8_t MIDI_NOTE_OFF = 0x80;
static constexpr uint8_t MIDI_NOTE_ON = 0x90;
static constexpr uint8_t MIDI_CONTROL_CHANGE = 0xb0;
static constexpr uint8_t MIDI_PROGRAM_CHANGE = 0xc0;
static constexpr uint8_t MIDI_CHANNEL_PRESSURE = 0xd0;
static constexpr uint8_t MIDI_REALTIME_START = 0xfa;
static constexpr uint8_t MIDI_REALTIME_STOP = 0xfc;
static constexpr uint8_t MIDI_REALTIME_CLOCK = 0xf8;
static constexpr uint8_t MIDI_NOTE_BASS_DRUM = 36; // C1
static constexpr uint8_t MIDI_NOTE_SNARE_DRUM = 38; // D1
static constexpr uint8_t MIDI_NOTE_RIM_SHOT = 37; // C#1
static constexpr uint8_t MIDI_NOTE_HAND_CLAP = 39; // D#1
static constexpr uint8_t MIDI_NOTE_CLOSED_HI_HAT = 42; // F#1
static constexpr uint8_t MIDI_NOTE_OPEN_HI_HAT = 46; // A#1
class MidiReceiver {
private:
uint8_t status_ = 0;
uint8_t current_channel_ = 0;
uint8_t data_[2] = {0, 0};
uint8_t data_length_mask_ = 0;
uint8_t data_pointer_ = 0;
uint8_t listening_channel_ = 0;
public:
MidiReceiver() {}
void Initialize();
inline uint8_t GetChannel() const { return listening_channel_; }
inline void SetChannel(uint8_t channel) { listening_channel_ = channel; }
void ParseMidiInput(uint8_t next_byte) {
if (next_byte >= 0xf0) {
switch (next_byte) {
case MIDI_REALTIME_START:
if (!g_sequencer.IsPlaying()) {
g_sequencer.StartRecording();
}
break;
case MIDI_REALTIME_CLOCK:
if (!g_sequencer.IsPlaying()) {
g_sequencer.StepForwardRecording();
}
break;
case MIDI_REALTIME_STOP:
if (!g_sequencer.IsPlaying()) {
g_sequencer.EndRecording();
}
break;
}
return;
}
if (next_byte > 0x7f) { // is status byte
status_ = next_byte & 0xf0;
current_channel_ = next_byte & 0x0f;
switch (status_) {
case MIDI_PROGRAM_CHANGE:
case MIDI_CHANNEL_PRESSURE:
data_length_mask_ = 0;
break;
default:
data_length_mask_ = 1;
}
data_pointer_ = 0;
} else { // data byte
data_[data_pointer_ & 0x1] = next_byte;
if ((++data_pointer_ & data_length_mask_) == 0) {
if (current_channel_ == listening_channel_) {
ProcessMidiChannelMessage();
}
}
}
}
void ProcessMidiChannelMessage() {
switch (status_) {
case MIDI_NOTE_ON: {
auto& velocity = data_[1];
if (velocity == 0) {
// this is actually a note-off message
break;
}
auto& note = data_[0];
switch (note) {
case MIDI_NOTE_BASS_DRUM:
g_sequencer.Trigger<Drum::kBassDrum, true>(velocity);
break;
case MIDI_NOTE_SNARE_DRUM:
g_sequencer.Trigger<Drum::kSnareDrum, true>(velocity);
break;
case MIDI_NOTE_RIM_SHOT:
g_sequencer.Trigger<Drum::kRimShot, true>(velocity);
break;
case MIDI_NOTE_HAND_CLAP:
g_sequencer.Trigger<Drum::kHandClap, true>(velocity);
break;
case MIDI_NOTE_CLOSED_HI_HAT:
g_sequencer.Trigger<Drum::kClosedHiHat, true>(velocity);
break;
case MIDI_NOTE_OPEN_HI_HAT:
g_sequencer.Trigger<Drum::kOpenHiHat, true>(velocity);
break;
}
} break;
}
}
};
extern MidiReceiver g_midi_receiver;
void MidiReceiver::Initialize() {
// Set baud rate
UBRR1H = static_cast<uint8_t>((USART_BAUD_SELECT >> 8) & 0xff);
UBRR1L = static_cast<uint8_t>(USART_BAUD_SELECT & 0xff);
// Enable receiver
UCSR1B = _BV(RXEN1);
// Set frame format: asynchronous operation, parity disabled, 8 data, 1 stop bit */
UCSR1C = _BV(UCSZ11) | _BV(UCSZ10);
SetChannel(eeprom_read_byte(E_MIDI_CH));
}
#endif /* MIDI_MESSAGE_HPP_ */