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

Last change on this file since 9ddbf3e was 9ddbf3e, checked in by Thomas Lopatic <thomas@…>, 19 months 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.