]> git.sesse.net Git - vlc/blob - modules/demux/sid.cpp
grain: fix clobber list
[vlc] / modules / demux / sid.cpp
1 /**
2  * @file sid.cpp
3  * @brief Sidplay demux module for VLC media player
4  */
5 /*****************************************************************************
6  * Copyright © 2010 Rémi Denis-Courmont
7  * Copyright © 2010 Alan Fischer <alan@lightningtoads.com>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 /* INT64_C and UINT64_C are only exposed to c++ if this is defined */
29 #ifndef __STDC_CONSTANT_MACROS
30 # define __STDC_CONSTANT_MACROS
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_input.h>
35 #include <vlc_demux.h>
36 #include <vlc_plugin.h>
37
38 #include <limits.h>
39
40 #include <sidplay/sidplay2.h>
41 #include <sidplay/builders/resid.h>
42
43 static int  Open (vlc_object_t *);
44 static void Close (vlc_object_t *);
45
46 vlc_module_begin ()
47     set_shortname ("sid")
48     set_description ( N_("C64 sid demuxer") )
49     set_category (CAT_INPUT)
50     set_subcategory (SUBCAT_INPUT_DEMUX)
51     set_capability ("demux", 100)
52     set_callbacks (Open, Close)
53 vlc_module_end ()
54
55 struct demux_sys_t
56 {
57     sidplay2 *player;
58     sid2_config_t config;
59     sid2_info_t info;
60     SidTune *tune;
61     SidTuneInfo tuneInfo;
62
63     int bytes_per_frame;
64     int block_size;
65     es_out_id_t *es;
66     date_t pts;
67 };
68
69
70 static int Demux (demux_t *);
71 static int Control (demux_t *, int, va_list);
72
73 static int Open (vlc_object_t *obj)
74 {
75     demux_t *demux = (demux_t *)obj;
76     demux_sys_t *sys = NULL;
77     es_format_t fmt;
78     bool result = false;
79     SidTune *tune = NULL;
80     sidplay2 *player = NULL;
81     ReSIDBuilder *builder = NULL;
82
83     int64_t size = stream_Size (demux->s);
84     if (size < 4 || size > LONG_MAX) /* We need to load the whole file for sidplay */
85         return VLC_EGENERIC;
86
87     const uint8_t *peek;
88     if (stream_Peek (demux->s, &peek, 4) < 4)
89         return VLC_EGENERIC;
90
91     /* sidplay2 can read PSID and the newer RSID formats */
92     if(memcmp(peek,"PSID",4)!=0 && memcmp(peek,"RSID",4)!=0)
93         return VLC_EGENERIC;
94
95     uint8_t *data = (uint8_t*) malloc(size);
96     if (unlikely (data==NULL))
97         goto error;
98
99     if (stream_Read (demux->s,data,size) < size) {
100         free (data);
101         goto error;
102     }
103
104     tune = new SidTune(0);
105     if (unlikely (tune==NULL)) {
106         free (data);
107         goto error;
108     }
109
110     result = tune->read (data, size);
111     free (data);
112     if (!result)
113         goto error;
114
115     player = new sidplay2();
116     if (unlikely(player==NULL))
117         goto error;
118
119     sys = (demux_sys_t*) calloc (1, sizeof(demux_sys_t));
120     if (unlikely(sys==NULL))
121         goto error;
122
123     sys->player = player;
124     sys->tune = tune;
125
126     tune->getInfo (sys->tuneInfo);
127
128     sys->info = player->info();
129     sys->config = player->config();
130
131     builder = new ReSIDBuilder ("ReSID");
132     if (unlikely(builder==NULL))
133         goto error;
134
135     builder->create (sys->info.maxsids);
136     builder->sampling (sys->config.frequency);
137
138     sys->config.sidEmulation = builder;
139     sys->config.precision    = 16;
140     sys->config.playback     = (sys->info.channels == 2 ? sid2_stereo : sid2_mono);
141
142     player->config (sys->config);
143
144     sys->bytes_per_frame = sys->info.channels * sys->config.precision / 8;
145     sys->block_size = sys->config.frequency / 10 * sys->bytes_per_frame;
146
147     es_format_Init (&fmt, AUDIO_ES, VLC_CODEC_S16N);
148
149     fmt.audio.i_channels        = sys->info.channels;
150     fmt.audio.i_bitspersample   = sys->config.precision;
151     fmt.audio.i_rate            = sys->config.frequency;
152     fmt.audio.i_bytes_per_frame = sys->bytes_per_frame;
153     fmt.audio.i_frame_length    = fmt.audio.i_bytes_per_frame;
154     fmt.audio.i_blockalign      = fmt.audio.i_bytes_per_frame;
155
156     fmt.i_bitrate = fmt.audio.i_rate * fmt.audio.i_bytes_per_frame;
157
158     sys->es = es_out_Add (demux->out, &fmt);
159
160     date_Init (&sys->pts, fmt.audio.i_rate, 1);
161     date_Set (&sys->pts, 0);
162
163     sys->tune->selectSong (0);
164     result = (sys->player->load (sys->tune) >=0 );
165     sys->player->fastForward (100);
166     if (!result)
167         goto error;
168
169     /* Callbacks */
170     demux->pf_demux = Demux;
171     demux->pf_control = Control;
172     demux->p_sys = sys;
173
174     return VLC_SUCCESS;
175
176 error:
177     msg_Err (demux, "An error occured during sid demuxing" );
178     delete player;
179     delete builder;
180     delete tune;
181     free (sys);
182     return VLC_EGENERIC;
183 }
184
185
186 static void Close (vlc_object_t *obj)
187 {
188     demux_t *demux = (demux_t *)obj;
189     demux_sys_t *sys = demux->p_sys;
190
191     delete sys->player;
192     delete sys->config.sidEmulation;
193     delete sys->tune;
194     free (sys);
195 }
196
197 static int Demux (demux_t *demux)
198 {
199     demux_sys_t *sys = demux->p_sys;
200
201     block_t *block = block_New( p_demux, sys->block_size);
202     if (unlikely(block==NULL))
203         return 0;
204
205     if (!sys->tune->getStatus()) {
206         block_Release (block);
207         return 0;
208     }
209
210     int i_read = sys->player->play ((void*)block->p_buffer, block->i_buffer);
211     if (i_read <= 0) {
212         block_Release (block);
213         return 0;
214     }
215     block->i_buffer = i_read;
216     block->i_pts = block->i_dts = VLC_TS_0 + date_Get (&sys->pts);
217
218     es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
219
220     es_out_Send (demux->out, sys->es, block);
221
222     date_Increment (&sys->pts, i_read / sys->bytes_per_frame);
223
224     return 1;
225 }
226
227
228 static int Control (demux_t *demux, int query, va_list args)
229 {
230     demux_sys_t *sys = demux->p_sys;
231
232     switch (query)
233     {
234         case DEMUX_GET_TIME : {
235             int64_t *v = va_arg (args, int64_t*);
236             *v = sys->player->time() * sys->player->timebase() * (CLOCK_FREQ / 100);
237             return VLC_SUCCESS;
238         }
239
240         case DEMUX_GET_META : {
241             vlc_meta_t *p_meta = (vlc_meta_t *) va_arg (args, vlc_meta_t*);
242
243             /* These are specified in the sid tune class as 0 = Title, 1 = Artist, 2 = Copyright/Publisher */
244             vlc_meta_SetTitle( p_meta, sys->tuneInfo.infoString[0] );
245             vlc_meta_SetArtist( p_meta, sys->tuneInfo.infoString[1] );
246             vlc_meta_SetCopyright( p_meta, sys->tuneInfo.infoString[2] );
247
248             return VLC_SUCCESS;
249         }
250
251         case DEMUX_GET_TITLE_INFO :
252             if ( sys->tuneInfo.songs > 1 ) {
253                 input_title_t ***ppp_title = (input_title_t***) va_arg (args, input_title_t***);
254                 int *pi_int    = (int*)va_arg( args, int* );
255
256                 *pi_int = sys->tuneInfo.songs;
257                 *ppp_title = (input_title_t**) malloc( sizeof (input_title_t**) * sys->tuneInfo.songs);
258
259                 for( int i = 0; i < sys->tuneInfo.songs; i++ ) {
260                     (*ppp_title)[i] = vlc_input_title_New();
261                 }
262
263                 return VLC_SUCCESS;
264             }
265             return VLC_EGENERIC;
266
267         case DEMUX_SET_TITLE : {
268             int i_idx = (int) va_arg (args, int);
269             sys->tune->selectSong (i_idx+1);
270             bool result = (sys->player->load (sys->tune) >=0 );
271             if (!result)
272                 return  VLC_EGENERIC;
273
274             demux->info.i_title = i_idx;
275             demux->info.i_update = INPUT_UPDATE_TITLE;
276             msg_Dbg( demux, "set song %i", i_idx);
277
278             return VLC_SUCCESS;
279         }
280     }
281
282     return VLC_EGENERIC;
283 }