Coverage Report

Created: 2026-03-31 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/demux/smf.c
Line
Count
Source
1
/*****************************************************************************
2
 * smf.c : Standard MIDI File (.mid) demux module for vlc
3
 *****************************************************************************
4
 * Copyright © 2007 Rémi Denis-Courmont
5
 *
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published by
8
 * the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this program; if not, write to the Free Software Foundation,
18
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19
 *****************************************************************************/
20
21
#ifdef HAVE_CONFIG_H
22
# include "config.h"
23
#endif
24
25
#include <vlc_common.h>
26
#include <vlc_plugin.h>
27
#include <vlc_demux.h>
28
#include <vlc_charset.h>
29
#include <limits.h>
30
31
#include <assert.h>
32
33
258
#define TEMPO_MIN  20
34
238
#define TEMPO_MAX 250 /* Beats per minute */
35
36
/**
37
 * Reads MIDI variable length (7, 14, 21 or 28 bits) integer.
38
 * @return read value, or -1 on EOF/error.
39
 */
40
static int32_t ReadVarInt (stream_t *s)
41
86.8k
{
42
86.8k
    uint32_t val = 0;
43
86.8k
    uint8_t byte;
44
45
93.3k
    for (unsigned i = 0; i < 4; i++)
46
93.2k
    {
47
93.2k
        if (vlc_stream_Read (s, &byte, 1) < 1)
48
182
            return -1;
49
50
93.1k
        val = (val << 7) | (byte & 0x7f);
51
93.1k
        if ((byte & 0x80) == 0)
52
86.5k
            return val;
53
93.1k
    }
54
55
97
    return -1;
56
86.8k
}
57
58
typedef struct smf_track_t
59
{
60
    uint64_t next;   /*< Time of next message (in term of pulses) */
61
    uint64_t start;  /*< Start offset in the file */
62
    uint32_t length; /*< Bytes length */
63
    uint32_t offset; /*< Read offset relative to the start offset */
64
    uint8_t  running_event; /*< Running (previous) event */
65
} mtrk_t;
66
67
/**
68
 * Reads (delta) time from the next event of a given track.
69
 * @param s stream to read data from (must be positioned at the right offset)
70
 */
71
static int ReadDeltaTime (stream_t *s, mtrk_t *track)
72
75.2k
{
73
75.2k
    int32_t delta_time;
74
75
75.2k
    assert (vlc_stream_Tell (s) == track->start + track->offset);
76
77
75.2k
    if (track->offset >= track->length)
78
201
    {
79
        /* This track is done */
80
201
        track->next = UINT64_MAX;
81
201
        return 0;
82
201
    }
83
84
75.0k
    delta_time = ReadVarInt (s);
85
75.0k
    if (delta_time < 0)
86
253
        return -1;
87
88
74.7k
    track->next += delta_time;
89
74.7k
    track->offset = vlc_stream_Tell (s) - track->start;
90
74.7k
    return 0;
91
75.0k
}
92
93
typedef struct
94
{
95
    es_out_id_t *es;
96
    date_t       pts; /*< Play timestamp */
97
    uint64_t     pulse; /*< Pulses counter */
98
    vlc_tick_t   tick; /*< Last tick timestamp */
99
100
    vlc_tick_t   duration; /*< Total duration */
101
    unsigned     ppqn;   /*< Pulses Per Quarter Note */
102
    /* by the way, "quarter note" is "noire" in French */
103
104
    unsigned     trackc; /*< Number of tracks */
105
    mtrk_t       trackv[]; /*< Track states */
106
} demux_sys_t;
107
108
/**
109
 * Non-MIDI Meta events handler
110
 */
