3 * @brief Game Music Emu demux module for VLC media player
5 /*****************************************************************************
6 * Copyright © 2010 Rémi Denis-Courmont
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.
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.
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 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_input.h>
33 #include <vlc_demux.h>
34 #include <vlc_plugin.h>
38 static int Open (vlc_object_t
*);
39 static void Close (vlc_object_t
*);
43 set_description ("Game Music Emu")
44 set_category (CAT_INPUT
)
45 set_subcategory (SUBCAT_INPUT_DEMUX
)
46 set_capability ("demux", 10)
47 set_callbacks (Open
, Close
)
60 input_title_t
**titlev
;
66 static int Demux (demux_t
*);
67 static int Control (demux_t
*, int, va_list);
68 static gme_err_t
ReaderStream (void *, void *, int);
69 static gme_err_t
ReaderBlock (void *, void *, int);
71 static int Open (vlc_object_t
*obj
)
73 demux_t
*demux
= (demux_t
*)obj
;
76 if (vlc_stream_GetSize(demux
->s
, &size
))
78 if (size
> LONG_MAX
/* too big for GME */)
83 if (vlc_stream_Peek (demux
->s
, &peek
, 4) < 4)
86 const char *type
= gme_identify_header (peek
);
89 msg_Dbg (obj
, "detected file type %s", type
);
94 data
= vlc_stream_Block (demux
->s
, 1 << 24);
100 demux_sys_t
*sys
= malloc (sizeof (*sys
));
101 if (unlikely(sys
== NULL
))
104 sys
->emu
= gme_new_emu (gme_identify_extension (type
), RATE
);
105 if (sys
->emu
== NULL
)
112 gme_load_custom (sys
->emu
, ReaderBlock
, data
->i_buffer
, data
);
117 gme_load_custom (sys
->emu
, ReaderStream
, size
, demux
->s
);
119 gme_start_track (sys
->emu
, sys
->track_id
= 0);
122 es_format_Init (&fmt
, AUDIO_ES
, VLC_CODEC_S16N
);
123 fmt
.audio
.i_rate
= RATE
;
124 fmt
.audio
.i_bytes_per_frame
= 4;
125 fmt
.audio
.i_frame_length
= 4;
126 fmt
.audio
.i_channels
= 2;
127 fmt
.audio
.i_blockalign
= 4;
128 fmt
.audio
.i_bitspersample
= 16;
129 fmt
.i_bitrate
= RATE
* 4;
131 sys
->es
= es_out_Add (demux
->out
, &fmt
);
132 date_Init (&sys
->pts
, RATE
, 1);
133 date_Set(&sys
->pts
, VLC_TICK_0
);
136 unsigned n
= gme_track_count (sys
->emu
);
137 sys
->titlev
= vlc_alloc (n
, sizeof (*sys
->titlev
));
138 if (unlikely(sys
->titlev
== NULL
))
141 for (unsigned i
= 0; i
< n
; i
++)
143 input_title_t
*title
= vlc_input_title_New ();
144 sys
->titlev
[i
] = title
;
145 if (unlikely(title
== NULL
))
149 if (gme_track_info (sys
->emu
, &infos
, i
))
151 msg_Dbg (obj
, "track %u: %s %d ms", i
, infos
->song
, infos
->length
);
152 if (infos
->length
!= -1)
153 title
->i_length
= VLC_TICK_FROM_MS(infos
->length
);
155 title
->psz_name
= strdup (infos
->song
);
156 gme_free_info (infos
);
158 sys
->title_changed
= false;
161 demux
->pf_demux
= Demux
;
162 demux
->pf_control
= Control
;
168 static void Close (vlc_object_t
*obj
)
170 demux_t
*demux
= (demux_t
*)obj
;
171 demux_sys_t
*sys
= demux
->p_sys
;
173 for (unsigned i
= 0, n
= sys
->titlec
; i
< n
; i
++)
174 vlc_input_title_Delete (sys
->titlev
[i
]);
176 gme_delete (sys
->emu
);
181 static gme_err_t
ReaderStream (void *data
, void *buf
, int length
)
185 if (vlc_stream_Read (s
, buf
, length
) < length
)
189 static gme_err_t
ReaderBlock (void *data
, void *buf
, int length
)
191 block_t
*block
= data
;
193 int max
= __MIN (length
, (int)block
->i_buffer
);
194 memcpy (buf
, block
->p_buffer
, max
);
195 block
->i_buffer
-= max
;
196 block
->p_buffer
+= max
;
202 #define SAMPLES (RATE / 10)
204 static int Demux (demux_t
*demux
)
206 demux_sys_t
*sys
= demux
->p_sys
;
209 if (gme_track_ended (sys
->emu
))
211 msg_Dbg (demux
, "track %u ended", sys
->track_id
);
212 if (++sys
->track_id
>= (unsigned)gme_track_count (sys
->emu
))
213 return VLC_DEMUXER_EOF
;
215 sys
->title_changed
= true;
216 gme_start_track (sys
->emu
, sys
->track_id
);
220 block_t
*block
= block_Alloc (2 * 2 * SAMPLES
);
221 if (unlikely(block
== NULL
))
222 return VLC_DEMUXER_EOF
;
224 gme_err_t ret
= gme_play (sys
->emu
, 2 * SAMPLES
, (void *)block
->p_buffer
);
227 block_Release (block
);
228 msg_Err (demux
, "%s", ret
);
229 return VLC_DEMUXER_EOF
;
232 block
->i_pts
= block
->i_dts
= date_Get (&sys
->pts
);
233 es_out_SetPCR (demux
->out
, block
->i_pts
);
234 es_out_Send (demux
->out
, sys
->es
, block
);
235 date_Increment (&sys
->pts
, SAMPLES
);
236 return VLC_DEMUXER_SUCCESS
;
240 static int Control (demux_t
*demux
, int query
, va_list args
)
242 demux_sys_t
*sys
= demux
->p_sys
;
247 *va_arg (args
, bool *) = true;
250 case DEMUX_GET_POSITION
:
252 double *pos
= va_arg (args
, double *);
254 if (unlikely(sys
->track_id
>= sys
->titlec
)
255 || (sys
->titlev
[sys
->track_id
]->i_length
== 0))
259 int offset
= gme_tell (sys
->emu
);
261 *pos
= (double)offset
262 / (double)(sys
->titlev
[sys
->track_id
]->i_length
/ 1000);
267 case DEMUX_SET_POSITION
:
269 double pos
= va_arg (args
, double);
271 if (unlikely(sys
->track_id
>= sys
->titlec
)
272 || (sys
->titlev
[sys
->track_id
]->i_length
== 0))
275 int seek
= MS_FROM_VLC_TICK(sys
->titlev
[sys
->track_id
]->i_length
) * pos
;
276 if (gme_seek (sys
->emu
, seek
))
281 case DEMUX_GET_LENGTH
:
283 if (unlikely(sys
->track_id
>= sys
->titlec
)
284 || (sys
->titlev
[sys
->track_id
]->i_length
== 0))
286 *va_arg (args
, vlc_tick_t
*) = sys
->titlev
[sys
->track_id
]->i_length
;
292 *va_arg (args
, vlc_tick_t
*) = VLC_TICK_FROM_MS(gme_tell (sys
->emu
));
298 int64_t v
= MS_FROM_VLC_TICK( va_arg (args
, vlc_tick_t
) );
299 if (v
> INT_MAX
|| gme_seek (sys
->emu
, v
))
304 case DEMUX_GET_TITLE_INFO
:
306 input_title_t
***titlev
= va_arg (args
, input_title_t
***);
307 int *titlec
= va_arg (args
, int *);
308 *(va_arg (args
, int *)) = 0; /* Title offset */
309 *(va_arg (args
, int *)) = 0; /* Chapter offset */
311 unsigned n
= sys
->titlec
;
312 *titlev
= vlc_alloc (n
, sizeof (**titlev
));
313 if (unlikely(*titlev
== NULL
))
316 for (unsigned i
= 0; i
< n
; i
++)
317 (*titlev
)[i
] = vlc_input_title_Duplicate (sys
->titlev
[i
]);
321 case DEMUX_SET_TITLE
:
323 int track_id
= va_arg (args
, int);
324 if (track_id
>= gme_track_count (sys
->emu
))
326 gme_start_track (sys
->emu
, track_id
);
327 sys
->title_changed
= true;
328 sys
->track_id
= track_id
;
332 case DEMUX_TEST_AND_CLEAR_FLAGS
:
334 unsigned *restrict flags
= va_arg(args
, unsigned *);
336 if ((*flags
& INPUT_UPDATE_TITLE
) && sys
->title_changed
) {
337 *flags
= INPUT_UPDATE_TITLE
;
338 sys
->title_changed
= false;
344 case DEMUX_GET_TITLE
:
345 *va_arg(args
, int *) = sys
->track_id
;
348 case DEMUX_CAN_PAUSE
:
349 case DEMUX_SET_PAUSE_STATE
:
350 case DEMUX_CAN_CONTROL_PACE
:
351 case DEMUX_GET_PTS_DELAY
:
352 return demux_vaControlHelper( demux
->s
, 0, -1, 0, 1, query
, args
);