/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 () |