111
static
112
int HandleMeta (demux_t *p_demux, mtrk_t *tr)
113
7.76k
{
114
7.76k
    stream_t *s = p_demux->s;
115
7.76k
    demux_sys_t *p_sys = p_demux->p_sys;
116
7.76k
    uint8_t *payload;
117
7.76k
    uint8_t type;
118
7.76k
    int32_t length;
119
7.76k
    int ret = 0;
120
121
7.76k
    if (vlc_stream_Read (s, &type, 1) != 1)
122
6
        return -1;
123
124
7.75k
    length = ReadVarInt (s);
125
7.75k
    if (length < 0)
126
12
        return -1;
127
128
7.74k
    payload = malloc (length + 1);
129
7.74k
    if ((payload == NULL)
130
7.74k
     || (vlc_stream_Read (s, payload, length) != length))
131
98
    {
132
98
        free (payload);
133
98
        return -1;
134
98
    }
135
136
7.64k
    payload[length] = '\0';
137
138
7.64k
    switch (type)
139
7.64k
    {
140
538
        case 0x00: /* Sequence Number */
141
538
            break;
142
143
398
        case 0x01: /* Text (comment) */
144
398
            EnsureUTF8 ((char *)payload);
145
398
            msg_Info (p_demux, "Text      : %s", (char *)payload);
146
398
            break;
147
148
530
        case 0x02: /* Copyright */
149
530
            EnsureUTF8 ((char *)payload);
150
530
            msg_Info (p_demux, "Copyright : %s", (char *)payload);
151
530
            break;
152
153
664
        case 0x03: /* Track name */
154
664
            EnsureUTF8 ((char *)payload);
155
664
            msg_Info (p_demux, "Track name: %s", (char *)payload);
156
664
            break;
157
158
718
        case 0x04: /* Instrument name */
159
718
            EnsureUTF8 ((char *)payload);
160
718
            msg_Info (p_demux, "Instrument: %s", (char *)payload);
161
718
            break;
162
163
340
        case 0x05: /* Lyric (one syllable) */
164
            /*EnsureUTF8 ((char *)payload);*/
165
340
            break;
166
167
2.79k
        case 0x06: /* Marker text */
168
2.79k
            EnsureUTF8 ((char *)payload);
169
2.79k
            msg_Info (p_demux, "Marker    : %s", (char *)payload);
170
2.79k
            break;
171
172
176
        case 0x07: /* Cue point (WAVE filename) */
173
176
            EnsureUTF8 ((char *)payload);
174
176
            msg_Info (p_demux, "Cue point : %s", (char *)payload);
175
176
            break;
176
177
94
        case 0x08: /* Program/Patch name */
178
94
            EnsureUTF8 ((char *)payload);
179
94
            msg_Info (p_demux, "Patch name: %s", (char *)payload);
180
94
            break;
181
182
92
        case 0x09: /* MIDI port name */
183
92
            EnsureUTF8 ((char *)payload);
184
92
            msg_Dbg (p_demux, "MIDI port : %s", (char *)payload);
185
92
            break;
186
187
152
        case 0x2F: /* End of track */
188
152
            if (tr->start + tr->length != vlc_stream_Tell (s))
189
64
            {
190
64
                msg_Err (p_demux, "misplaced end of track");
191
64
                ret = -1;
192
64
            }
193
152
            break;
194
195
238
        case 0x51: /* Tempo */
196
238
            if (length == 3)
197
232
            {
198
232
                uint32_t uspqn = (payload[0] << 16)
199
232
                               | (payload[1] << 8) | payload[2];
200
232
                unsigned tempo = 60 * 1000000 / (uspqn ? uspqn : 1);
201
232
                msg_Dbg (p_demux, "tempo: %uus/qn -> %u BPM",
202
232
                         (unsigned)uspqn, tempo);
203
204
232
                if (tempo < TEMPO_MIN)
205
26
                {
206
26
                    msg_Warn (p_demux, "tempo too slow -> %u BPM", TEMPO_MIN);
207
26
                    tempo = TEMPO_MIN;
208
26
                }
209
206
                else
210
206
                if (tempo > TEMPO_MAX)
211
32
                {
212
32
                    msg_Warn (p_demux, "tempo too fast -> %u BPM", TEMPO_MAX);
213
32
                    tempo = TEMPO_MAX;
214
32
                }
215
232
                date_Change (&p_sys->pts, p_sys->ppqn * tempo, 60);
216
232
            }
217
6
            else
218
6
                ret = -1;
219
238
            break;
220
221
42
        case 0x54: /* SMPTE offset */
222
42
            if (length == 5)
223
42
                msg_Warn (p_demux, "SMPTE offset not implemented");
224
6
            else
225
6
                ret = -1;
226
42
            break;
227
228
152
        case 0x58: /* Time signature */
229
152
            if (length == 4)
230
146
                ;
231
6
            else
232
6
                ret = -1;
233
152
            break;
234
235
56
        case 0x59: /* Key signature */
236
56
            if (length != 2)
237
56
                msg_Warn(p_demux, "invalid key signature");
238
56
            break;
239
240
82
        case 0x7f: /* Proprietary event */
241
82
            msg_Dbg (p_demux, "ignored proprietary SMF Meta Event (%d bytes)",
242
82
                     length);
243
82
            break;
244
245
578
        default:
246
578
            msg_Warn (p_demux, "unknown SMF Meta Event type 0x%02X (%d bytes)",
247
7.64k
                      type, length);
248
7.64k
    }
249
250
7.64k
    free (payload);
251
7.64k
    return ret;
252
7.64k
}
253
254
static
255
int HandleMessage (demux_t *p_demux, mtrk_t *tr, es_out_t *out)
256
74.7k
{
257
74.7k
    stream_t *s = p_demux->s;
258
74.7k
    demux_sys_t *sys = p_demux->p_sys;
259
74.7k
    block_t *block;
260
74.7k
    uint8_t first, event;
261
74.7k
    int datalen;
262
263
74.7k
    if (vlc_stream_Seek (s, tr->start + tr->offset)
264
74.7k
     || (vlc_stream_Read (s, &first, 1) != 1))
265
80
        return -1;
266
267
74.6k
    event = (first & 0x80) ? first : tr->running_event;
268
269
74.6k
    switch (event & 0xf0)
270
74.6k
    {
271
15.6k
        case 0xF0: /* System Exclusive */
272
15.6k
            switch (event)
273
15.6k
            {
274
3.30k
                case 0xF0: /* System Specific start */
275
4.06k
                case 0xF7: /* System Specific continuation */
276
4.06k
                {
277
                    /* Variable length followed by SysEx event data */
278
4.06k
                    int32_t len = ReadVarInt (s);
279
4.06k
                    if (len == -1)
280
14
                        return -1;
281
282
4.04k
                    block = vlc_stream_Block (s, len);
283
4.04k
                    if (block == NULL)
284
16
                        return -1;
285
4.03k
                    block = block_Realloc (block, 1, len);
286
4.03k
                    if (block == NULL)
287
6
                        return -1;
288
4.02k
                    block->p_buffer[0] = event;
289
4.02k
                    goto send;
290
4.03k
                }
291
7.76k
                case 0xFF: /* SMF Meta Event */
292
7.76k
                    if (HandleMeta (p_demux, tr))
293
198
                        return -1;
294
                    /* We MUST NOT pass this event forward. It would be
295
                     * confused as a MIDI Reset real-time event. */
296
7.56k
                    goto skip;
297
7.56k
                case 0xF1:
298
3.13k
                case 0xF3:
299
3.13k
                    datalen = 1;
300
3.13k
                    break;
301
500
                case 0xF2:
302
500
                    datalen = 2;
303
500
                    break;
304
8
                case 0xF4:
305
16
                case 0xF5:
306
                    /* We cannot handle undefined "common" (non-real-time)
307
                     * events inside SMF, as we cannot differentiate a
308
                     * one byte delta-time (< 0x80) from event data. */
309
222
                default:
310
222
                    datalen = 0;
311
222
                    break;
312
15.6k
            }
313
3.85k
            break;
314
3.85k
        case 0xC0:
315
24.0k
        case 0xD0:
316
24.0k
            datalen = 1;
317
24.0k
            break;
318
34.9k
        default:
319
34.9k
            datalen = 2;
320
34.9k
            break;
321
74.6k
    }
322
323
    /* FIXME: one message per block is very inefficient */
324
62.8k
    block = block_Alloc (1 + datalen);
325
62.8k
    if (block == NULL)
326
0
        goto skip;
327
328
62.8k
    block->p_buffer[0] = event;
329
62.8k
    if (first & 0x80)
330
9.26k
    {
331
9.26k
        if (vlc_stream_Read(s, block->p_buffer + 1, datalen) < datalen)
332
12
            goto error;
333
9.26k
    }
334
53.5k
    else
335
53.5k
    {
336
53.5k
        if (datalen == 0)
337
104
        {   /* implicit running status requires non-empty payload */
338
104
            msg_Err (p_demux, "malformatted MIDI event");
339
104
            goto error;
340
104
        }
341
342
53.4k
        block->p_buffer[1] = first;
343
53.4k
        if (datalen > 1
344
28.2k
         && vlc_stream_Read(s, block->p_buffer + 2, datalen - 1) < datalen - 1)
345
116
            goto error;
346
53.4k
    }
347
348
66.6k
send:
349
66.6k
    block->i_dts = block->i_pts = date_Get(&sys->pts);
350
66.6k
    if (out != NULL)
351
33.3k
        es_out_Send(out, sys->es, block);
352
33.3k
    else
353
33.3k
        block_Release (block);
354
355
74.2k
skip:
356
74.2k
    if (event < 0xF8)
357
        /* If event is not real-time, update running status */
358
66.5k
        tr->running_event = event;
359
360
74.2k
    tr->offset = vlc_stream_Tell (s) - tr->start;
361
74.2k
    return 0;
362
363
232
error:
364
232
    block_Release(block);
365
232
    return -1;
366
66.6k
}
367
368
static int SeekSet0 (demux_t *demux)
369
939
{
370
939
    stream_t *stream = demux->s;
371
939
    demux_sys_t *sys = demux->p_sys;
372
373
    /* Default SMF tempo is 120BPM, i.e. half a second per quarter note */
374
939
    date_Init (&sys->pts, sys->ppqn * 2, 1);
375
939
    date_Set (&sys->pts, VLC_TICK_0);
376
939
    sys->pulse = 0;
377
939
    sys->tick = VLC_TICK_0;
378
379
1.93k
    for (unsigned i = 0; i < sys->trackc; i++)
380
1.01k
    {
381
1.01k
        mtrk_t *tr = sys->trackv + i;
382
383
1.01k
        tr->offset = 0;
384
1.01k
        tr->next = 0;
385
        /* Why 0xF6 (Tuning Calibration)?
386
         * Because it has zero bytes of data, so the parser will detect the
387
         * error if the first event uses running status. */
388
1.01k
        tr->running_event = 0xF6;
389
390
1.01k
        if (vlc_stream_Seek (stream, tr->start)
391
1.01k
         || ReadDeltaTime (stream, tr))
392
19
        {
393
19
            msg_Err (demux, "fatal parsing error");
394
19
            return -1;
395
19
        }
396
1.01k
    }
397
398
920
    return 0;
399
939
}
400
401
static int ReadEvents (demux_t *demux, uint64_t *restrict pulse,
402
                       es_out_t *out)
