source: buchla-emu/emu/mid.c

0.1
Last change on this file was 72b5744, checked in by Thomas Lopatic <thomas@…>, 6 years ago

Fix compiler warning.

  • Property mode set to 100644
File size: 6.4 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 // XXX - remove length calculation once RtMidi passes the length
106
107 int32_t len;
108
109 switch (midi[0] & 0xf0) {
110 case 0x80: // note on
111 case 0x90: // note off
112 case 0xa0: // polyphonic key pressure
113 case 0xb0: // control change
114 case 0xe0: // pitch bend change
115 len = 3;
116 break;
117
118 case 0xc0: // program change
119 case 0xd0: // channel pressure
120 len = 2;
121 break;
122
123 case 0xf0:
124 switch (midi[0]) {
125 case 0xf0: // system exclusive
126 case 0xf4: // undefined
127 case 0xf5: // undefined
128 case 0xf7: // end of exclusive
129 case 0xf9: // undefined
130 case 0xfd: // undefined
131 len = -1;
132 break;
133
134 case 0xf1: // MIDI time code quarter frame
135 case 0xf3: // song select
136 len = 2;
137 break;
138
139 case 0xf2: // song position pointer
140 len = 3;
141 break;
142
143 case 0xf6: // tune request
144 case 0xf8: // timing clock
145 case 0xfa: // start
146 case 0xfb: // continue
147 case 0xfc: // stop
148 case 0xfe: // active sensing
149 case 0xff: // reset
150 len = 1;
151 break;
152 }
153
154 break;
155
156 default:
157 len = 0;
158 break;
159 }
160
161 ver2("time %f, len %d", time, len);
162
163 for (int32_t i = 0; i < len; ++i) {
164 ver2("midi[%d] 0x%02x", i, midi[i]);
165 out(0, midi[i]);
166 }
167}
168
169void mid_init(void)
170{
171 ver("mid init");
172
173 mid_in = rtmidi_in_create_default();
174 mid_in->data = NULL; // XXX - remove initialization once it's added to RtMidi
175
176 if (!mid_in->ok) {
177 fail("rtmidi_in_create_default() failed: %s", mid_in->msg);
178 }
179
180 uint32_t n_ports = rtmidi_get_port_count(mid_in);
181
182 if (n_ports == 0) {
183 inf("no MIDI ports\n");
184 rtmidi_in_free(mid_in);
185 mid_in = NULL;
186 return;
187 }
188
189 if (mid_port >= n_ports) {
190 fail("invalid MIDI port %u selected (%u available)", mid_port, n_ports);
191 }
192
193 const char *name = rtmidi_get_port_name(mid_in, mid_port);
194 rtmidi_open_port(mid_in, mid_port, name);
195
196 if (!mid_in->ok) {
197 fail("error while opening MIDI port %u (%s): %s", mid_port, name, mid_in->msg);
198 }
199
200 inf("using MIDI port %u (%s)", mid_port, name);
201 free((char *)name);
202
203 rtmidi_in_set_callback(mid_in, callback, mid_in->data);
204
205 if (!mid_in->ok) {
206 fail("rtmidi_in_set_callback() failed: %s", mid_in->msg);
207 }
208}
209
210void mid_quit(void)
211{
212 ver("mid quit");
213
214 if (mid_in == NULL) {
215 return;
216 }
217
218 rtmidi_close_port(mid_in);
219 rtmidi_in_free(mid_in);
220}
221
222bool mid_exec(void)
223{
224 ver3("mid exec");
225 return state[0].irq_r || state[0].irq_t || state[1].irq_r || state[1].irq_t;
226}
227
228uint32_t mid_read(uint32_t off, int32_t sz)
229{
230 ver2("mid rd %u:%d", off, sz * 8);
231
232 if (sz != 1 || off > 7) {
233 fail("invalid mid rd %u:%d", off, sz * 8);
234 }
235
236 int32_t rg = (int32_t)(off % 4);
237 int32_t un = (int32_t)(off / 4);
238
239 uint32_t rv;
240
241 switch (rg) {
242 case REG_IER_ISR:
243 rv = (uint32_t)(0xc0 | (state[un].rdr_ok ? 0x01 : 0x00));
244 state[un].irq_r = false;
245 state[un].irq_t = false;
246 ver2("ISR[%d] 0x%02x", un, rv);
247 break;
248
249 case REG_TDR_RDR:
250 rv = state[un].rdr;
251 state[un].rdr_ok = false;
252 ver2("RDR[%d] 0x%02x", un, rv);
253 break;
254
255 default:
256 rv = 0x00;
257 break;
258 }
259
260 if (!state[un].irq_r && !state[un].rdr_ok) {
261 xmit(un);
262 }
263
264 return rv;
265}
266
267void mid_write(uint32_t off, int32_t sz, uint32_t val)
268{
269 ver2("mid wr %u:%d 0x%0*x", off, sz * 8, sz * 2, val);
270
271 if (sz != 1 || off > 7) {
272 fail("invalid mid wr %u:%d", off, sz * 8);
273 }
274
275 int32_t rg = (int32_t)(off % 4);
276 int32_t un = (int32_t)(off / 4);
277
278 switch (rg) {
279 case REG_CFR_SR:
280 ver2("CFR[%d] 0x%02x", un, val);
281
282 if (un == 1) {
283 fdd_set_side((int32_t)val & 0x01);
284 }
285 else {
286 fdd_set_sel((int32_t)val & 0x01);
287 }
288
289 break;
290
291 default:
292 break;
293 }
294}
295
296void mid_list(void)
297{
298 mid_in = rtmidi_in_create_default();
299 mid_in->data = NULL; // XXX - remove initialization once it's added to RtMidi
300
301 if (!mid_in->ok) {
302 fprintf(stderr, "rtmidi_in_create_default() failed: %s\n", mid_in->msg);
303 return;
304 }
305
306 uint32_t n_ports = rtmidi_get_port_count(mid_in);
307
308 if (n_ports == 0) {
309 fprintf(stdout, "no available MIDI ports\n");
310 }
311 else {
312 for (uint32_t i = 0; i < n_ports; i++) {
313 fprintf(stdout, "port %u: %s\n", i, rtmidi_get_port_name(mid_in, i));
314 }
315 }
316
317 rtmidi_in_free(mid_in);
318}
Note: See TracBrowser for help on using the repository browser.