1 /*****************************************************************************
2 * smf.c : Standard MIDI File (.mid) demux module for vlc
3 *****************************************************************************
4 * Copyright © 2007 Rémi Denis-Courmont
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20 *****************************************************************************/
26 #include <vlc_common.h>
27 #include <vlc_plugin.h>
28 #include <vlc_demux.h>
29 #include <vlc_charset.h>
35 #define TEMPO_MAX 250 /* Beats per minute */
38 * Reads MIDI variable length (7, 14, 21 or 28 bits) integer.
39 * @return read value, or -1 on EOF/error.
41 static int32_t ReadVarInt (stream_t
*s
)
46 for (unsigned i
= 0; i
< 4; i
++)
48 if (vlc_stream_Read (s
, &byte
, 1) < 1)
51 val
= (val
<< 7) | (byte
& 0x7f);
52 if ((byte
& 0x80) == 0)
59 typedef struct smf_track_t
61 uint64_t next
; /*< Time of next message (in term of pulses) */
62 uint64_t start
; /*< Start offset in the file */
63 uint32_t length
; /*< Bytes length */
64 uint32_t offset
; /*< Read offset relative to the start offset */
65 uint8_t running_event
; /*< Running (previous) event */
69 * Reads (delta) time from the next event of a given track.
70 * @param s stream to read data from (must be positioned at the right offset)
72 static int ReadDeltaTime (stream_t
*s
, mtrk_t
*track
)
76 assert (vlc_stream_Tell (s
) == track
->start
+ track
->offset
);
78 if (track
->offset
>= track
->length
)
80 /* This track is done */
81 track
->next
= UINT64_MAX
;
85 delta_time
= ReadVarInt (s
);
89 track
->next
+= delta_time
;
90 track
->offset
= vlc_stream_Tell (s
) - track
->start
;
97 date_t pts
; /*< Play timestamp */
98 uint64_t pulse
; /*< Pulses counter */
99 mtime_t tick
; /*< Last tick timestamp */
101 mtime_t duration
; /*< Total duration */
102 unsigned ppqn
; /*< Pulses Per Quarter Note */
103 /* by the way, "quarter note" is "noire" in French */
105 unsigned trackc
; /*< Number of tracks */
106 mtrk_t trackv
[]; /*< Track states */
110 * Non-MIDI Meta events handler
113 int HandleMeta (demux_t
*p_demux
, mtrk_t
*tr
)
115 stream_t
*s
= p_demux
->s
;
116 demux_sys_t
*p_sys
= p_demux
->p_sys
;
122 if (vlc_stream_Read (s
, &type
, 1) != 1)
125 length
= ReadVarInt (s
);
129 payload
= malloc (length
+ 1);
130 if ((payload
== NULL
)
131 || (vlc_stream_Read (s
, payload
, length
) != length
))
137 payload
[length
] = '\0';
141 case 0x00: /* Sequence Number */
144 case 0x01: /* Text (comment) */
145 EnsureUTF8 ((char *)payload
);
146 msg_Info (p_demux
, "Text : %s", (char *)payload
);
149 case 0x02: /* Copyright */
150 EnsureUTF8 ((char *)payload
);
151 msg_Info (p_demux
, "Copyright : %s", (char *)payload
);
154 case 0x03: /* Track name */
155 EnsureUTF8 ((char *)payload
);
156 msg_Info (p_demux
, "Track name: %s", (char *)payload
);
159 case 0x04: /* Instrument name */
160 EnsureUTF8 ((char *)payload
);
161 msg_Info (p_demux
, "Instrument: %s", (char *)payload
);
164 case 0x05: /* Lyric (one syllable) */
165 /*EnsureUTF8 ((char *)payload);*/
168 case 0x06: /* Marker text */
169 EnsureUTF8 ((char *)payload
);
170 msg_Info (p_demux
, "Marker : %s", (char *)payload
);
173 case 0x07: /* Cue point (WAVE filename) */
174 EnsureUTF8 ((char *)payload
);
175 msg_Info (p_demux
, "Cue point : %s", (char *)payload
);
178 case 0x08: /* Program/Patch name */
179 EnsureUTF8 ((char *)payload
);
180 msg_Info (p_demux
, "Patch name: %s", (char *)payload
);
183 case 0x09: /* MIDI port name */
184 EnsureUTF8 ((char *)payload
);
185 msg_Dbg (p_demux
, "MIDI port : %s", (char *)payload
);
188 case 0x2F: /* End of track */
189 if (tr
->start
+ tr
->length
!= vlc_stream_Tell (s
))
191 msg_Err (p_demux
, "misplaced end of track");
196 case 0x51: /* Tempo */
199 uint32_t uspqn
= (payload
[0] << 16)
200 | (payload
[1] << 8) | payload
[2];
201 unsigned tempo
= 60 * 1000000 / (uspqn
? uspqn
: 1);
202 msg_Dbg (p_demux
, "tempo: %uus/qn -> %u BPM",
203 (unsigned)uspqn
, tempo
);
205 if (tempo
< TEMPO_MIN
)
207 msg_Warn (p_demux
, "tempo too slow -> %u BPM", TEMPO_MIN
);
211 if (tempo
> TEMPO_MAX
)
213 msg_Warn (p_demux
, "tempo too fast -> %u BPM", TEMPO_MAX
);
216 date_Change (&p_sys
->pts
, p_sys
->ppqn
* tempo
, 60);
222 case 0x54: /* SMPTE offset */
224 msg_Warn (p_demux
, "SMPTE offset not implemented");
229 case 0x58: /* Time signature */
236 case 0x59: /* Key signature */
243 case 0x7f: /* Proprietary event */
244 msg_Dbg (p_demux
, "ignored proprietary SMF Meta Event (%d bytes)",
249 msg_Warn (p_demux
, "unknown SMF Meta Event type 0x%02X (%d bytes)",
258 int HandleMessage (demux_t
*p_demux
, mtrk_t
*tr
, es_out_t
*out
)
260 stream_t
*s
= p_demux
->s
;
262 uint8_t first
, event
;
265 if (vlc_stream_Seek (s
, tr
->start
+ tr
->offset
)
266 || (vlc_stream_Read (s
, &first
, 1) != 1))
269 event
= (first
& 0x80) ? first
: tr
->running_event
;
271 switch (event
& 0xf0)
273 case 0xF0: /* System Exclusive */
276 case 0xF0: /* System Specific start */
277 case 0xF7: /* System Specific continuation */
279 /* Variable length followed by SysEx event data */
280 int32_t len
= ReadVarInt (s
);
284 block
= vlc_stream_Block (s
, len
);
287 block
= block_Realloc (block
, 1, len
);
290 block
->p_buffer
[0] = event
;
293 case 0xFF: /* SMF Meta Event */
294 if (HandleMeta (p_demux
, tr
))
296 /* We MUST NOT pass this event forward. It would be
297 * confused as a MIDI Reset real-time event. */
308 /* We cannot handle undefined "common" (non-real-time)
309 * events inside SMF, as we cannot differentiate a
310 * one byte delta-time (< 0x80) from event data. */
325 /* FIXME: one message per block is very inefficient */
326 block
= block_Alloc (1 + datalen
);
330 block
->p_buffer
[0] = event
;
333 if (vlc_stream_Read(s
, block
->p_buffer
+ 1, datalen
) < datalen
)
339 { /* implicit running status requires non-empty payload */
340 msg_Err (p_demux
, "malformatted MIDI event");
344 block
->p_buffer
[1] = first
;
346 && vlc_stream_Read(s
, block
->p_buffer
+ 2, datalen
- 1) < datalen
- 1)
351 block
->i_dts
= block
->i_pts
= date_Get (&p_demux
->p_sys
->pts
);
353 es_out_Send (out
, p_demux
->p_sys
->es
, block
);
355 block_Release (block
);
359 /* If event is not real-time, update running status */
360 tr
->running_event
= event
;
362 tr
->offset
= vlc_stream_Tell (s
) - tr
->start
;
366 block_Release(block
);
370 static int SeekSet0 (demux_t
*demux
)
372 stream_t
*stream
= demux
->s
;
373 demux_sys_t
*sys
= demux
->p_sys
;
375 /* Default SMF tempo is 120BPM, i.e. half a second per quarter note */
376 date_Init (&sys
->pts
, sys
->ppqn
* 2, 1);
377 date_Set (&sys
->pts
, VLC_TS_0
);
379 sys
->tick
= VLC_TS_0
;
381 for (unsigned i
= 0; i
< sys
->trackc
; i
++)
383 mtrk_t
*tr
= sys
->trackv
+ i
;
387 /* Why 0xF6 (Tuning Calibration)?
388 * Because it has zero bytes of data, so the parser will detect the
389 * error if the first event uses running status. */
390 tr
->running_event
= 0xF6;
392 if (vlc_stream_Seek (stream
, tr
->start
)
393 || ReadDeltaTime (stream
, tr
))
395 msg_Err (demux
, "fatal parsing error");
403 static int ReadEvents (demux_t
*demux
, uint64_t *restrict pulse
,
406 uint64_t cur_pulse
= *pulse
, next_pulse
= UINT64_MAX
;
407 demux_sys_t
*sys
= demux
->p_sys
;
409 for (unsigned i
= 0; i
< sys
->trackc
; i
++)
411 mtrk_t
*track
= sys
->trackv
+ i
;
413 while (track
->next
<= cur_pulse
)
415 if (HandleMessage (demux
, track
, out
)
416 || ReadDeltaTime (demux
->s
, track
))
418 msg_Err (demux
, "fatal parsing error");
423 if (next_pulse
> track
->next
)
424 next_pulse
= track
->next
;
427 if (next_pulse
!= UINT64_MAX
)
428 date_Increment (&sys
->pts
, next_pulse
- cur_pulse
);
433 #define TICK (CLOCK_FREQ / 100)
435 /*****************************************************************************
436 * Demux: read chunks and send them to the synthesizer
437 *****************************************************************************
438 * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
439 *****************************************************************************/
440 static int Demux (demux_t
*demux
)
442 demux_sys_t
*sys
= demux
->p_sys
;
444 /* MIDI Tick emulation (ping the decoder every 10ms) */
445 if (sys
->tick
<= date_Get (&sys
->pts
))
447 block_t
*tick
= block_Alloc (1);
448 if (unlikely(tick
== NULL
))
451 tick
->p_buffer
[0] = 0xF9;
452 tick
->i_dts
= tick
->i_pts
= sys
->tick
;
454 es_out_Send (demux
->out
, sys
->es
, tick
);
455 es_out_SetPCR (demux
->out
, sys
->tick
);
461 /* MIDI events in chronological order across all tracks */
462 uint64_t pulse
= sys
->pulse
;
464 if (ReadEvents (demux
, &pulse
, demux
->out
))
467 if (pulse
== UINT64_MAX
)
468 return 0; /* all tracks are done */
474 static int Seek (demux_t
*demux
, mtime_t pts
)
476 demux_sys_t
*sys
= demux
->p_sys
;
478 /* Rewind if needed */
479 if (pts
< date_Get (&sys
->pts
) && SeekSet0 (demux
))
483 uint64_t pulse
= sys
->pulse
;
485 while (pts
> date_Get (&sys
->pts
))
487 if (pulse
== UINT64_MAX
)
488 return VLC_SUCCESS
; /* premature end */
489 if (ReadEvents (demux
, &pulse
, NULL
))
494 sys
->tick
= ((date_Get (&sys
->pts
) - VLC_TS_0
) / TICK
) * TICK
+ VLC_TS_0
;
498 /*****************************************************************************
500 *****************************************************************************/
501 static int Control (demux_t
*demux
, int i_query
, va_list args
)
503 demux_sys_t
*sys
= demux
->p_sys
;
508 *va_arg (args
, bool *) = true;
510 case DEMUX_GET_POSITION
:
513 *va_arg (args
, double *) = (sys
->tick
- (double)VLC_TS_0
)
516 case DEMUX_SET_POSITION
:
517 return Seek (demux
, va_arg (args
, double) * sys
->duration
);
518 case DEMUX_GET_LENGTH
:
519 *va_arg (args
, int64_t *) = sys
->duration
;
522 *va_arg (args
, int64_t *) = sys
->tick
- VLC_TS_0
;
525 return Seek (demux
, va_arg (args
, int64_t));
533 * Probes file format and starts demuxing.
535 static int Open (vlc_object_t
*obj
)
537 demux_t
*demux
= (demux_t
*)obj
;
538 stream_t
*stream
= demux
->s
;
542 /* (Try to) parse the SMF header */
543 /* Header chunk always has 6 bytes payload */
544 if (vlc_stream_Peek (stream
, &peek
, 14) < 14)
547 /* Skip RIFF MIDI header if present */
548 if (!memcmp (peek
, "RIFF", 4) && !memcmp (peek
+ 8, "RMID", 4))
550 uint32_t riff_len
= GetDWLE (peek
+ 4);
552 msg_Dbg (demux
, "detected RIFF MIDI file (%"PRIu32
" bytes)", riff_len
);
553 if ((vlc_stream_Read (stream
, NULL
, 12) < 12))
556 /* Look for the RIFF data chunk */
563 || (vlc_stream_Read (stream
, chnk_hdr
, 8) < 8))
567 chnk_len
= GetDWLE (chnk_hdr
+ 4);
568 if (riff_len
< chnk_len
)
570 riff_len
-= chnk_len
;
572 if (!memcmp (chnk_hdr
, "data", 4))
575 if (vlc_stream_Read (stream
, NULL
, chnk_len
) < (ssize_t
)chnk_len
)
579 /* Read real SMF header. Assume RIFF data chunk length is proper. */
580 if (vlc_stream_Peek (stream
, &peek
, 14) < 14)
584 if (memcmp (peek
, "MThd\x00\x00\x00\x06", 8))
588 /* First word: SMF type */
589 switch (GetWBE (peek
))
598 /* We don't implement SMF2 (as do many) */
599 msg_Err (demux
, "unsupported SMF file type %u", GetWBE (peek
));
604 /* Second word: number of tracks */
605 unsigned tracks
= GetWBE (peek
);
607 if (!multitrack
&& (tracks
!= 1))
609 msg_Err (demux
, "invalid SMF type 0 file");
613 msg_Dbg (demux
, "detected Standard MIDI File (type %u) with %u track(s)",
616 /* Third/last word: timing */
617 unsigned ppqn
= GetWBE (peek
);
620 msg_Err (demux
, "SMPTE timestamps not implemented");
627 msg_Err(demux
, "invalid SMF file PPQN: %u", ppqn
);
630 msg_Dbg (demux
, " %u pulses per quarter note", ppqn
);
633 demux_sys_t
*sys
= malloc (sizeof (*sys
) + (sizeof (mtrk_t
) * tracks
));
634 if (unlikely(sys
== NULL
))
637 /* We've had a valid SMF header - now skip it*/
638 if (vlc_stream_Read (stream
, NULL
, 14) < 14)
644 sys
->trackc
= tracks
;
646 /* Prefetch track offsets */
647 for (unsigned i
= 0; i
< tracks
; i
++)
649 mtrk_t
*tr
= sys
->trackv
+ i
;
652 /* Seeking screws streaming up, but there is no way around this, as
653 * SMF1 tracks are performed simultaneously.
654 * Not a big deal as SMF1 are usually only a few kbytes anyway. */
655 if (i
> 0 && vlc_stream_Seek (stream
, tr
[-1].start
+ tr
[-1].length
))
657 msg_Err (demux
, "cannot build SMF index (corrupted file?)");
663 if (vlc_stream_Read (stream
, head
, 8) < 8)
665 /* FIXME: don't give up if we have at least one valid track */
666 msg_Err (demux
, "incomplete SMF chunk, file is corrupted");
670 if (memcmp (head
, "MTrk", 4) == 0)
673 uint_fast32_t chunk_len
= GetDWBE(head
+ 4);
674 msg_Dbg(demux
, "skipping unknown SMF chunk (%"PRIuFAST32
" bytes)",
676 if (vlc_stream_Seek(stream
, vlc_stream_Tell(stream
) + chunk_len
))
680 tr
->start
= vlc_stream_Tell (stream
);
681 tr
->length
= GetDWBE (head
+ 4);
685 if (vlc_stream_Control (stream
, STREAM_CAN_FASTSEEK
, &b
) == 0 && b
)
687 if (SeekSet0 (demux
))
690 for (uint64_t pulse
= 0; pulse
!= UINT64_MAX
;)
691 if (ReadEvents (demux
, &pulse
, NULL
))
694 sys
->duration
= date_Get (&sys
->pts
);
697 if (SeekSet0 (demux
))
701 es_format_Init (&fmt
, AUDIO_ES
, VLC_CODEC_MIDI
);
702 fmt
.audio
.i_channels
= 2;
703 fmt
.audio
.i_rate
= 44100; /* dummy value */
704 sys
->es
= es_out_Add (demux
->out
, &fmt
);
706 demux
->pf_demux
= Demux
;
707 demux
->pf_control
= Control
;
716 * Releases allocate resources.
718 static void Close (vlc_object_t
* p_this
)
720 demux_t
*p_demux
= (demux_t
*)p_this
;
721 demux_sys_t
*p_sys
= p_demux
->p_sys
;
727 set_description (N_("SMF demuxer"))
728 set_category (CAT_INPUT
)
729 set_subcategory (SUBCAT_INPUT_DEMUX
)
730 set_capability ("demux", 20)
731 set_callbacks (Open
, Close
)