403
57.0k
{
404
57.0k
    uint64_t cur_pulse = *pulse, next_pulse = UINT64_MAX;
405
57.0k
    demux_sys_t *sys = demux->p_sys;
406
407
114k
    for (unsigned i = 0; i < sys->trackc; i++)
408
58.1k
    {
409
58.1k
        mtrk_t *track = sys->trackv + i;
410
411
132k
        while (track->next <= cur_pulse)
412
74.7k
        {
413
74.7k
            if (HandleMessage (demux, track, out)
414
74.2k
             || ReadDeltaTime (demux->s, track))
415
780
            {
416
780
                msg_Err (demux, "fatal parsing error");
417
780
                return -1;
418
780
            }
419
74.7k
        }
420
421
57.3k
        if (next_pulse > track->next)
422
56.2k
            next_pulse = track->next;
423
57.3k
    }
424
425
56.2k
    if (next_pulse != UINT64_MAX)
426
56.1k
        date_Increment (&sys->pts, next_pulse - cur_pulse);
427
56.2k
    *pulse = next_pulse;
428
56.2k
    return 0;
429
57.0k
}
430
431
37.0M
#define TICK VLC_TICK_FROM_MS(10)
432
433
/*****************************************************************************
434
 * Demux: read chunks and send them to the synthesizer
435
 *****************************************************************************
436
 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
437
 *****************************************************************************/
