1
|
1 /*
|
|
2 xmms-sid - SIDPlay input plugin for X MultiMedia System (XMMS)
|
|
3
|
|
4 Main source file
|
|
5
|
|
6 Originally by Willem Monsuwe <willem@stack.nl>
|
|
7 Additions, fixes, etc by Matti "ccr" Hamalainen <mhamalai@ratol.fi>
|
|
8
|
|
9 This program is free software; you can redistribute it and/or modify
|
|
10 it under the terms of the GNU General Public License as published by
|
|
11 the Free Software Foundation; either version 2 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 General Public License for more details.
|
|
18
|
|
19 You should have received a copy of the GNU General Public License
|
|
20 along with this program; if not, write to the Free Software
|
|
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
22 */
|
|
23
|
|
24 #include "xmms-sid.h"
|
|
25 #include <sidplay/player.h>
|
|
26 #include <sidplay/myendian.h>
|
|
27 #include <sidplay/fformat.h>
|
|
28
|
|
29
|
|
30 extern "C" {
|
|
31 #include <pthread.h>
|
|
32
|
|
33 #include <stdlib.h>
|
|
34 #include <string.h>
|
|
35 #include <stdio.h>
|
|
36
|
|
37 #include <xmms/plugin.h>
|
|
38 #include <xmms/util.h>
|
|
39 }
|
|
40
|
|
41
|
|
42 /*
|
|
43 * General variables
|
|
44 */
|
|
45 static struct emuConfig xs_emuConf;
|
|
46 static emuEngine xs_emuEngine;
|
|
47 static pthread_t xs_decode_thread;
|
|
48 static int xs_error = 0, xs_going = 0, xs_songs = 0;
|
|
49 struct T_sid_cfg xs_cfg;
|
|
50
|
|
51
|
|
52 /*
|
|
53 * Initialize xmms-sid plugin
|
|
54 */
|
|
55 void xs_init(void)
|
|
56 {
|
|
57
|
|
58 if (!xs_emuEngine) {
|
|
59 xs_error = 1;
|
|
60 XSERR("Couldn't start SIDPlay emulator engine!\n");
|
|
61 return;
|
|
62 }
|
|
63
|
|
64 if (!xs_emuEngine.verifyEndianess()) {
|
|
65 xs_error = 1;
|
|
66 XSERR("Wrong hardware endianess (SIDPlay error)!\n");
|
|
67 return;
|
|
68 }
|
|
69
|
|
70 // Initialize STIL structures
|
|
71 memset(&xs_stil_info, 0, sizeof(xs_stil_info));
|
|
72 xs_stil_clear();
|
|
73
|
|
74 // Get configuration
|
|
75 xs_get_configure();
|
|
76 }
|
|
77
|
|
78
|
|
79 /*
|
|
80 * Special, custom hand-made strcpy with smooth leather coating.
|
|
81 */
|
|
82 int xs_strcpy(char *dest, const char *src, unsigned int *j)
|
|
83 {
|
|
84 unsigned int i;
|
|
85
|
|
86 if ((dest == NULL) || (src == NULL)) return -1;
|
|
87
|
|
88 for (i = 0; i < strlen(src); i++) {
|
|
89 dest[(*j)++] = src[i];
|
|
90 }
|
|
91
|
|
92 return 0;
|
|
93 }
|
|
94
|
|
95
|
|
96 /*
|
|
97 Create the SID-tune description string from the
|
|
98 tune's information formatted by the user-specified
|
|
99 format-string.
|
|
100 */
|
|
101 static char * xs_make_filedesc(struct sidTuneInfo *s)
|
|
102 {
|
|
103 unsigned int i, len, j;
|
|
104 char *result;
|
|
105
|
|
106 /* Check the info strings */
|
|
107 if (s->numberOfInfoStrings != 3) {
|
|
108 if (s->numberOfInfoStrings < 1) {
|
|
109 return 0;
|
|
110 }
|
|
111 return g_strdup(s->infoString[0]);
|
|
112 }
|
|
113
|
|
114 /* Check the format-string for NULL */
|
|
115 if (xs_cfg.fileInfo == NULL) {
|
|
116 return g_strdup_printf("%s - %s", s->nameString, s->authorString);
|
|
117 }
|
|
118
|
|
119 /* Pre-calculate the length of the result-string */
|
|
120 len = 2;
|
|
121 for (i = 0; i < strlen(xs_cfg.fileInfo); i++) {
|
|
122 if (xs_cfg.fileInfo[i] == '%') {
|
|
123 switch (xs_cfg.fileInfo[++i]) {
|
|
124 case '1': len += strlen(s->authorString); break;
|
|
125 case '2': len += strlen(s->nameString); break;
|
|
126 case '3': len += strlen(s->copyrightString); break;
|
|
127 case '4': len += strlen(s->formatString); break;
|
|
128 }
|
|
129 } else len++;
|
|
130 }
|
|
131
|
|
132 /* Allocate the result-string */
|
|
133 result = (char *) g_malloc(len);
|
|
134
|
|
135 /* Construct the final result info */
|
|
136 j = 0;
|
|
137 for (i = 0; i < strlen(xs_cfg.fileInfo); i++) {
|
|
138
|
|
139 if (xs_cfg.fileInfo[i] == '%') {
|
|
140 switch (xs_cfg.fileInfo[++i]) {
|
|
141 case '1': xs_strcpy(result, s->authorString, &j); break;
|
|
142 case '2': xs_strcpy(result, s->nameString, &j); break;
|
|
143 case '3': xs_strcpy(result, s->copyrightString, &j); break;
|
|
144 case '4': xs_strcpy(result, s->formatString, &j); break;
|
|
145 }
|
|
146
|
|
147 } else {
|
|
148 result[j++] = xs_cfg.fileInfo[i];
|
|
149 } /* if */
|
|
150
|
|
151 } /* for */
|
|
152
|
|
153 result[j] = '\0';
|
|
154
|
|
155 return result;
|
|
156 }
|
|
157
|
|
158
|
|
159 /*
|
|
160 * Check whether the given file is handled by this plugin
|
|
161 */
|
|
162 int xs_is_our_file(char *filename)
|
|
163 {
|
|
164 if (xs_cfg.detectMagic) {
|
|
165 sidTune *t = new sidTune(filename);
|
|
166
|
|
167 if (!t->getStatus()) {
|
|
168 delete t;
|
|
169 return FALSE;
|
|
170 }
|
|
171
|
|
172 delete t;
|
|
173 return TRUE;
|
|
174 }
|
|
175
|
|
176 char *ext = strrchr(filename, '.');
|
|
177 if (ext) {
|
|
178 ext++;
|
|
179 if (!strcasecmp(ext, "psid")) return TRUE;
|
|
180 if (!strcasecmp(ext, "sid")) return TRUE;
|
|
181 if (!strcasecmp(ext, "dat")) return TRUE;
|
|
182 if (!strcasecmp(ext, "inf")) return TRUE;
|
|
183 if (!strcasecmp(ext, "info")) return TRUE;
|
|
184 }
|
|
185 return FALSE;
|
|
186 }
|
|
187
|
|
188
|
|
189 /*
|
|
190 * Playing thread loop function
|
|
191 */
|
|
192 static void * xs_play_loop(void *arg)
|
|
193 {
|
|
194 sidTune *tune = (sidTune *)arg;
|
|
195 char data[XMMS_SID_BUFSIZE];
|
|
196 int fxlen, tn;
|
|
197 struct sidTuneInfo sidInf;
|
|
198 char *name;
|
|
199 enum AFormat fmt = (xs_emuConf.bitsPerSample == 16) ? FMT_S16_NE : FMT_U8;
|
|
200 gint chn = xs_emuConf.channels;
|
|
201
|
|
202 tune->getInfo(sidInf);
|
|
203 name = xs_make_filedesc(&sidInf);
|
|
204
|
|
205 play_loop_new_tune:
|
|
206 tn = xs_going;
|
|
207 if (tn <= 0) tn = 1;
|
|
208 if (!xmms_sid_ip.output->open_audio(fmt, xs_emuConf.frequency, chn))
|
|
209 {
|
|
210 xs_error = 1;
|
|
211 XSERR("Couldn't open XMMS audio output!\n");
|
|
212 delete tune;
|
|
213 pthread_exit(NULL);
|
|
214 xs_stop();
|
|
215 }
|
|
216
|
|
217 if (!sidEmuInitializeSong(xs_emuEngine, *tune, tn)) {
|
|
218 xs_error = 1;
|
|
219 XSERR("Couldn't initialize SIDPlay emulator engine!\n");
|
|
220 delete tune;
|
|
221 pthread_exit(NULL);
|
|
222 xs_stop();
|
|
223 }
|
|
224
|
|
225 tune->getInfo(sidInf);
|
|
226
|
|
227 xmms_sid_ip.set_info(name, -1,
|
|
228 1000 * (sidInf.songSpeed ? sidInf.songSpeed : (sidInf.clockSpeed == SIDTUNE_CLOCK_NTSC) ? 60 : 50),
|
|
229 xs_emuConf.frequency, chn);
|
|
230
|
|
231 while (xs_going == tn)
|
|
232 {
|
|
233 fxlen = XMMS_SID_BUFSIZE;
|
|
234 sidEmuFillBuffer(xs_emuEngine, *tune, data, fxlen);
|
|
235
|
|
236 xmms_sid_ip.add_vis_pcm(xmms_sid_ip.output->written_time(),
|
|
237 fmt, chn, fxlen, data);
|
|
238
|
|
239 while ((xs_going == tn) && (xmms_sid_ip.output->buffer_free() < fxlen))
|
|
240 xmms_usleep(10000);
|
|
241
|
|
242 if (xs_going == tn)
|
|
243 xmms_sid_ip.output->write_audio(data, fxlen);
|
|
244 }
|
|
245
|
|
246 /* Exit the playing thread */
|
|
247 xmms_sid_ip.output->close_audio();
|
|
248
|
|
249 if (xs_going) goto play_loop_new_tune;
|
|
250
|
|
251 g_free(name);
|
|
252
|
|
253 delete tune;
|
|
254
|
|
255 pthread_exit(NULL);
|
|
256 }
|
|
257
|
|
258
|
|
259 /*
|
|
260 * Start playing the given file
|
|
261 */
|
|
262 void xs_play_file(char *filename)
|
|
263 {
|
|
264 sidTune *tune = new sidTune(filename);
|
|
265 struct sidTuneInfo sidInf;
|
|
266
|
|
267 /* Get current configuration */
|
|
268 xs_emuEngine.getConfig(xs_emuConf);
|
|
269
|
|
270 /* Configure channels and stuff */
|
|
271 switch (xs_cfg.channels) {
|
|
272
|
|
273 case XMMS_SID_CHN_AUTOPAN:
|
|
274 xs_emuConf.channels = SIDEMU_STEREO;
|
|
275 xs_emuConf.autoPanning = SIDEMU_CENTEREDAUTOPANNING;
|
|
276 xs_emuConf.volumeControl = SIDEMU_FULLPANNING;
|
|
277 break;
|
|
278
|
|
279 case XMMS_SID_CHN_STEREO:
|
|
280 xs_emuConf.channels = SIDEMU_STEREO;
|
|
281 xs_emuConf.autoPanning = SIDEMU_NONE;
|
|
282 xs_emuConf.volumeControl = SIDEMU_NONE;
|
|
283 break;
|
|
284
|
|
285 case XMMS_SID_CHN_MONO:
|
|
286 xs_emuConf.channels = SIDEMU_MONO;
|
|
287 xs_emuConf.autoPanning = SIDEMU_NONE;
|
|
288 xs_emuConf.volumeControl = SIDEMU_NONE;
|
|
289 break;
|
|
290
|
|
291 default:xs_error = 1;
|
|
292 XSERR("Internal: Invalid channels setting. Please report to author!\n");
|
|
293 delete tune;
|
|
294 break;
|
|
295 }
|
|
296
|
|
297 /* Memory mode settings */
|
|
298 switch (xs_cfg.memoryMode) {
|
|
299 case XMMS_SID_MPU_BANK_SWITCHING:
|
|
300 xs_emuConf.memoryMode = MPU_BANK_SWITCHING;
|
|
301 break;
|
|
302
|
|
303 case XMMS_SID_MPU_TRANSPARENT_ROM:
|
|
304 xs_emuConf.memoryMode = MPU_TRANSPARENT_ROM;
|
|
305 break;
|
|
306
|
|
307 case XMMS_SID_MPU_PLAYSID_ENVIRONMENT:
|
|
308 xs_emuConf.memoryMode = MPU_PLAYSID_ENVIRONMENT;
|
|
309 break;
|
|
310
|
|
311 default:xs_error = 1;
|
|
312 XSERR("Internal: Invalid memoryMode setting. Please report to author!\n");
|
|
313 delete tune;
|
|
314 break;
|
|
315 }
|
|
316
|
|
317
|
|
318 /* Clockspeed settings */
|
|
319 switch (xs_cfg.clockSpeed) {
|
|
320 case XMMS_SID_CLOCK_PAL:
|
|
321 xs_emuConf.clockSpeed = SIDTUNE_CLOCK_PAL;
|
|
322 break;
|
|
323
|
|
324 case XMMS_SID_CLOCK_NTSC:
|
|
325 xs_emuConf.clockSpeed = SIDTUNE_CLOCK_NTSC;
|
|
326 break;
|
|
327
|
|
328 default:xs_error = 1;
|
|
329 XSERR("Internal: Invalid clockSpeed setting. Please report to author!\n");
|
|
330 delete tune;
|
|
331 break;
|
|
332 }
|
|
333
|
|
334 /* Configure rest of the paske */
|
|
335 xs_emuConf.bitsPerSample = xs_cfg.bitsPerSample;
|
|
336 xs_emuConf.frequency = xs_cfg.frequency;
|
|
337 xs_emuConf.sampleFormat = SIDEMU_SIGNED_PCM;
|
|
338 xs_emuConf.mos8580 = xs_cfg.mos8580;
|
|
339 xs_emuConf.emulateFilter = xs_cfg.emulateFilter;
|
|
340 xs_emuConf.filterFs = xs_cfg.filterFs;
|
|
341 xs_emuConf.filterFm = xs_cfg.filterFm;
|
|
342 xs_emuConf.filterFt = xs_cfg.filterFt;
|
|
343
|
|
344 /* Now set the emulator configuration */
|
|
345 xs_emuEngine.setConfig(xs_emuConf);
|
|
346 tune->getInfo(sidInf);
|
|
347 xs_error = 0;
|
|
348 xs_going = sidInf.startSong;
|
|
349 xs_songs = sidInf.songs;
|
|
350
|
|
351 /* Start the playing thread! */
|
|
352 if (pthread_create(&xs_decode_thread, NULL, xs_play_loop, tune) < 0) {
|
|
353 xs_error = 1;
|
|
354 XSERR("Couldn't start playing thread!\n");
|
|
355 delete tune;
|
|
356 }
|
|
357 }
|
|
358
|
|
359
|
|
360 /*
|
|
361 * Stop playing
|
|
362 */
|
|
363 void xs_stop(void)
|
|
364 {
|
|
365 if (xs_going)
|
|
366 {
|
|
367 xs_going = 0;
|
|
368 pthread_join(xs_decode_thread, NULL);
|
|
369 }
|
|
370 }
|
|
371
|
|
372
|
|
373 /*
|
|
374 * Pause the playing
|
|
375 */
|
|
376 void xs_pause(short p)
|
|
377 {
|
|
378 xmms_sid_ip.output->pause(p);
|
|
379 }
|
|
380
|
|
381
|
|
382 /*
|
|
383 * Set the time-seek position
|
|
384 * (the playing thread will do the "seeking" aka song-change)
|
|
385 */
|
|
386 void xs_seek(int time)
|
|
387 {
|
|
388 if ((time > 0) && (time <= xs_songs)) {
|
|
389 xs_going = time;
|
|
390 }
|
|
391 }
|
|
392
|
|
393
|
|
394 /*
|
|
395 * Return the playing "position/time" aka song number
|
|
396 */
|
|
397 int xs_get_time(void)
|
|
398 {
|
|
399 if (xs_error) return -2;
|
|
400 if (!xs_going) return -1;
|
|
401 #ifdef HAVE_SONG_POSITION
|
|
402 set_song_position(xs_going, 1, xs_songs);
|
|
403 #endif
|
|
404 return xmms_sid_ip.output->output_time();
|
|
405 }
|
|
406
|
|
407
|
|
408 /*
|
|
409 * Return song information
|
|
410 */
|
|
411 void xs_get_song_info(char *filename, char **title, int *length)
|
|
412 {
|
|
413 struct sidTuneInfo sidInf;
|
|
414 sidTune t(filename);
|
|
415
|
|
416 if (!t) return;
|
|
417
|
|
418 t.getInfo(sidInf);
|
|
419
|
|
420 *title = xs_make_filedesc(&sidInf);
|
|
421
|
|
422 *length = -1;
|
|
423 }
|
|
424
|
|
425
|