diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/xmms-sid.cc	Tue Jun 03 10:23:04 2003 +0000
@@ -0,0 +1,425 @@
+/*
+   xmms-sid - SIDPlay input plugin for X MultiMedia System (XMMS)
+
+   Main source file
+
+   Originally by Willem Monsuwe <willem@stack.nl>
+   Additions, fixes, etc by Matti "ccr" Hamalainen <mhamalai@ratol.fi>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "xmms-sid.h"
+#include <sidplay/player.h>
+#include <sidplay/myendian.h>
+#include <sidplay/fformat.h>
+
+
+extern "C" {
+#include <pthread.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <xmms/plugin.h>
+#include <xmms/util.h>
+}
+
+
+/*
+ *	General variables
+ */
+static struct emuConfig	xs_emuConf;
+static emuEngine	xs_emuEngine;
+static pthread_t	xs_decode_thread;
+static int		xs_error = 0, xs_going = 0, xs_songs = 0;
+struct T_sid_cfg	xs_cfg;
+
+
+/*
+ *	Initialize xmms-sid plugin
+ */
+void xs_init(void)
+{
+
+	if (!xs_emuEngine) {
+		xs_error = 1;
+		XSERR("Couldn't start SIDPlay emulator engine!\n");
+		return;
+	}
+
+	if (!xs_emuEngine.verifyEndianess()) {
+		xs_error = 1;
+		XSERR("Wrong hardware endianess (SIDPlay error)!\n");
+		return;
+	}
+
+	// Initialize STIL structures
+	memset(&xs_stil_info, 0, sizeof(xs_stil_info));
+	xs_stil_clear();
+
+	// Get configuration
+	xs_get_configure();
+}
+
+
+/*
+ *	Special, custom hand-made strcpy with smooth leather coating.
+ */
+int xs_strcpy(char *dest, const char *src, unsigned int *j)
+{
+ unsigned int i;
+
+ if ((dest == NULL) || (src == NULL)) return -1;
+
+ for (i = 0; i < strlen(src); i++) {
+	dest[(*j)++] = src[i];
+	}
+
+ return 0;
+}
+
+
+/*
+	Create the SID-tune description string from the
+	tune's information formatted by the user-specified
+	format-string.
+*/
+static char * xs_make_filedesc(struct sidTuneInfo *s)
+{
+	unsigned int i, len, j;
+	char *result;
+
+	/* Check the info strings */
+	if (s->numberOfInfoStrings != 3) {
+		if (s->numberOfInfoStrings < 1) {
+			return 0;
+		}
+		return g_strdup(s->infoString[0]);
+	}
+
+	/* Check the format-string for NULL */
+	if (xs_cfg.fileInfo == NULL) {
+		return g_strdup_printf("%s - %s", s->nameString, s->authorString);
+		}
+
+	/* Pre-calculate the length of the result-string */
+	len = 2;
+	for (i = 0; i < strlen(xs_cfg.fileInfo); i++) {
+	if (xs_cfg.fileInfo[i] == '%') {
+		switch (xs_cfg.fileInfo[++i]) {
+		case '1': len += strlen(s->authorString); break;
+		case '2': len += strlen(s->nameString); break;
+		case '3': len += strlen(s->copyrightString); break;
+		case '4': len += strlen(s->formatString); break;
+		}
+	} else len++;
+	}
+
+	/* Allocate the result-string */
+	result = (char *) g_malloc(len);
+
+	/* Construct the final result info */
+	j = 0;
+	for (i = 0; i < strlen(xs_cfg.fileInfo); i++) {
+
+	if (xs_cfg.fileInfo[i] == '%') {
+		switch (xs_cfg.fileInfo[++i]) {
+		case '1': xs_strcpy(result, s->authorString, &j); break;
+		case '2': xs_strcpy(result, s->nameString, &j); break;
+		case '3': xs_strcpy(result, s->copyrightString, &j); break;
+		case '4': xs_strcpy(result, s->formatString, &j); break;
+		}
+
+		} else {
+		result[j++] = xs_cfg.fileInfo[i];
+		} /* if */
+
+	} /* for */
+
+	result[j] = '\0';
+
+	return result;
+}
+
+
+/*
+ *	Check whether the given file is handled by this plugin
+ */
+int xs_is_our_file(char *filename) 
+{
+	if (xs_cfg.detectMagic) {
+		sidTune *t = new sidTune(filename);
+
+		if (!t->getStatus()) {
+			delete t;
+			return FALSE;
+			}
+
+		delete t;
+		return TRUE;
+	}
+
+	char *ext = strrchr(filename, '.');
+	if (ext) {
+		ext++;
+		if (!strcasecmp(ext, "psid")) return TRUE;
+		if (!strcasecmp(ext, "sid")) return TRUE;
+		if (!strcasecmp(ext, "dat")) return TRUE;
+		if (!strcasecmp(ext, "inf")) return TRUE;
+		if (!strcasecmp(ext, "info")) return TRUE;
+	}
+	return FALSE;
+}
+
+
+/*
+ *	Playing thread loop function
+ */
+static void * xs_play_loop(void *arg)
+{
+	sidTune *tune = (sidTune *)arg;
+	char data[XMMS_SID_BUFSIZE];
+	int fxlen, tn;
+	struct sidTuneInfo sidInf;
+	char *name;
+	enum AFormat fmt = (xs_emuConf.bitsPerSample == 16) ? FMT_S16_NE : FMT_U8;
+	gint chn = xs_emuConf.channels;
+
+	tune->getInfo(sidInf);
+	name = xs_make_filedesc(&sidInf);
+
+play_loop_new_tune:
+	tn = xs_going;
+	if (tn <= 0) tn = 1;
+	if (!xmms_sid_ip.output->open_audio(fmt, xs_emuConf.frequency, chn))
+	{
+		xs_error = 1;
+		XSERR("Couldn't open XMMS audio output!\n");
+		delete tune;
+		pthread_exit(NULL);
+		xs_stop();
+	}
+
+	if (!sidEmuInitializeSong(xs_emuEngine, *tune, tn)) {
+		xs_error = 1;
+		XSERR("Couldn't initialize SIDPlay emulator engine!\n");
+		delete tune;
+		pthread_exit(NULL);
+		xs_stop();
+	}
+
+	tune->getInfo(sidInf);
+
+	xmms_sid_ip.set_info(name, -1,
+		1000 * (sidInf.songSpeed ? sidInf.songSpeed : (sidInf.clockSpeed == SIDTUNE_CLOCK_NTSC) ? 60 : 50),
+		xs_emuConf.frequency, chn);
+
+	while (xs_going == tn)
+	{
+		fxlen = XMMS_SID_BUFSIZE;
+		sidEmuFillBuffer(xs_emuEngine, *tune, data, fxlen);
+
+		xmms_sid_ip.add_vis_pcm(xmms_sid_ip.output->written_time(),
+					fmt, chn, fxlen, data);
+
+		while ((xs_going == tn) && (xmms_sid_ip.output->buffer_free() < fxlen))
+			xmms_usleep(10000);
+
+		if (xs_going == tn)
+			xmms_sid_ip.output->write_audio(data, fxlen);
+	}
+
+	/* Exit the playing thread */
+	xmms_sid_ip.output->close_audio();
+
+	if (xs_going) goto play_loop_new_tune;
+
+	g_free(name);
+
+	delete tune;
+
+	pthread_exit(NULL);
+}
+
+
+/*
+ *	Start playing the given file
+ */
+void xs_play_file(char *filename)
+{
+	sidTune *tune = new sidTune(filename);
+	struct sidTuneInfo sidInf;
+
+	/* Get current configuration */
+	xs_emuEngine.getConfig(xs_emuConf);
+
+	/* Configure channels and stuff */
+	switch (xs_cfg.channels) {
+
+	case XMMS_SID_CHN_AUTOPAN:
+		xs_emuConf.channels = SIDEMU_STEREO;
+		xs_emuConf.autoPanning = SIDEMU_CENTEREDAUTOPANNING;
+		xs_emuConf.volumeControl = SIDEMU_FULLPANNING;
+		break;
+
+	case XMMS_SID_CHN_STEREO:
+		xs_emuConf.channels = SIDEMU_STEREO;
+		xs_emuConf.autoPanning = SIDEMU_NONE;
+		xs_emuConf.volumeControl = SIDEMU_NONE;
+		break;
+
+	case XMMS_SID_CHN_MONO:
+		xs_emuConf.channels = SIDEMU_MONO;
+		xs_emuConf.autoPanning = SIDEMU_NONE;
+		xs_emuConf.volumeControl = SIDEMU_NONE;
+		break;
+
+	default:xs_error = 1;
+		XSERR("Internal: Invalid channels setting. Please report to author!\n");
+		delete tune;
+		break;
+	}
+
+	/* Memory mode settings */
+	switch (xs_cfg.memoryMode) {
+	case XMMS_SID_MPU_BANK_SWITCHING:
+		xs_emuConf.memoryMode = MPU_BANK_SWITCHING;
+		break;
+
+	case XMMS_SID_MPU_TRANSPARENT_ROM:
+		xs_emuConf.memoryMode = MPU_TRANSPARENT_ROM;
+		break;
+
+	case XMMS_SID_MPU_PLAYSID_ENVIRONMENT:
+		xs_emuConf.memoryMode = MPU_PLAYSID_ENVIRONMENT;
+		break;
+
+	default:xs_error = 1;
+		XSERR("Internal: Invalid memoryMode setting. Please report to author!\n");
+		delete tune;
+		break;
+	}
+
+
+	/* Clockspeed settings */
+	switch (xs_cfg.clockSpeed) {
+	case XMMS_SID_CLOCK_PAL:
+		xs_emuConf.clockSpeed = SIDTUNE_CLOCK_PAL;
+		break;
+
+	case XMMS_SID_CLOCK_NTSC:
+		xs_emuConf.clockSpeed = SIDTUNE_CLOCK_NTSC;
+		break;
+
+	default:xs_error = 1;
+		XSERR("Internal: Invalid clockSpeed setting. Please report to author!\n");
+		delete tune;
+		break;
+	}
+
+	/* Configure rest of the paske */
+	xs_emuConf.bitsPerSample	= xs_cfg.bitsPerSample;
+	xs_emuConf.frequency		= xs_cfg.frequency;
+	xs_emuConf.sampleFormat		= SIDEMU_SIGNED_PCM;
+	xs_emuConf.mos8580		= xs_cfg.mos8580;
+	xs_emuConf.emulateFilter	= xs_cfg.emulateFilter;
+	xs_emuConf.filterFs		= xs_cfg.filterFs;
+	xs_emuConf.filterFm		= xs_cfg.filterFm;
+	xs_emuConf.filterFt		= xs_cfg.filterFt;
+
+	/* Now set the emulator configuration */
+	xs_emuEngine.setConfig(xs_emuConf);
+	tune->getInfo(sidInf);
+	xs_error = 0;
+	xs_going = sidInf.startSong;
+	xs_songs = sidInf.songs;
+
+	/* Start the playing thread! */
+	if (pthread_create(&xs_decode_thread, NULL, xs_play_loop, tune) < 0) {
+		xs_error = 1;
+		XSERR("Couldn't start playing thread!\n");
+		delete tune;
+	}
+}
+
+
+/*
+ *	Stop playing
+ */
+void xs_stop(void)
+{
+	if (xs_going)
+	{
+		xs_going = 0;
+		pthread_join(xs_decode_thread, NULL);
+	}
+}
+
+
+/*
+ *	Pause the playing
+ */
+void xs_pause(short p)
+{
+	xmms_sid_ip.output->pause(p);
+}
+
+
+/*
+ *	Set the time-seek position
+ *	(the playing thread will do the "seeking" aka song-change)
+ */
+void xs_seek(int time)
+{
+	if ((time > 0) && (time <= xs_songs)) {
+		xs_going = time;
+	}
+}
+
+
+/*
+ *	Return the playing "position/time" aka song number
+ */
+int xs_get_time(void)
+{
+	if (xs_error) return -2;
+	if (!xs_going) return -1;
+#ifdef HAVE_SONG_POSITION
+	set_song_position(xs_going, 1, xs_songs);
+#endif
+	return xmms_sid_ip.output->output_time();
+}
+	
+
+/*
+ *	Return song information
+ */
+void xs_get_song_info(char *filename, char **title, int *length)
+{
+	struct sidTuneInfo sidInf;
+	sidTune t(filename);
+
+	if (!t) return;
+
+	t.getInfo(sidInf);
+
+	*title = xs_make_filedesc(&sidInf);
+
+	*length = -1;
+}
+
+