438
static int Demux (demux_t *demux)
439
37.1M
{
440
37.1M
    demux_sys_t *sys = demux->p_sys;
441
442
    /* MIDI Tick emulation (ping the decoder every 10ms) */
443
37.1M
    if (sys->tick <= date_Get (&sys->pts))
444
37.0M
    {
445
37.0M
        block_t *tick = block_Alloc (1);
446
37.0M
        if (unlikely(tick == NULL))
447
0
            return VLC_ENOMEM;
448
449
37.0M
        tick->p_buffer[0] = 0xF9;
450
37.0M
        tick->i_dts = tick->i_pts = sys->tick;
451
452
37.0M
        es_out_Send (demux->out, sys->es, tick);
453
37.0M
        es_out_SetPCR (demux->out, sys->tick);
454
455
37.0M
        sys->tick += TICK;
456
37.0M
        return VLC_DEMUXER_SUCCESS;
457
37.0M
    }
458
459
    /* MIDI events in chronological order across all tracks */
460
28.5k
    uint64_t pulse = sys->pulse;
461
462
28.5k
    if (ReadEvents (demux, &pulse, demux->out))
463
390
        return VLC_DEMUXER_EGENERIC;
464
465
28.1k
    if (pulse == UINT64_MAX)
466
70
        return VLC_DEMUXER_EOF; /* all tracks are done */
467
468
28.0k
    sys->pulse = pulse;
469
28.0k
    return VLC_DEMUXER_SUCCESS;
470
28.1k
}
471
472
static int Seek (demux_t *demux, vlc_tick_t pts)
473
0
{
474
0
    demux_sys_t *sys = demux->p_sys;
475
476
    /* Rewind if needed */
477
0
    if (pts < date_Get (&sys->pts) && SeekSet0 (demux))
478
0
        return VLC_EGENERIC;
479
480
    /* Fast forward */
481
0
    uint64_t pulse = sys->pulse;
482
483
0
    while (pts > date_Get (&sys->pts))
484
0
    {
485
0
        if (pulse == UINT64_MAX)
486
0
            return VLC_SUCCESS; /* premature end */
487
0
        if (ReadEvents (demux, &pulse, NULL))
488
0
            return VLC_EGENERIC;
489
0
    }
490
491
0
    sys->pulse = pulse;
492
0
    sys->tick = ((date_Get (&sys->pts) - VLC_TICK_0) / TICK) * TICK + VLC_TICK_0;
493
0
    return VLC_SUCCESS;
494
0
}
495
496
/*****************************************************************************
497
 * Control:
498
 *****************************************************************************/
