Mercurial > hg > xmms-sid
comparison src/xmms-sid.cc @ 1:183e7cbc1036
Initial revision
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 03 Jun 2003 10:23:04 +0000 |
parents | |
children | 5b7009eef767 |
comparison
equal
deleted
inserted
replaced
0:5ce0a94edc2e | 1:183e7cbc1036 |
---|---|
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 |