source: buchla-emu/emu/mid.c@ 25c3737

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

Updated Valgrind suppressions. Fixed minor memory leak.

  • Property mode set to 100644
File size: 6.3 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
157 ver2("time %f", time);
158
159 for (int32_t i = 0; i < len; ++i) {
160 ver2("midi[%d] 0x%02x", i, midi[i]);
161 out(0, midi[i]);
162 }
163}
164
165void mid_init(void)
166{
167 ver("mid init");
168
169 mid_in = rtmidi_in_create_default();
170 mid_in->data = NULL; // XXX - remove initialization once it's added to RtMidi
171
172 if (!mid_in->ok) {
173 fail("rtmidi_in_create_default() failed: %s", mid_in->msg);
174 }
175
176 uint32_t n_ports = rtmidi_get_port_count(mid_in);
177
178 if (n_ports == 0) {
179 inf("no MIDI ports\n");
180 rtmidi_in_free(mid_in);
181 mid_in = NULL;
182 return;
183 }
184
185 if (mid_port >= n_ports) {
186 fail("invalid MIDI port %u selected (%u available)", mid_port, n_ports);
187 }
188
189 const char *name = rtmidi_get_port_name(mid_in, mid_port);
190 rtmidi_open_port(mid_in, mid_port, name);
191
192 if (!mid_in->ok) {
193 fail("error while opening MIDI port %u (%s): %s", mid_port, name, mid_in->msg);
194 }
195
196 inf("using MIDI port %u (%s)", mid_port, name);
197 free((char *)name);
198
199 rtmidi_in_set_callback(mid_in, callback, mid_in->data);
200
201 if (!mid_in->ok) {
202 fail("rtmidi_in_set_callback() failed: %s", mid_in->msg);
203 }
204}
205
206void mid_quit(void)
207{
208 ver("mid quit");
209
210 if (mid_in == NULL) {
211 return;
212 }
213
214 rtmidi_close_port(mid_in);
215 rtmidi_in_free(mid_in);
216}
217
218bool mid_exec(void)
219{
220 ver3("mid exec");
221 return state[0].irq_r || state[0].irq_t || state[1].irq_r || state[1].irq_t;
222}
223
224uint32_t mid_read(uint32_t off, int32_t sz)
225{
226 ver2("mid rd %u:%d", off, sz * 8);
227
228 if (sz != 1 || off > 7) {
229 fail("invalid mid rd %u:%d", off, sz * 8);
230 }
231
232 int32_t rg = (int32_t)(off % 4);
233 int32_t un = (int32_t)(off / 4);
234
235 uint32_t rv;
236
237 switch (rg) {
238 case REG_IER_ISR:
239 rv = (uint32_t)(0xc0 | (state[un].rdr_ok ? 0x01 : 0x00));
240 state[un].irq_r = false;
241 state[un].irq_t = false;
242 ver2("ISR[%d] 0x%02x", un, rv);
243 break;
244
245 case REG_TDR_RDR:
246 rv = state[un].rdr;
247 state[un].rdr_ok = false;
248 ver2("RDR[%d] 0x%02x", un, rv);
249 break;
250
251 default:
252 rv = 0x00;
253 break;
254 }
255
256 if (!state[un].irq_r && !state[un].rdr_ok) {
257 xmit(un);
258 }
259
260 return rv;
261}
262
263void mid_write(uint32_t off, int32_t sz, uint32_t val)
264{
265 ver2("mid wr %u:%d 0x%0*x", off, sz * 8, sz * 2, val);
266
267 if (sz != 1 || off > 7) {
268 fail("invalid mid wr %u:%d", off, sz * 8);
269 }
270
271 int32_t rg = (int32_t)(off % 4);
272 int32_t un = (int32_t)(off / 4);
273
274 switch (rg) {
275 case REG_CFR_SR:
276 ver2("CFR[%d] 0x%02x", un, val);
277
278 if (un == 1) {
279 fdd_set_side((int32_t)val & 0x01);
280 }
281 else {
282 fdd_set_sel((int32_t)val & 0x01);
283 }
284
285 break;
286
287 default:
288 break;
289 }
290}
291
292void mid_list(void)
293{
294 mid_in = rtmidi_in_create_default();
295 mid_in->data = NULL; // XXX - remove initialization once it's added to RtMidi
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.