499
static int Control (demux_t *demux, int i_query, va_list args)
500
0
{
501
0
    demux_sys_t *sys = demux->p_sys;
502
503
0
    switch (i_query)
504
0
    {
505
0
        case DEMUX_CAN_SEEK:
506
0
            *va_arg (args, bool *) = true;
507
0
            break;
508
0
        case DEMUX_GET_POSITION:
509
0
            if (!sys->duration)
510
0
                return VLC_EGENERIC;
511
0
            *va_arg (args, double *) = (sys->tick - (double)VLC_TICK_0)
512
0
                                     / sys->duration;
513
0
            break;
514
0
        case DEMUX_SET_POSITION:
515
0
            return Seek (demux, va_arg (args, double) * sys->duration);
516
0
        case DEMUX_GET_LENGTH:
517
0
            *va_arg (args, vlc_tick_t *) = sys->duration;
518
0
            break;
519
0
        case DEMUX_GET_TIME:
520
0
            *va_arg (args, vlc_tick_t *) = sys->tick - VLC_TICK_0;
521
0
            break;
522
0
        case DEMUX_SET_TIME:
523
0
            return Seek (demux, va_arg (args, vlc_tick_t));
524
525
0
        case DEMUX_CAN_PAUSE:
526
0
        case DEMUX_SET_PAUSE_STATE:
527
0
        case DEMUX_CAN_CONTROL_PACE:
528
0
        case DEMUX_GET_PTS_DELAY:
529
0
            return demux_vaControlHelper( demux->s, 0, -1, 0, 1, i_query, args );
530
531
0
        default:
532
0
            return VLC_EGENERIC;
533
0
    }
534
0
    return VLC_SUCCESS;
535
0
}
536
537
/**
538
 * Probes file format and starts demuxing.
539
 */
