Coverage Report

Created: 2026-08-14 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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()