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
;
65 static int Demux (demux_t
*);
66 static int Control (demux_t
*, int, va_list);
67 static gme_err_t
ReaderStream (void *, void *, int);
68 static gme_err_t
ReaderBlock (void *, void *, int);
70 static int Open (vlc_object_t
*obj
)
72 demux_t
*demux
= (demux_t
*)obj
;
74 int64_t size
= stream_Size (demux
->s
);
75 if (size
> LONG_MAX
/* too big for GME */)
80 if (vlc_stream_Peek (demux
->s
, &peek
, 4) < 4)
83 const char *type
= gme_identify_header (peek
);
86 msg_Dbg (obj
, "detected file type %s", type
);
91 data
= vlc_stream_Block (demux
->s
, 1 << 24);
97 demux_sys_t
*sys
= malloc (sizeof (*sys
));
98 if (unlikely(sys
== NULL
))
101 sys
->emu
= gme_new_emu (gme_identify_extension (type
), RATE
);
102 if (sys
->emu
== NULL
)
109 gme_load_custom (sys
->emu
, ReaderBlock
, data
->i_buffer
, data
);
114 gme_load_custom (sys
->emu
, ReaderStream
, size
, demux
->s
);
116 gme_start_track (sys
->emu
, sys
->track_id
= 0);
119 es_format_Init (&fmt
, AUDIO_ES
, VLC_CODEC_S16N
);
120 fmt
.audio
.i_rate
= RATE
;
121 fmt
.audio
.i_bytes_per_frame
= 4;
122 fmt
.audio
.i_frame_length
= 4;
123 fmt
.audio
.i_channels
= 2;
124 fmt
.audio
.i_blockalign
= 4;
125 fmt
.audio
.i_bitspersample
= 16;
126 fmt
.i_bitrate
= RATE
* 4;
128 sys
->es
= es_out_Add (demux
->out
, &fmt
);
129 date_Init (&sys
->pts
, RATE
, 1);
130 date_Set (&sys
->pts
, 0);
133 unsigned n
= gme_track_count (sys
->emu
);
134 sys
->titlev
= vlc_alloc (n
, sizeof (*sys
->titlev
));
135 if (unlikely(sys
->titlev
== NULL
))
138 for (unsigned i
= 0; i
< n
; i
++)
140 input_title_t
*title
= vlc_input_title_New ();
141 sys
->titlev
[i
] = title
;
142 if (unlikely(title
== NULL
))
146 if (gme_track_info (sys
->emu
, &infos
, i
))
148 msg_Dbg (obj
, "track %u: %s %d ms", i
, infos
->song
, infos
->length
);
149 if (infos
->length
!= -1)
150 title
->i_length
= infos
->length
* INT64_C(1000);
152 title
->psz_name
= strdup (infos
->song
);
153 gme_free_info (infos
);
157 demux
->pf_demux
= Demux
;
158 demux
->pf_control
= Control
;
164 static void Close (vlc_object_t
*obj
)
166 demux_t
*demux
= (demux_t
*)obj
;
167 demux_sys_t
*sys
= demux
->p_sys
;
169 for (unsigned i
= 0, n
= sys
->titlec
; i
< n
; i
++)
170 vlc_input_title_Delete (sys
->titlev
[i
]);
172 gme_delete (sys
->emu
);
177 static gme_err_t
ReaderStream (void *data
, void *buf
, int length
)
181 if (vlc_stream_Read (s
, buf
, length
) < length
)
185 static gme_err_t
ReaderBlock (void *data
, void *buf
, int length
)
187 block_t
*block
= data
;
189 int max
= __MIN (length
, (int)block
->i_buffer
);
190 memcpy (buf
, block
->p_buffer
, max
);
191 block
->i_buffer
-= max
;
192 block
->p_buffer
+= max
;
198 #define SAMPLES (RATE / 10)
200 static int Demux (demux_t
*demux
)
202 demux_sys_t
*sys
= demux
->p_sys
;
205 if (gme_track_ended (sys
->emu
))
207 msg_Dbg (demux
, "track %u ended", sys
->track_id
);
208 if (++sys
->track_id
>= (unsigned)gme_track_count (sys
->emu
))
211 demux
->info
.i_update
|= INPUT_UPDATE_TITLE
;
212 demux
->info
.i_title
= sys
->track_id
;
213 gme_start_track (sys
->emu
, sys
->track_id
);
217 block_t
*block
= block_Alloc (2 * 2 * SAMPLES
);
218 if (unlikely(block
== NULL
))
221 gme_err_t ret
= gme_play (sys
->emu
, 2 * SAMPLES
, (void *)block
->p_buffer
);
224 block_Release (block
);
225 msg_Err (demux
, "%s", ret
);
229 block
->i_pts
= block
->i_dts
= VLC_TS_0
+ date_Get (&sys
->pts
);
230 es_out_SetPCR (demux
->out
, block
->i_pts
);
231 es_out_Send (demux
->out
, sys
->es
, block
);
232 date_Increment (&sys
->pts
, SAMPLES
);
237 static int Control (demux_t
*demux
, int query
, va_list args
)
239 demux_sys_t
*sys
= demux
->p_sys
;
244 *va_arg (args
, bool *) = true;
247 case DEMUX_GET_POSITION
:
249 double *pos
= va_arg (args
, double *);
251 if (unlikely(sys
->track_id
>= sys
->titlec
)
252 || (sys
->titlev
[sys
->track_id
]->i_length
== 0))
256 int offset
= gme_tell (sys
->emu
);
258 *pos
= (double)offset
259 / (double)(sys
->titlev
[sys
->track_id
]->i_length
/ 1000);
264 case DEMUX_SET_POSITION
:
266 double pos
= va_arg (args
, double);
268 if (unlikely(sys
->track_id
>= sys
->titlec
)
269 || (sys
->titlev
[sys
->track_id
]->i_length
== 0))
272 int seek
= (sys
->titlev
[sys
->track_id
]->i_length
/ 1000) * pos
;
273 if (gme_seek (sys
->emu
, seek
))
278 case DEMUX_GET_LENGTH
:
280 int64_t *v
= va_arg (args
, int64_t *);
282 if (unlikely(sys
->track_id
>= sys
->titlec
)
283 || (sys
->titlev
[sys
->track_id
]->i_length
== 0))
285 *v
= sys
->titlev
[sys
->track_id
]->i_length
;
291 int64_t *v
= va_arg (args
, int64_t *);
292 *v
= gme_tell (sys
->emu
) * INT64_C(1000);
298 int64_t v
= va_arg (args
, int64_t) / 1000;
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 demux
->info
.i_update
|= INPUT_UPDATE_TITLE
;
328 demux
->info
.i_title
= track_id
;
329 sys
->track_id
= track_id
;