540
static int Open (vlc_object_t *obj)
541
673
{
542
673
    demux_t *demux = (demux_t *)obj;
543
673
    stream_t *stream = demux->s;
544
673
    const uint8_t *peek;
545
673
    bool multitrack;
546
547
    /* (Try to) parse the SMF header */
548
    /* Header chunk always has 6 bytes payload */
549
673
    if (vlc_stream_Peek (stream, &peek, 14) < 14)
550
7
        return VLC_EGENERIC;
551
552
    /* Skip RIFF MIDI header if present */
553
666
    if (!memcmp (peek, "RIFF", 4) && !memcmp (peek + 8, "RMID", 4))
554
63
    {
555
63
        uint32_t riff_len = GetDWLE (peek + 4);
556
557
63
        msg_Dbg (demux, "detected RIFF MIDI file (%"PRIu32" bytes)", riff_len);
558
63
        if (vlc_stream_Read( stream, NULL, 12 ) != 12 )
559
0
            return VLC_EGENERIC;
560
561
        /* Look for the RIFF data chunk */
562
63
        for (;;)
563
1.33k
        {
564
1.33k
            char chnk_hdr[8];
565
1.33k
            uint32_t chnk_len;
566
567
1.33k
            if ((riff_len < 8)
568
1.33k
             || (vlc_stream_Read (stream, chnk_hdr, 8) < 8))
569
6
                return VLC_EGENERIC;
570
571
1.33k
            riff_len -= 8;
572
1.33k
            chnk_len = GetDWLE (chnk_hdr + 4);
573
1.33k
            if (riff_len < chnk_len)
574
16
                return VLC_EGENERIC;
575
1.31k
            riff_len -= chnk_len;
576
577
1.31k
            if (!memcmp (chnk_hdr, "data", 4))
578
8
                break; /* found! */
579
580
1.30k
            if (vlc_stream_Read( stream, NULL, chnk_len ) != chnk_len )
581
33
                return VLC_EGENERIC;
582
1.30k
        }
583
584
        /* Read real SMF header. Assume RIFF data chunk length is proper. */
585
8
        if (vlc_stream_Peek (stream, &peek, 14) < 14)
586
5
            return VLC_EGENERIC;
587
8
    }
588
589
606
    if (memcmp (peek, "MThd\x00\x00\x00\x06", 8))
590
51
        return VLC_EGENERIC;
591
555
    peek += 8;
592
593
    /* First word: SMF type */
594
555
    switch (GetWBE (peek))
595
555
    {
596
497
        case 0:
597
497
            multitrack = false;
598
497
            break;
599
57
        case 1:
600
57
            multitrack = true;
601
57
            break;
602
1
        default:
603
            /* We don't implement SMF2 (as do many) */
604
1
            msg_Err (demux, "unsupported SMF file type %u", GetWBE (peek));
605
1
            return VLC_EGENERIC;
606
555
    }
607
554
    peek += 2;
608
609
    /* Second word: number of tracks */
610
554
    unsigned tracks = GetWBE (peek);
611
554
    peek += 2;
612
554
    if (!multitrack && (tracks != 1))
613
6
    {
614
6
        msg_Err (demux, "invalid SMF type 0 file");
615
6
        return VLC_EGENERIC;
616
6
    }
617
618
548
    msg_Dbg (demux, "detected Standard MIDI File (type %u) with %u track(s)",
619
548
             multitrack, tracks);
620
621
    /* Third/last word: timing */
622
548
    unsigned ppqn = GetWBE (peek);
623
548
    if (ppqn & 0x8000)
624
4
    {   /* FIXME */
625
4
        msg_Err (demux, "SMPTE timestamps not implemented");
626
4
        return VLC_EGENERIC;
627
4
    }
628
544
    else
629
544
    {
630
544
        if (ppqn == 0)
631
1
        {
632
1
            msg_Err(demux, "invalid SMF file PPQN: %u", ppqn);
633
1
            return VLC_EGENERIC;
634
1
        }
635
543
        msg_Dbg (demux, " %u pulses per quarter note", ppqn);
636
543
    }
637
638
543
    demux_sys_t *sys = malloc (sizeof (*sys) + (sizeof (mtrk_t) * tracks));
639
543
    if (unlikely(sys == NULL))
640
0
        return VLC_ENOMEM;
641
642
    /* We've had a valid SMF header - now skip it*/
643
543
    if (vlc_stream_Read( stream, NULL, 14 ) != 14 )
644
0
        goto error;
645
646
543
    demux->p_sys = sys;
647
543
    sys->duration = 0;
648
543
    sys->ppqn = ppqn;
649
543
    sys->trackc = tracks;
650
651
    /* Prefetch track offsets */
652
1.08k
    for (unsigned i = 0; i < tracks; i++)
653
604
    {
654
604
        mtrk_t *tr = sys->trackv + i;
655
604
        uint8_t head[8];
656
657
        /* Seeking screws streaming up, but there is no way around this, as
658
         * SMF1 tracks are performed simultaneously.
659
         * Not a big deal as SMF1 are usually only a few kbytes anyway. */
660
604
        if (i > 0 && vlc_stream_Seek (stream, tr[-1].start + tr[-1].length))
661
0
        {
662
0
            msg_Err (demux, "cannot build SMF index (corrupted file?)");
663
0
            goto error;
664
0
        }
665
666
604
        for (;;)
667
1.39k
        {
668
1.39k
            if (vlc_stream_Read (stream, head, 8) < 8)
669
64
            {
670
                /* FIXME: don't give up if we have at least one valid track */
671
64
                msg_Err (demux, "incomplete SMF chunk, file is corrupted");
672
64
                goto error;
673
64
            }
674
675
1.32k
            if (memcmp (head, "MTrk", 4) == 0)
676
540
                break;
677
678
789
            uint_fast32_t chunk_len = GetDWBE(head + 4);
679
789
            msg_Dbg(demux, "skipping unknown SMF chunk (%"PRIuFAST32" bytes)",
680
789
                    chunk_len);
681
789
            if (vlc_stream_Seek(stream, vlc_stream_Tell(stream) + chunk_len))
682
0
                goto error;
683
789
        }
684
685
540
        tr->start = vlc_stream_Tell (stream);
686
540
        tr->length = GetDWBE (head + 4);
687
540
    }
688
689
543
    bool b;
690
479
    if (vlc_stream_Control (stream, STREAM_CAN_FASTSEEK, &b) == 0 && b)
691
479
    {
692
479
        if (SeekSet0 (demux))
693
19
            goto error;
694
695
28.6k
        for (uint64_t pulse = 0; pulse != UINT64_MAX;)
696
28.5k
             if (ReadEvents (demux, &pulse, NULL))
697
390
                 break;
698
699
460
        sys->duration = date_Get (&sys->pts);
700
460
    }
701
702
460
    if (SeekSet0 (demux))
703
0
        goto error;
704
705
460
    es_format_t  fmt;
706
460
    es_format_Init (&fmt, AUDIO_ES, VLC_CODEC_MIDI);
707
460
    fmt.audio.i_channels = 2;
708
460
    fmt.audio.i_rate = 44100; /* dummy value */
709
460
    fmt.i_id = 0;
710
460
    sys->es = es_out_Add (demux->out, &fmt);
711
460
    if( unlikely(!sys->es) )
712
0
        goto error;
713
714
460
    demux->pf_demux = Demux;
715
460
    demux->pf_control = Control;
716
460
    return VLC_SUCCESS;
717
718
83
error:
719
83
    free (sys);
720
83
    return VLC_EGENERIC;
721
460
}
722
723
/**
724
 * Releases allocate resources.
725
 */
726
static void Close (vlc_object_t * p_this)
727
460
{
728
460
    demux_t *p_demux = (demux_t *)p_this;
729
460
    demux_sys_t *p_sys = p_demux->p_sys;
730
731
460
    free (p_sys);
732
460
}
733
734
96
vlc_module_begin ()
735
48
    set_description (N_("SMF demuxer"))
736
48
    set_subcategory (SUBCAT_INPUT_DEMUX)
737
48
    set_capability ("demux", 20)
738
96
    set_callbacks (Open, Close)
739
48
    add_file_extension("kar")
740
48
    add_file_extension("mid")
741
48
    add_file_extension("rmi")
742
48
vlc_module_end ()