source: buchla-emu/emu/mid.c@ 9ddbf3e

Last change on this file since 9ddbf3e was 9ddbf3e, checked in by Thomas Lopatic <thomas@…>, 6 years ago

Code review changes.

  • Property mode set to 100644
File size: 6.2 KB
Line 
1/*
2 * Copyright (C) 2017-2018 The Contributors
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or (at
7 * your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * A copy of the GNU General Public License can be found in the file
15 * "gpl.txt" in the top directory of this repository.
16 */
17
18#include <all.h>
19
20#define ver(...) _ver(mid_verbose, 0, __VA_ARGS__)
21#define ver2(...) _ver(mid_verbose, 1, __VA_ARGS__)
22#define ver3(...) _ver(mid_verbose, 2, __VA_ARGS__)
23
24int32_t mid_verbose = 0;
25
26#define REG_IER_ISR 0
27#define REG_CFR_SR 1
28#define REG_CDR_TBR 2
29#define REG_TDR_RDR 3
30
31#define BUF_SZ 256
32
33typedef struct {
34 int32_t buf_hd;
35 int32_t buf_tl;
36 uint8_t buf[BUF_SZ];
37 bool irq_r;
38 bool irq_t;
39 bool rdr_ok;
40 uint8_t rdr;
41} state_t;
42
43static state_t state[] = {
44 { .buf_hd = 0, .buf_tl = 0, .irq_r = false, .irq_t = false, .rdr_ok = false, .rdr = 0x00 },
45 { .buf_hd = 0, .buf_tl = 0, .irq_r = false, .irq_t = false, .rdr_ok = false, .rdr = 0x00 }
46};
47
48static struct RtMidiWrapper *mid_in = NULL;
49
50static void xmit(int32_t un)
51{
52 int32_t i = state[un].buf_tl;
53 ver2("mid xmit %d %d", i, state[un].buf_hd);
54
55 if (i >= state[un].buf_hd) {
56 return;
57 }
58
59 uint8_t byte = state[un].buf[i % BUF_SZ];
60 ver2("mid xmit 0x%02x", byte);
61
62 state[un].rdr = byte;
63 state[un].rdr_ok = true;
64 state[un].irq_r = true;
65
66 state[un].buf_tl = i + 1;
67
68 if (state[un].buf_tl >= BUF_SZ) {
69 state[un].buf_hd -= BUF_SZ;
70 state[un].buf_tl -= BUF_SZ;
71 ver2("mid adj %d %d", state[un].buf_tl, state[un].buf_hd);
72 }
73}
74
75static void out(int32_t un, uint8_t c)
76{
77 if (SDL_LockMutex(cpu_mutex) < 0) {
78 fail("SDL_LockMutex() failed: %s", SDL_GetError());
79 }
80
81 int32_t i = state[un].buf_hd;
82 ver2("mid out %d %d 0x%02x", state[un].buf_tl, i, c);
83
84 if (i >= state[un].buf_tl + BUF_SZ) {
85 err("midi port %d losing data", un);
86 return;
87 }
88
89 state[un].buf[i % BUF_SZ] = c;
90 state[un].buf_hd = i + 1;
91
92 if (!state[un].irq_r && !state[un].rdr_ok) {
93 xmit(un);
94 }
95
96 if (SDL_UnlockMutex(cpu_mutex) < 0) {
97 fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
98 }
99}
100
101static void callback(double time, const uint8_t *midi, void *data)
102{
103 (void)data;
104
105 int32_t len;
106
107 switch (midi[0] & 0xf0) {
108 case 0x80: // note on
109 case 0x90: // note off
110 case 0xa0: // polyphonic key pressure
111 case 0xb0: // control change
112 case 0xe0: // pitch bend change
113 len = 3;
114 break;
115
116 case 0xc0: // program change
117 case 0xd0: // channel pressure
118 len = 2;
119 break;
120
121 case 0xf0:
122 switch (midi[0]) {
123 case 0xf0: // system exclusive
124 case 0xf4: // undefined
125 case 0xf5: // undefined
126 case 0xf7: // end of exclusive
127 case 0xf9: // undefined
128 case 0xfd: // undefined
129 len = -1;
130 break;
131
132 case 0xf1: // MIDI time code quarter frame
133 case 0xf3: // song select
134 len = 2;
135 break;
136
137 case 0xf2: // song position pointer
138 len = 3;
139 break;
140
141 case 0xf6: // tune request
142 case 0xf8: // timing clock
143 case 0xfa: // start
144 case 0xfb: // continue
145 case 0xfc: // stop
146 case 0xfe: // active sensing
147 case 0xff: // reset
148 len = 1;
149 break;
150 }
151
152 break;
153 }
154
155 ver2("time %f", time);
156
157 for (int32_t i = 0; i < len; ++i) {
158 ver2("midi[%d] 0x%02x", i, midi[i]);
159 out(0, midi[i]);
160 }
161}
162
163void mid_init(void)
164{
165 ver("mid init");
166
167 if (mid_port < 0) {
168 ver("no MIDI requested");
169 return;
170 }
171
172 mid_in = rtmidi_in_create_default();
173
174 if (!mid_in->ok) {
175 fail("rtmidi_in_create_default() failed: %s", mid_in->msg);
176 }
177
178 int32_t n_ports = (int32_t)rtmidi_get_port_count(mid_in);
179
180 if (n_ports == 0) {
181 inf("no MIDI ports\n");
182 rtmidi_in_free(mid_in);
183 mid_in = NULL;
184 return;
185 }
186
187 if (mid_port >= n_ports) {
188 fail("invalid MIDI port %d selected (%d available)", mid_port, n_ports);
189 }
190
191 const char *name = rtmidi_get_port_name(mid_in, (uint32_t)mid_port);
192 rtmidi_open_port(mid_in, (uint32_t)mid_port, name);
193
194 if (!mid_in->ok) {
195 fail("error while opening MIDI port %d (%s): %s", mid_port, name, mid_in->msg);
196 }
197
198 inf("using MIDI port %d (%s)", mid_port, name);
199
200 rtmidi_in_set_callback(mid_in, callback, mid_in->data);
201
202 if (!mid_in->ok) {
203 fail("rtmidi_in_set_callback() failed: %s", mid_in->msg);
204 }
205}
206
207void mid_quit(void)
208{
209 ver("mid quit");
210
211 if (mid_in == NULL) {
212 return;
213 }
214
215 rtmidi_close_port(mid_in);
216 rtmidi_in_free(mid_in);
217}
218
219bool mid_exec(void)
220{
221 ver3("mid exec");
222 return state[0].irq_r || state[0].irq_t || state[1].irq_r || state[1].irq_t;
223}
224
225uint32_t mid_read(uint32_t off, int32_t sz)
226{
227 ver2("mid rd %u:%d", off, sz * 8);
228
229 if (sz != 1 || off > 7) {
230 fail("invalid mid rd %u:%d", off, sz * 8);
231 }
232
233 int32_t rg = (int32_t)(off % 4);
234 int32_t un = (int32_t)(off / 4);
235
236 uint32_t rv;
237
238 switch (rg) {
239 case REG_IER_ISR:
240 rv = (uint32_t)(0xc0 | (state[un].rdr_ok ? 0x01 : 0x00));
241 state[un].irq_r = false;
242 state[un].irq_t = false;
243 ver2("ISR[%d] 0x%02x", un, rv);
244 break;
245
246 case REG_TDR_RDR:
247 rv = state[un].rdr;
248 state[un].rdr_ok = false;
249 ver2("RDR[%d] 0x%02x", un, rv);
250 break;
251
252 default:
253 rv = 0x00;
254 break;
255 }
256
257 if (!state[un].irq_r && !state[un].rdr_ok) {
258 xmit(un);
259 }
260
261 return rv;
262}
263
264void mid_write(uint32_t off, int32_t sz, uint32_t val)
265{
266 ver2("mid wr %u:%d 0x%0*x", off, sz * 8, sz * 2, val);
267
268 if (sz != 1 || off > 7) {
269 fail("invalid mid wr %u:%d", off, sz * 8);
270 }
271
272 int32_t rg = (int32_t)(off % 4);
273 int32_t un = (int32_t)(off / 4);
274
275 switch (rg) {
276 case REG_CFR_SR:
277 ver2("CFR[%d] 0x%02x", un, val);
278
279 if (un == 1) {
280 fdd_set_side((int32_t)val & 0x01);
281 }
282 else {
283 fdd_set_sel((int32_t)val & 0x01);
284 }
285
286 break;
287
288 default:
289 break;
290 }
291}
292
293void mid_list(void)
294{
295 mid_in = rtmidi_in_create_default();
296
297 if (!mid_in->ok) {
298 fprintf(stderr, "rtmidi_in_create_default() failed: %s\n", mid_in->msg);
299 return;
300 }
301
302 uint32_t n_ports = rtmidi_get_port_count(mid_in);
303
304 if (n_ports == 0) {
305 fprintf(stdout, "no available MIDI ports\n");
306 }
307 else {
308 for (uint32_t i = 0; i < n_ports; i++) {
309 fprintf(stdout, "port %u: %s\n", i, rtmidi_get_port_name(mid_in, i));
310 }
311 }
312
313 rtmidi_in_free(mid_in);
314}
Note: See TracBrowser for help on using the repository browser.