forked from pure-data/pure-data
/
ceammc_convert_pitch.cpp
172 lines (157 loc) · 4.18 KB
/
ceammc_convert_pitch.cpp
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include "ceammc_convert.h"
#include "ceammc_log.h"
#include <cstdint>
#include <cstring>
class SPN {
public:
enum State {
ST_INIT,
ST_NOTE,
ST_REST,
ST_ALT_SHARP,
ST_ALT_FLAT,
ST_OCTAVE,
ST_DONE,
ST_ERROR
};
private:
State st_;
uint8_t note_;
int8_t alt_;
uint8_t oct_;
char err_[64];
static const int defOct = 4;
public:
SPN()
: st_(ST_INIT)
, note_(0)
, alt_(0)
, oct_(0)
{
}
State state() const { return st_; }
const char* error() const { return err_; }
void reset()
{
st_ = ST_INIT;
note_ = 0;
alt_ = 0;
oct_ = 0;
err_[0] = '\0';
}
bool isNote() const { return note_ != 0 && !isRest(); }
bool isRest() const { return note_ == 'R'; }
int midi() const { return ((oct_ + 1) * 12 + midiNote(note_)) + alt_; }
State setState(State st, const char* msg = nullptr)
{
st_ = st;
if (msg) {
strncpy(err_, msg, sizeof(err_));
err_[sizeof(err_) - 1] = '\0';
}
return st;
}
State put(char c)
{
switch (st_) {
case ST_INIT: {
if (c >= 'A' && c <= 'G') {
note_ = c;
return setState(ST_NOTE);
} else if (c == 'R') {
note_ = c;
return setState(ST_REST);
} else {
return setState(ST_ERROR, "Note name expected");
}
} break;
case ST_REST: {
if (c == '\0')
return setState(ST_DONE);
else
return setState(ST_ERROR, "extra character after Rest");
} break;
case ST_NOTE: {
if (c == '#') {
alt_++;
return setState(ST_ALT_SHARP);
} else if (c == 'b') {
alt_--;
return setState(ST_ALT_FLAT);
} else if (isdigit(c)) {
oct_ = c - '0';
return setState(ST_OCTAVE);
} else if (c == '\0') {
oct_ = defOct;
return setState(ST_DONE);
} else {
return setState(ST_ERROR, "Alteration or Octave expected");
}
} break;
case ST_ALT_FLAT: {
if (c == 'b') {
alt_--;
return setState(ST_ALT_FLAT);
} else if (isdigit(c)) {
oct_ = c - '0';
return setState(ST_OCTAVE);
} else if (c == '\0') {
oct_ = defOct;
return setState(ST_DONE);
} else {
return setState(ST_ERROR, "Flat or Octave expected");
}
} break;
case ST_ALT_SHARP: {
if (c == '#') {
alt_++;
return setState(ST_ALT_SHARP);
} else if (isdigit(c)) {
oct_ = c - '0';
return setState(ST_OCTAVE);
} else if (c == '\0') {
oct_ = defOct;
return setState(ST_DONE);
} else {
return setState(ST_ERROR, "Sharp or Octave expected");
}
} break;
case ST_OCTAVE: {
if (c == '\0') {
return setState(ST_DONE);
} else {
return setState(ST_ERROR, "extra characters after OCTAVE");
}
} break;
default:
return setState(ST_ERROR, "extra characters");
}
}
static int midiNote(uint8_t n)
{
auto deg = (n >= 'C') ? (n - 'C') : (n - 'A' + 5);
return 2 * deg - (deg > 2);
}
};
static SPN spn;
int ceammc::convert::spn2midi(const char* p)
{
const char* pch = p;
spn.reset();
int guard = 0;
while (true) {
switch (spn.put(*pch)) {
case SPN::ST_ERROR:
LIB_ERR << "SPN parse: " << spn.error();
return MIDI_NONE;
case SPN::ST_DONE:
return spn.isRest() ? MIDI_REST : spn.midi();
default:
++pch;
}
if (++guard > 16) {
LIB_ERR << "SPN parse loop";
return MIDI_NONE;
}
}
}