44
44
xs_status_t xs_status;
45
45
XS_MUTEX(xs_status);
46
static XS_THREAD_T xs_decode_thread;
48
47
static void xs_get_song_tuple_info(Tuple *pResult, xs_tuneinfo_t *pInfo, gint subTune);
53
void xs_error(const char *fmt, ...)
52
void xs_verror(const gchar *fmt, va_list ap)
57
54
g_logv("AUD-SID", G_LOG_LEVEL_WARNING, fmt, ap);
57
void xs_error(const gchar *fmt, ...)
62
void XSDEBUG(const char *fmt, ...)
66
void XSDEBUG(const gchar *fmt, ...)
180
* Check whether the given file is handled by this plugin
182
static gchar * xs_has_tracknumber(gchar *filename)
184
gchar *sep = xs_strrchr(filename, '?');
185
if (sep && g_ascii_isdigit(*(sep + 1)))
191
gboolean xs_get_trackinfo(const gchar *filename, gchar **result, gint *track)
195
*result = g_strdup(filename);
196
sep = xs_has_tracknumber(*result);
200
*track = atoi(sep + 1);
210
178
* Start playing the given file
212
180
void xs_play_file(InputPlayback *pb)
214
182
xs_tuneinfo_t *tmpTune;
215
183
gboolean audioOpen = FALSE;
216
gint audioGot, tmpLength, subTune;
217
gchar *tmpFilename, *audioBuffer = NULL, *oversampleBuffer = NULL, *tmpTitle;
184
gint audioBufSize, bufChunkSize, bufRemaining, tmpLength, subTune = -1;
185
gchar *tmpFilename, *bufPointer,
186
*audioBuffer = NULL, *oversampleBuffer = NULL;
221
190
assert(xs_status.sidPlayer != NULL);
223
192
XSDEBUG("play '%s'\n", pb->filename);
225
XS_MUTEX_LOCK(xs_status);
194
tmpFilename = aud_filename_split_subtune(pb->filename, &subTune);
195
if (tmpFilename == NULL) return;
227
197
/* Get tune information */
228
xs_get_trackinfo(pb->filename, &tmpFilename, &subTune);
198
XS_MUTEX_LOCK(xs_status);
229
199
if ((xs_status.tuneInfo = xs_status.sidPlayer->plrGetSIDInfo(tmpFilename)) == NULL) {
230
200
XS_MUTEX_UNLOCK(xs_status);
231
201
g_free(tmpFilename);
261
230
XSDEBUG("subtune #%i selected (#%d wanted), initializing...\n", xs_status.currSong, subTune);
265
xs_decode_thread = g_thread_self();
266
XSDEBUG("playing thread = %p\n", xs_decode_thread);
269
233
/* Allocate audio buffer */
270
audioBuffer = (gchar *) g_malloc(XS_AUDIOBUF_SIZE);
234
audioBufSize = (xs_status.audioFrequency * xs_status.audioChannels * xs_status.audioBitsPerSample) / (8 * 4);
235
if (audioBufSize < 512) audioBufSize = 512;
236
bufChunkSize = (xs_status.audioFrequency * xs_status.audioChannels * xs_status.audioBitsPerSample) / (8 * 50);
237
if (bufChunkSize < 512) bufChunkSize = 512;
239
audioBuffer = (gchar *) g_malloc(audioBufSize);
271
240
if (audioBuffer == NULL) {
272
241
xs_error("Couldn't allocate memory for audio data buffer!\n");
273
242
XS_MUTEX_UNLOCK(xs_status);
274
243
goto xs_err_exit;
277
246
if (xs_status.oversampleEnable) {
278
oversampleBuffer = (gchar *) g_malloc(XS_AUDIOBUF_SIZE * xs_status.oversampleFactor);
247
oversampleBuffer = (gchar *) g_malloc(audioBufSize * xs_status.oversampleFactor);
279
248
if (oversampleBuffer == NULL) {
280
249
xs_error("Couldn't allocate memory for audio oversampling buffer!\n");
281
250
XS_MUTEX_UNLOCK(xs_status);
317
286
audioOpen = TRUE;
319
pb->set_pb_ready(pb);
321
288
/* Set song information for current subtune */
322
289
XSDEBUG("foobar #1\n");
323
290
xs_status.sidPlayer->plrUpdateSIDInfo(&xs_status);
324
XS_MUTEX_UNLOCK(xs_status);
325
291
tmpTuple = aud_tuple_new_from_filename(tmpTune->sidFilename);
326
292
xs_get_song_tuple_info(tmpTuple, tmpTune, xs_status.currSong);
293
XS_MUTEX_UNLOCK(xs_status);
294
pb->set_tuple(pb, tmpTuple);
295
pb->set_params(pb, NULL, 0, -1, xs_status.audioFrequency, xs_status.audioChannels);
296
pb->set_pb_ready(pb);
328
tmpTitle = aud_tuple_formatter_process_string(tmpTuple,
329
xs_cfg.titleOverride ? xs_cfg.titleFormat : aud_get_gentitle_format());
331
XSDEBUG("foobar #4\n");
332
XS_MUTEX_LOCK(xs_status);
335
(tmpLength > 0) ? (tmpLength * 1000) : 0,
337
xs_status.audioFrequency,
338
xs_status.audioChannels);
342
298
XS_MUTEX_UNLOCK(xs_status);
343
299
XSDEBUG("playing\n");
344
300
while (pb->playing) {
345
301
/* Render audio data */
346
XS_MUTEX_LOCK(xs_status);
347
302
if (xs_status.oversampleEnable) {
348
303
/* Perform oversampled rendering */
349
audioGot = xs_status.sidPlayer->plrFillBuffer(
304
bufRemaining = xs_status.sidPlayer->plrFillBuffer(
351
306
oversampleBuffer,
352
(XS_AUDIOBUF_SIZE * xs_status.oversampleFactor));
307
(audioBufSize * xs_status.oversampleFactor));
354
audioGot /= xs_status.oversampleFactor;
309
bufRemaining /= xs_status.oversampleFactor;
356
311
/* Execute rate-conversion with filtering */
357
312
if (xs_filter_rateconv(audioBuffer, oversampleBuffer,
358
xs_status.audioFormat, xs_status.oversampleFactor, audioGot) < 0) {
313
xs_status.audioFormat, xs_status.oversampleFactor, bufRemaining) < 0) {
359
314
xs_error("Oversampling rate-conversion pass failed.\n");
360
315
pb->error = TRUE;
361
XS_MUTEX_UNLOCK(xs_status);
362
316
goto xs_err_exit;
365
audioGot = xs_status.sidPlayer->plrFillBuffer(
366
&xs_status, audioBuffer, XS_AUDIOBUF_SIZE);
319
bufRemaining = xs_status.sidPlayer->plrFillBuffer(
320
&xs_status, audioBuffer, audioBufSize);
369
323
/* I <3 visualice/haujobb */
370
pb->pass_audio(pb, xs_status.audioFormat, xs_status.audioChannels,
371
audioGot, audioBuffer, NULL);
373
XS_MUTEX_UNLOCK(xs_status);
324
bufPointer = audioBuffer;
325
while (bufRemaining > 0 && pb->playing) {
326
gint blockSize = MIN(bufChunkSize, bufRemaining);
328
pb->pass_audio(pb, xs_status.audioFormat,
329
xs_status.audioChannels,
330
blockSize, bufPointer, NULL);
376
while (pb->playing && pb->output->buffer_free() < audioGot)
332
bufPointer += blockSize;
333
bufRemaining -= blockSize;
379
336
/* Check if we have played enough */
380
XS_MUTEX_LOCK(xs_status);
381
337
if (xs_cfg.playMaxTimeEnable) {
382
338
if (xs_cfg.playMaxTimeUnknown) {
383
339
if (tmpLength < 0 &&
384
pb->output->output_time() >= xs_cfg.playMaxTime * 1000) {
340
pb->output->written_time() >= xs_cfg.playMaxTime * 1000)
385
341
pb->playing = FALSE;
386
xs_status.isPlaying = FALSE;
390
if (pb->output->output_time() >= xs_cfg.playMaxTime * 1000) {
343
if (pb->output->written_time() >= xs_cfg.playMaxTime * 1000)
391
344
pb->playing = FALSE;
392
xs_status.isPlaying = FALSE;
398
348
if (tmpLength >= 0) {
399
if (pb->output->output_time() >= tmpLength * 1000) {
349
if (pb->output->written_time() >= tmpLength * 1000)
400
350
pb->playing = FALSE;
401
xs_status.isPlaying = FALSE;
405
XS_MUTEX_UNLOCK(xs_status);
409
355
XSDEBUG("out of playing loop\n");
411
357
/* Close audio output plugin */
413
359
XSDEBUG("close audio #2\n");
455
403
if (pb != NULL && pb->playing) {
456
404
XSDEBUG("stopping...\n");
457
405
pb->playing = FALSE;
458
xs_status.isPlaying = FALSE;
459
406
XS_MUTEX_UNLOCK(xs_status);
460
XS_THREAD_JOIN(xs_decode_thread);
462
408
XS_MUTEX_UNLOCK(xs_status);
465
XSDEBUG("done, updating status\n");
467
/* Free tune information */
468
XS_MUTEX_LOCK(xs_status);
469
xs_status.sidPlayer->plrDeleteSID(&xs_status);
470
xs_tuneinfo_free(xs_status.tuneInfo);
471
xs_status.tuneInfo = NULL;
472
XS_MUTEX_UNLOCK(xs_status);
496
* Return the playing "position/time"
497
* Determine current position/time in song. Used by XMMS to update
498
* the song clock and position slider and MOST importantly to determine
499
* END OF SONG! Return value of -2 means error, XMMS opens an audio
500
* error dialog. -1 means end of song (if one was playing currently).
502
gint xs_get_time(InputPlayback *pb)
504
/* If errorflag is set, return -2 to signal it to XMMS's idle callback */
505
XS_MUTEX_LOCK(xs_status);
507
XS_MUTEX_UNLOCK(xs_status);
511
/* If there is no tune, return -1 */
512
if (!xs_status.tuneInfo) {
513
XS_MUTEX_UNLOCK(xs_status);
517
/* If tune has ended, return -1 */
519
XS_MUTEX_UNLOCK(xs_status);
523
XS_MUTEX_UNLOCK(xs_status);
525
/* Return output time reported by audio output plugin */
526
return pb->output->output_time();
531
436
* Return song information Tuple
533
static void xs_get_song_tuple_info(Tuple *pResult, xs_tuneinfo_t *pInfo, gint subTune)
438
static void xs_get_song_tuple_info(Tuple *tuple, xs_tuneinfo_t *info, gint subTune)
535
440
gchar *tmpStr, tmpStr2[64];
537
aud_tuple_associate_string(pResult, FIELD_TITLE, NULL, pInfo->sidName);
538
aud_tuple_associate_string(pResult, FIELD_ARTIST, NULL, pInfo->sidComposer);
539
aud_tuple_associate_string(pResult, FIELD_GENRE, NULL, "SID-tune");
540
aud_tuple_associate_string(pResult, FIELD_COPYRIGHT, NULL, pInfo->sidCopyright);
541
aud_tuple_associate_string(pResult, -1, "sid-format", pInfo->sidFormat);
442
aud_tuple_associate_string_rel(tuple, FIELD_TITLE, NULL, aud_str_to_utf8(info->sidName));
443
aud_tuple_associate_string_rel(tuple, FIELD_ARTIST, NULL, aud_str_to_utf8(info->sidComposer));
444
aud_tuple_associate_string_rel(tuple, FIELD_COPYRIGHT, NULL, aud_str_to_utf8(info->sidCopyright));
445
aud_tuple_associate_string(tuple, -1, "sid-format", info->sidFormat);
446
aud_tuple_associate_string(tuple, FIELD_CODEC, NULL, "Commodore 64 SID PlaySID/RSID");
543
switch (pInfo->sidModel) {
448
switch (info->sidModel) {
544
449
case XS_SIDMODEL_6581: tmpStr = "6581"; break;
545
450
case XS_SIDMODEL_8580: tmpStr = "8580"; break;
546
451
case XS_SIDMODEL_ANY: tmpStr = "ANY"; break;
547
452
default: tmpStr = "?"; break;
549
aud_tuple_associate_string(pResult, -1, "sid-model", tmpStr);
454
aud_tuple_associate_string(tuple, -1, "sid-model", tmpStr);
551
456
/* Get sub-tune information, if available */
552
if (subTune < 0 || pInfo->startTune > pInfo->nsubTunes)
553
subTune = pInfo->startTune;
555
if ((subTune > 0) && (subTune <= pInfo->nsubTunes)) {
556
gint tmpInt = pInfo->subTunes[subTune - 1].tuneLength;
557
aud_tuple_associate_int(pResult, FIELD_LENGTH, NULL, (tmpInt < 0) ? -1 : tmpInt * 1000);
559
tmpInt = pInfo->subTunes[subTune - 1].tuneSpeed;
457
if (subTune < 0 || info->startTune > info->nsubTunes)
458
subTune = info->startTune;
460
if (subTune > 0 && subTune <= info->nsubTunes) {
461
gint tmpInt = info->subTunes[subTune - 1].tuneLength;
462
aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, (tmpInt < 0) ? -1 : tmpInt * 1000);
464
tmpInt = info->subTunes[subTune - 1].tuneSpeed;
560
465
if (tmpInt > 0) {
561
466
switch (tmpInt) {
562
467
case XS_CLOCK_PAL: tmpStr = "PAL"; break;
575
aud_tuple_associate_string(pResult, -1, "sid-speed", tmpStr);
480
aud_tuple_associate_string(tuple, -1, "sid-speed", tmpStr);
579
aud_tuple_associate_int(pResult, FIELD_SUBSONG_NUM, NULL, pInfo->nsubTunes);
580
aud_tuple_associate_int(pResult, FIELD_SUBSONG_ID, NULL, subTune);
581
aud_tuple_associate_int(pResult, FIELD_TRACK_NUMBER, NULL, subTune);
484
aud_tuple_associate_int(tuple, FIELD_SUBSONG_NUM, NULL, info->nsubTunes);
485
aud_tuple_associate_int(tuple, FIELD_SUBSONG_ID, NULL, subTune);
486
aud_tuple_associate_int(tuple, FIELD_TRACK_NUMBER, NULL, subTune);
583
488
if (xs_cfg.titleOverride)
584
aud_tuple_associate_string(pResult, FIELD_FORMATTER, NULL, xs_cfg.titleFormat);
588
Tuple * xs_get_song_tuple(gchar *filename)
489
aud_tuple_associate_string(tuple, FIELD_FORMATTER, NULL, xs_cfg.titleFormat);
493
static void xs_fill_subtunes(Tuple *tuple, xs_tuneinfo_t *info)
497
tuple->subtunes = g_new(gint, info->nsubTunes);
499
for (found = count = 0; count < info->nsubTunes; count++) {
500
if (count + 1 == info->startTune || !xs_cfg.subAutoMinOnly ||
501
info->subTunes[count].tuneLength >= xs_cfg.subAutoMinTime)
502
tuple->subtunes[found++] = count + 1;
505
tuple->nsubtunes = found;
509
Tuple * xs_get_song_tuple(const gchar *filename)
591
512
gchar *tmpFilename;
592
xs_tuneinfo_t *tmpInfo;
595
516
/* Get information from URL */
596
xs_get_trackinfo(filename, &tmpFilename, &tmpTune);
517
tmpFilename = aud_filename_split_subtune(filename, &tune);
518
if (tmpFilename == NULL) return NULL;
598
result = aud_tuple_new_from_filename(tmpFilename);
520
tuple = aud_tuple_new_from_filename(tmpFilename);
600
522
g_free(tmpFilename);
526
if (xs_status.sidPlayer == NULL)
604
529
/* Get tune information from emulation engine */
605
530
XS_MUTEX_LOCK(xs_status);
606
tmpInfo = xs_status.sidPlayer->plrGetSIDInfo(tmpFilename);
531
info = xs_status.sidPlayer->plrGetSIDInfo(tmpFilename);
607
532
XS_MUTEX_UNLOCK(xs_status);
608
533
g_free(tmpFilename);
613
xs_get_song_tuple_info(result, tmpInfo, tmpTune);
614
xs_tuneinfo_free(tmpInfo);
538
xs_get_song_tuple_info(tuple, info, tune);
540
if (xs_cfg.subAutoEnable && info->nsubTunes > 1 && tune < 0)
541
xs_fill_subtunes(tuple, info);
543
xs_tuneinfo_free(info);
620
Tuple *xs_probe_for_tuple(gchar *filename, xs_file_t *fd)
549
Tuple * xs_probe_for_tuple(const gchar *filename, xs_file_t *fd)
623
552
gchar *tmpFilename;
624
xs_tuneinfo_t *tmpInfo;
627
assert(xs_status.sidPlayer != NULL);
629
if (filename == NULL)
556
if (xs_status.sidPlayer == NULL || filename == NULL)
632
559
XS_MUTEX_LOCK(xs_status);
637
564
XS_MUTEX_UNLOCK(xs_status);
640
566
/* Get information from URL */
641
xs_get_trackinfo(filename, &tmpFilename, &tmpTune);
642
result = aud_tuple_new_from_filename(tmpFilename);
567
tmpFilename = aud_filename_split_subtune(filename, &tune);
568
if (tmpFilename == NULL) return NULL;
570
tuple = aud_tuple_new_from_filename(tmpFilename);
644
572
g_free(tmpFilename);
648
576
/* Get tune information from emulation engine */
649
577
XS_MUTEX_LOCK(xs_status);
650
tmpInfo = xs_status.sidPlayer->plrGetSIDInfo(tmpFilename);
578
info = xs_status.sidPlayer->plrGetSIDInfo(tmpFilename);
651
579
XS_MUTEX_UNLOCK(xs_status);
652
580
g_free(tmpFilename);
657
xs_get_song_tuple_info(result, tmpInfo, tmpTune);
660
if (xs_cfg.subAutoEnable && tmpInfo->nsubTunes > 1 && tmpTune < 0) {
662
result->subtunes = g_new(gint, tmpInfo->nsubTunes);
663
for (n = 0, i = 1; i <= tmpInfo->nsubTunes; i++) {
664
gboolean doAdd = FALSE;
666
if (xs_cfg.subAutoMinOnly) {
667
if (i == tmpInfo->startTune ||
668
tmpInfo->subTunes[i - 1].tuneLength >= xs_cfg.subAutoMinTime)
673
if (doAdd) result->subtunes[n++] = i;
675
result->nsubtunes = n;
677
result->nsubtunes = 0;
679
xs_tuneinfo_free(tmpInfo);
585
xs_get_song_tuple_info(tuple, info, tune);
587
if (xs_cfg.subAutoEnable && info->nsubTunes > 1 && tune < 0)
588
xs_fill_subtunes(tuple, info);
590
xs_tuneinfo_free(info);