/src/vlc/modules/demux/dmxmus.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * dmxmus.c : DMX audio music (.mus) demux module for vlc |
3 | | ***************************************************************************** |
4 | | * Copyright © 2019 Rémi Denis-Courmont |
5 | | * |
6 | | * Perusing documentation by Vladimir Arnost and Adam Nielsen. |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify it |
9 | | * under the terms of the GNU Lesser General Public License as published by |
10 | | * the Free Software Foundation; either version 2.1 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with this program; if not, write to the Free Software Foundation, |
20 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
21 | | *****************************************************************************/ |
22 | | |
23 | | #ifdef HAVE_CONFIG_H |
24 | | # include "config.h" |
25 | | #endif |
26 | | |
27 | | #include <assert.h> |
28 | | #include <string.h> |
29 | | |
30 | | #include <vlc_common.h> |
31 | | #include <vlc_plugin.h> |
32 | | #include <vlc_demux.h> |
33 | | |
34 | | typedef struct |
35 | | { |
36 | | es_out_id_t *es; |
37 | | date_t pts; /*< Play timestamp */ |
38 | | vlc_tick_t tick; /*< Last tick timestamp */ |
39 | | vlc_tick_t length; /*< Total length */ |
40 | | uint16_t start_offset; /*< Start byte offset of music events */ |
41 | | unsigned end_offset:17; /*< End byte offset of music events */ |
42 | | unsigned primaries:4; /*< Number of primary channels (0-9) */ |
43 | | unsigned secondaries:3; /*< Number of secondary channels (10-14) */ |
44 | | unsigned char volume[16]; /*< Volume of last note on each channel */ |
45 | | } demux_sys_t; |
46 | | |
47 | | enum { |
48 | | MUS_EV_RELEASE, |
49 | | MUS_EV_PLAY, |
50 | | MUS_EV_PITCH, |
51 | | MUS_EV_CONTROL, |
52 | | MUS_EV_CONTROL_VALUE, |
53 | | MUS_EV_MEASURE_END, |
54 | | MUS_EV_TRACK_END, |
55 | | MUS_EV_DUMMY, |
56 | | }; |
57 | | |
58 | 747k | #define MUS_EV(byte) (((byte) >> 4) & 0x7) |
59 | | |
60 | | enum { |
61 | | MUS_CTRL_PROGRAM_CHANGE, |
62 | | MUS_CTRL_BANK_SELECT, |
63 | | MUS_CTRL_MODULATION, |
64 | | MUS_CTRL_VOLUME, |
65 | | MUS_CTRL_PAN, |
66 | | MUS_CTRL_EXPRESSION, |
67 | | MUS_CTRL_REVERB, |
68 | | MUS_CTRL_CHORUS, |
69 | | MUS_CTRL_PEDAL_HOLD, |
70 | | MUS_CTRL_PEDAL_SOFT, |
71 | | MUS_CTRL_SOUND_OFF, |
72 | | MUS_CTRL_NOTES_OFF, |
73 | | MUS_CTRL_MONO, |
74 | | MUS_CTRL_POLY, |
75 | | MUS_CTRL_RESET, |
76 | | MUS_CTRL_EVENT, |
77 | | }; |
78 | | |
79 | 728 | #define MUS_FREQ 140 |
80 | | |
81 | | static int GetByte(demux_t *demux) |
82 | 784k | { |
83 | 784k | unsigned char c; |
84 | | |
85 | 784k | return (vlc_stream_Read(demux->s, &c, 1) < 1) ? -1 : c; |
86 | 784k | } |
87 | | |
88 | | /** |
89 | | * Reads one event from the bit stream. |
90 | | */ |
91 | | static int ReadEvent(demux_t *demux, unsigned char *buf, |
92 | | unsigned *restrict delay) |
93 | 373k | { |
94 | 373k | int byte = GetByte(demux); |
95 | 373k | if (byte < 0) |
96 | 217 | return -1; |
97 | | |
98 | 373k | uint_fast8_t type = MUS_EV(byte); |
99 | | |
100 | 373k | buf[0] = byte; |
101 | | |
102 | 373k | if (likely(type != MUS_EV_MEASURE_END && type != MUS_EV_TRACK_END)) { |
103 | 298k | int c = GetByte(demux); |
104 | 298k | if (c < 0) |
105 | 119 | return -1; |
106 | | |
107 | 297k | buf[1] = c; |
108 | | |
109 | 297k | switch (type) { |
110 | 22.3k | case MUS_EV_PLAY: |
111 | 22.3k | if (c & 0x80) { |
112 | 88.8k | case MUS_EV_CONTROL_VALUE: |
113 | 88.8k | c = GetByte(demux); |
114 | 88.8k | if (c < 0) |
115 | 25 | return -1; |
116 | | |
117 | 88.7k | buf[2] = c; |
118 | 88.7k | } |
119 | 108k | break; |
120 | 297k | } |
121 | 297k | } |
122 | | |
123 | | /* Compute delay until next event */ |
124 | 373k | *delay = 0; |
125 | | |
126 | 397k | while (byte & 0x80) { |
127 | 24.1k | byte = GetByte(demux); |
128 | | |
129 | 24.1k | if (byte < 0) |
130 | 35 | return -1; |
131 | | |
132 | 24.1k | *delay <<= 7; |
133 | 24.1k | *delay |= byte & 0x7f; |
134 | 24.1k | } |
135 | | |
136 | 373k | return 0; |
137 | 373k | } |
138 | | |
139 | | static block_t *Event2(uint8_t type, uint8_t channel, uint8_t data) |
140 | 58.4k | { |
141 | 58.4k | block_t *ev = block_Alloc(2); |
142 | 58.4k | if (likely(ev != NULL)) { |
143 | 58.4k | ev->p_buffer[0] = type | channel; |
144 | 58.4k | ev->p_buffer[1] = data; |
145 | 58.4k | } |
146 | 58.4k | return ev; |
147 | 58.4k | } |
148 | | |
149 | | static block_t *Event3(uint8_t type, uint8_t channel, |
150 | | uint8_t data, uint8_t data2) |
151 | 43.5k | { |
152 | 43.5k | block_t *ev = block_Alloc(3); |
153 | 43.5k | if (likely(ev != NULL)) { |
154 | 43.5k | ev->p_buffer[0] = type | channel; |
155 | 43.5k | ev->p_buffer[1] = data; |
156 | 43.5k | ev->p_buffer[2] = data2; |
157 | 43.5k | } |
158 | 43.5k | return ev; |
159 | 43.5k | } |
160 | | |
161 | | static block_t *HandleControl(demux_t *demux, uint8_t channel, uint8_t num) |
162 | 36.4k | { |
163 | 36.4k | block_t *ev = NULL; |
164 | | |
165 | 36.4k | switch (num & 0x7f) { |
166 | 3.27k | case MUS_CTRL_SOUND_OFF: |
167 | 3.27k | return Event3(0xB0, channel, 120, 0); |
168 | | |
169 | 1.88k | case MUS_CTRL_NOTES_OFF: |
170 | 1.88k | return Event3(0xB0, channel, 123, 0); |
171 | | |
172 | 2.17k | case MUS_CTRL_MONO: |
173 | 4.07k | case MUS_CTRL_POLY: |
174 | 4.07k | break; /* only meaningful for OPL3, not soft synth */ |
175 | | |
176 | 3.48k | case MUS_CTRL_RESET: |
177 | 3.48k | return Event3(0xB0, channel, 121, 0); |
178 | | |
179 | 1.88k | case MUS_CTRL_EVENT: |
180 | 1.88k | break; |
181 | | |
182 | 21.8k | default: |
183 | 21.8k | msg_Warn(demux, "unknown control %u", num & 0x7f); |
184 | 36.4k | } |
185 | | |
186 | 27.8k | return ev; |
187 | 36.4k | } |
188 | | |
189 | | static block_t *HandleControlValue(demux_t *demux, uint8_t channel, |
190 | | uint8_t num, uint8_t val) |
191 | 43.0k | { |
192 | 43.0k | val &= 0x7f; |
193 | | |
194 | 43.0k | switch (num & 0x7f) { |
195 | 2.14k | case MUS_CTRL_PROGRAM_CHANGE: |
196 | 2.14k | return Event2(0xC0, channel, val); |
197 | | |
198 | 1.79k | case MUS_CTRL_BANK_SELECT: |
199 | 1.79k | return NULL; |
200 | | |
201 | 1.56k | case MUS_CTRL_MODULATION: |
202 | 1.56k | return Event3(0xB0, channel, 1, val); |
203 | | |
204 | 3.18k | case MUS_CTRL_VOLUME: |
205 | 3.18k | return Event3(0xB0, channel, 7, val); |
206 | | |
207 | 2.65k | case MUS_CTRL_PAN: |
208 | 2.65k | return Event3(0xB0, channel, 10, val); |
209 | | |
210 | 2.91k | case MUS_CTRL_EXPRESSION: |
211 | 2.91k | return Event3(0xB0, channel, 11, val); |
212 | | |
213 | 2.53k | case MUS_CTRL_REVERB: |
214 | 2.53k | return Event3(0xB0, channel, 91, val); |
215 | | |
216 | 2.20k | case MUS_CTRL_CHORUS: |
217 | 2.20k | return Event3(0xB0, channel, 93, val); |
218 | | |
219 | 1.40k | case MUS_CTRL_PEDAL_HOLD: |
220 | 1.40k | return Event3(0xB0, channel, 64, val); |
221 | | |
222 | 2.15k | case MUS_CTRL_PEDAL_SOFT: |
223 | 2.15k | return Event3(0xB0, channel, 67, val); |
224 | | |
225 | 20.4k | default: |
226 | 20.4k | return HandleControl(demux, channel, num); |
227 | 43.0k | } |
228 | 43.0k | } |
229 | | |
230 | | static int Demux(demux_t *demux) |
231 | 48.3M | { |
232 | 48.3M | stream_t *stream = demux->s; |
233 | 48.3M | demux_sys_t *sys = demux->p_sys; |
234 | | |
235 | 48.3M | if (vlc_stream_Tell(stream) >= sys->end_offset) |
236 | 61 | return VLC_DEMUXER_EOF; |
237 | | /* We might overflow the end offset by 2 bytes. Not really a problem. */ |
238 | | |
239 | | /* Inject the MIDI Tick every 10 ms */ |
240 | 48.3M | if (sys->tick < date_Get(&sys->pts)) { |
241 | 48.2M | block_t *tick = block_Alloc(1); |
242 | 48.2M | if (unlikely(tick == NULL)) |
243 | 0 | return VLC_ENOMEM; |
244 | | |
245 | 48.2M | tick->p_buffer[0] = 0xF9; |
246 | 48.2M | tick->i_dts = tick->i_pts = sys->tick; |
247 | | |
248 | 48.2M | es_out_Send(demux->out, sys->es, tick); |
249 | 48.2M | es_out_SetPCR(demux->out, sys->tick); |
250 | | |
251 | 48.2M | sys->tick += VLC_TICK_FROM_MS(10); |
252 | 48.2M | return VLC_DEMUXER_SUCCESS; |
253 | 48.2M | } |
254 | | |
255 | | /* Read one MIDI event */ |
256 | 186k | block_t *ev = NULL; |
257 | 186k | unsigned char buf[3]; |
258 | 186k | unsigned delay; |
259 | | |
260 | 186k | if (ReadEvent(demux, buf, &delay)) |
261 | 191 | return VLC_DEMUXER_EGENERIC; |
262 | | |
263 | 186k | uint_fast8_t channel = buf[0] & 0xf; |
264 | | |
265 | 186k | if (channel >= ((channel < 10) ? sys->primaries : (10 + sys->secondaries))) |
266 | 37.4k | channel = 9; |
267 | | |
268 | 186k | switch (MUS_EV(buf[0])) { |
269 | 56.3k | case MUS_EV_RELEASE: |
270 | 56.3k | ev = Event2(0x80, channel, buf[1] & 0x7f); |
271 | 56.3k | break; |
272 | | |
273 | 11.1k | case MUS_EV_PLAY: |
274 | 11.1k | if (buf[1] & 0x80) |
275 | 1.33k | sys->volume[channel] = buf[2] & 0x7f; |
276 | | |
277 | 11.1k | ev = Event3(0x90, channel, buf[1] & 0x7f, sys->volume[channel]); |
278 | 11.1k | break; |
279 | | |
280 | 5.14k | case MUS_EV_PITCH: |
281 | 5.14k | ev = Event3(0xE0, channel, (buf[1] << 6) & 0x7f, |
282 | 5.14k | (buf[1] >> 1) & 0x7f); |
283 | 5.14k | break; |
284 | | |
285 | 15.9k | case MUS_EV_CONTROL: |
286 | 15.9k | ev = HandleControl(demux, channel, buf[1]); |
287 | 15.9k | break; |
288 | | |
289 | 43.0k | case MUS_EV_CONTROL_VALUE: |
290 | 43.0k | ev = HandleControlValue(demux, channel, buf[1], buf[2]); |
291 | 43.0k | break; |
292 | | |
293 | 37.7k | case MUS_EV_MEASURE_END: |
294 | 37.7k | break; |
295 | | |
296 | 112 | case MUS_EV_TRACK_END: |
297 | 112 | return VLC_DEMUXER_EOF; |
298 | | |
299 | 17.2k | case MUS_EV_DUMMY: |
300 | 17.2k | break; |
301 | | |
302 | 0 | default: |
303 | 186k | vlc_assert_unreachable(); |
304 | 186k | } |
305 | | |
306 | 186k | if (ev != NULL) { |
307 | 102k | ev->i_pts = ev->i_dts = date_Get(&sys->pts); |
308 | 102k | es_out_Send(demux->out, sys->es, ev); |
309 | 102k | } |
310 | | |
311 | 186k | date_Increment(&sys->pts, delay); |
312 | 186k | return VLC_DEMUXER_SUCCESS; |
313 | 186k | } |
314 | | |
315 | | static int SeekSet0(demux_t *demux) |
316 | 728 | { |
317 | 728 | demux_sys_t *sys = demux->p_sys; |
318 | | |
319 | 728 | if (vlc_stream_Seek(demux->s, sys->start_offset)) |
320 | 0 | return -1; |
321 | | |
322 | 728 | date_Set(&sys->pts, VLC_TICK_0); |
323 | 728 | return 0; |
324 | 728 | } |
325 | | |
326 | | /** |
327 | | * Gets the total length in ticks. |
328 | | * |
329 | | * @note This function clobbers the read offset of the byte stream. |
330 | | */ |
331 | | static vlc_tick_t GetLength(demux_t *demux) |
332 | 364 | { |
333 | 364 | demux_sys_t *sys = demux->p_sys; |
334 | 364 | unsigned parts = 0; |
335 | | |
336 | 364 | if (SeekSet0(demux)) |
337 | 0 | return VLC_TICK_INVALID; |
338 | | |
339 | 186k | for (;;) { |
340 | 186k | unsigned char buf[3]; |
341 | 186k | unsigned delay; |
342 | | |
343 | 186k | if (ReadEvent(demux, buf, &delay) |
344 | 186k | || MUS_EV(buf[0]) == MUS_EV_TRACK_END |
345 | 186k | || vlc_stream_Tell(demux->s) >= sys->end_offset) |
346 | 364 | break; |
347 | | |
348 | 186k | parts += delay; |
349 | 186k | } |
350 | | |
351 | 364 | return vlc_tick_from_samples(parts, MUS_FREQ); |
352 | 364 | } |
353 | | |
354 | | static int Control(demux_t *demux, int query, va_list args) |
355 | 0 | { |
356 | 0 | demux_sys_t *sys = demux->p_sys; |
357 | |
|
358 | 0 | switch (query) { |
359 | 0 | case DEMUX_CAN_SEEK: |
360 | 0 | *va_arg(args, bool *) = false; /* TODO */ |
361 | 0 | break; |
362 | | |
363 | 0 | case DEMUX_GET_POSITION: { |
364 | 0 | double pos = 0.; |
365 | |
|
366 | 0 | static_assert(VLC_TICK_INVALID <= 0, "Oops"); |
367 | 0 | if (sys->length > 0) |
368 | 0 | pos = (date_Get(&sys->pts) - VLC_TICK_0) * 1. / sys->length; |
369 | |
|
370 | 0 | *va_arg(args, double *) = pos; |
371 | 0 | break; |
372 | 0 | } |
373 | | |
374 | 0 | case DEMUX_SET_POSITION: |
375 | 0 | return VLC_EGENERIC; |
376 | | |
377 | 0 | case DEMUX_GET_LENGTH: |
378 | 0 | if (sys->length == VLC_TICK_INVALID) |
379 | 0 | return VLC_EGENERIC; |
380 | 0 | *va_arg(args, vlc_tick_t *) = sys->length; |
381 | 0 | break; |
382 | | |
383 | 0 | case DEMUX_GET_TIME: |
384 | 0 | *va_arg(args, vlc_tick_t *) = date_Get(&sys->pts) - VLC_TICK_0; |
385 | 0 | break; |
386 | | |
387 | 0 | case DEMUX_SET_TIME: |
388 | 0 | return VLC_EGENERIC; |
389 | | |
390 | 0 | case DEMUX_CAN_PAUSE: |
391 | 0 | case DEMUX_SET_PAUSE_STATE: |
392 | 0 | case DEMUX_CAN_CONTROL_PACE: |
393 | 0 | case DEMUX_GET_PTS_DELAY: |
394 | 0 | return demux_vaControlHelper(demux->s, 0, -1, 0, 1, query, args); |
395 | | |
396 | 0 | default: |
397 | 0 | return VLC_EGENERIC; |
398 | 0 | } |
399 | 0 | return VLC_SUCCESS; |
400 | 0 | } |
401 | | |
402 | | static int Open(vlc_object_t *obj) |
403 | 462 | { |
404 | 462 | demux_t *demux = (demux_t *)obj; |
405 | 462 | stream_t *stream = demux->s; |
406 | 462 | const uint8_t *hdr; |
407 | | |
408 | 462 | if (vlc_stream_Peek(stream, &hdr, 16) < 16) |
409 | 9 | return VLC_EGENERIC; |
410 | 453 | if (memcmp(hdr, "MUS\x1A", 4)) |
411 | 43 | return VLC_EGENERIC; |
412 | | |
413 | 410 | uint_fast16_t length = GetWLE(hdr + 4); |
414 | 410 | uint_fast16_t offset = GetWLE(hdr + 6); |
415 | 410 | uint_fast16_t primaries = GetWLE(hdr + 8); |
416 | 410 | uint_fast16_t secondaries = GetWLE(hdr + 10); |
417 | | |
418 | 410 | if (primaries > 8 || secondaries > 5) |
419 | 26 | return VLC_EGENERIC; |
420 | | |
421 | | /* Ignore the patch list and jump to the event offset. */ |
422 | 384 | size_t instc = GetWLE(hdr + 12); |
423 | 384 | size_t hdrlen = 16 + 2 * instc; |
424 | | |
425 | 384 | if (offset < hdrlen) |
426 | 20 | return VLC_EGENERIC; |
427 | | |
428 | 364 | msg_Dbg(demux, "MIDI channels: %u primary, %u secondary", |
429 | 364 | GetWLE(hdr + 8), GetWLE(hdr + 10)); |
430 | | |
431 | 364 | demux_sys_t *sys = vlc_obj_malloc(obj, sizeof (*sys)); |
432 | 364 | if (unlikely(sys == NULL)) |
433 | 0 | return VLC_ENOMEM; |
434 | | |
435 | 364 | demux->p_sys = sys; |
436 | 364 | sys->start_offset = offset; |
437 | 364 | sys->end_offset = (uint_fast32_t)offset + (uint_fast32_t)length; |
438 | 364 | sys->primaries = primaries; |
439 | 364 | sys->secondaries = secondaries; |
440 | 364 | memset(sys->volume, 0, sizeof (sys->volume)); |
441 | | |
442 | 364 | bool can_seek; |
443 | 364 | vlc_stream_Control(demux->s, STREAM_CAN_SEEK, &can_seek); |
444 | 364 | if (can_seek) { |
445 | 364 | sys->length = GetLength(demux); |
446 | | |
447 | 364 | if (SeekSet0(demux)) |
448 | 0 | return VLC_EGENERIC; |
449 | 364 | } else { |
450 | 0 | sys->length = VLC_TICK_INVALID; |
451 | 0 | offset -= hdrlen; |
452 | |
|
453 | 0 | if ((size_t)vlc_stream_Read(stream, NULL, offset) != offset) |
454 | 0 | return VLC_EGENERIC; |
455 | 0 | } |
456 | | |
457 | 364 | es_format_t fmt; |
458 | | |
459 | 364 | es_format_Init(&fmt, AUDIO_ES, VLC_CODEC_MIDI); |
460 | 364 | fmt.audio.i_channels = 2; |
461 | 364 | fmt.audio.i_rate = 44100; |
462 | 364 | sys->es = es_out_Add(demux->out, &fmt); |
463 | 364 | if(unlikely(!sys->es)) |
464 | 0 | return VLC_EGENERIC; |
465 | | |
466 | 364 | date_Init(&sys->pts, MUS_FREQ, 1); |
467 | 364 | date_Set(&sys->pts, VLC_TICK_0); |
468 | 364 | sys->tick = VLC_TICK_0; |
469 | | |
470 | 364 | demux->pf_demux = Demux; |
471 | 364 | demux->pf_control = Control; |
472 | 364 | return VLC_SUCCESS; |
473 | 364 | } |
474 | | |
475 | 172 | vlc_module_begin() |
476 | 86 | set_description(N_("DMX music demuxer")) |
477 | 86 | set_subcategory(SUBCAT_INPUT_DEMUX) |
478 | 86 | set_capability("demux", 20) |
479 | 172 | set_callbacks(Open, NULL) |
480 | 86 | vlc_module_end() |