view src/xmms-sid.cc @ 5:5b7009eef767

Updated to 0.8
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 03 Jun 2003 11:07:00 +0000
parents 183e7cbc1036
children ac2972a7ccd5
line wrap: on
line source

/*
   XMMS-SID - SIDPlay input plugin for X MultiMedia System (XMMS)

   Main source file

   Written by Matti "ccr" Hamalainen <ccr@tnsp.org>
   (few bits may still be by Willem Monsuwe)

   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 <errno.h>

#include <xmms/plugin.h>
#include <xmms/util.h>
}


/*
 *	Global variables
 */
static struct emuConfig	xs_emuConf;
static emuEngine	xs_emuEngine;
static pthread_t	xs_decode_thread;
struct t_xs_cfg		xs_cfg;

struct {
	int	s_error, s_playing, s_songs, s_allownext;
	sidTune	*s_tune;
	gchar	*s_fname;
} xs_status;

pthread_mutex_t xs_mutex = PTHREAD_MUTEX_INITIALIZER;


/*
 * Initialize XMMS-SID
 */
void xs_init(void)
{
 XSDEBUG("xs_init()\n");

 /* Try to initialize libSIDPlay */
 if (!xs_emuEngine)
	{
	XSERR("Couldn't start SIDPlay emulator engine!\n");
	return;
	}

 if (!xs_emuEngine.verifyEndianess())
	{
	XSERR("Wrong hardware endianess (SIDPlay error)!\n");
	return;
	}

 /* Initialize and get configuration */
 memset(&xs_cfg, 0, sizeof(xs_cfg));
 xs_get_configure();

 xs_status.s_error = 0;
 xs_status.s_playing = 0;
 xs_status.s_songs = 0;
 xs_status.s_allownext = 1;	// Initialize to TRUE to allow first song
 xs_status.s_tune = NULL;
 xs_status.s_fname = NULL;
	
 /* Initialize STIL structures */
// FIXME FIXME FIx ME

 /* Create sub-song control window */
// FIX ME FIXME
}


/*
 * Shut down XMMS-SID
 */
void xs_close(void)
{
 XSDEBUG("shutting down...\n");

 /* Stop playing */
 xmms_sid_ip.stop();

 /* Free allocated memory */

 // FIXME FIXME: STIL-entries, songlendb

}


/*
 *	Check whether the given file is handled by this plugin
 */
gint xs_is_our_file(char *fileName)
{
 char *pcExt;

 /* Check the filename */
 if (fileName == NULL)
	return FALSE;

 /* Try to detect via libSIDPlay's detection routine, if required */
 if (xs_cfg.detectMagic) {

	sidTune *testTune = new sidTune(fileName);

	if (!testTune) return FALSE;
	if (!testTune->getStatus())
		{
		delete testTune;
		return FALSE;
		}

	delete testTune;
	return TRUE;
	}


 /* Detect just by checking filename extension */
 pcExt = strrchr(fileName, '.');
 if (pcExt)
	{
	pcExt++;
	if (!strcasecmp(pcExt, "psid")) return TRUE;
	if (!strcasecmp(pcExt, "sid"))	return TRUE;
	if (!strcasecmp(pcExt, "dat"))	return TRUE;
	if (!strcasecmp(pcExt, "inf"))	return TRUE;
	if (!strcasecmp(pcExt, "info"))	return TRUE;
	}

 return FALSE;
}


/*
 *	Playing thread loop function
 */
static void *xs_play_loop(void *argPointer)
{
 gchar		plr_data[XMMS_SID_BUFSIZE];
 struct sidTuneInfo plr_sidInf;
 gchar		*plr_tune_title = NULL;
 gint		plr_fxlen;
 enum AFormat	plr_fmt;
 gint		plr_tune_num;
 gint32		plr_tune_len;
	

 /* Don't allow next song to be set yet */
 pthread_mutex_lock(&xs_mutex);
 xs_status.s_allownext = 0;
 pthread_mutex_unlock(&xs_mutex);


 /* Check tune number */
 plr_tune_num = xs_status.s_playing;

 if (plr_tune_num <= 0)
	plr_tune_num = 1;

XSDEBUG("xs_play_loop(%d, %d)\n", plr_tune_num, xs_status.s_playing);

 /* Get song information */
 xs_status.s_tune->getInfo(plr_sidInf);
 plr_tune_len = xs_get_length(xs_status.s_fname, plr_tune_num);
 plr_tune_title = xs_get_filetitle(&plr_sidInf, plr_tune_num);

XSDEBUG("title='%s', len=%d\n", plr_tune_title, plr_tune_len);


 /* Initialize audio output */
 // FIXME FIXME: FMT_S16_XXX -- different architechtures??
 // the patch may break something ...
 plr_fmt = (xs_emuConf.bitsPerSample == 16) ? FMT_S16_NE : FMT_U8;


 if (!xmms_sid_ip.output->open_audio(plr_fmt, xs_emuConf.frequency, xs_emuConf.channels))
	{
	pthread_mutex_lock(&xs_mutex);
	xs_status.s_error = 1;
	if (plr_tune_title) g_free(plr_tune_title);
	if (xs_status.s_tune) delete xs_status.s_tune;
	xs_status.s_allownext = 1;
	pthread_mutex_unlock(&xs_mutex);
	return NULL;
	}


 /* Initialize the SIDPlay-emulator for song */
 if (!sidEmuInitializeSong(xs_emuEngine, *xs_status.s_tune, plr_tune_num))
	{
	XSERR("Couldn't initialize SIDPlay emulator engine! This may be a problem with your sound settings, or possibly a bug in XMMS-SID.\n");
	pthread_mutex_lock(&xs_mutex);
	xs_status.s_error = 1;
	pthread_mutex_unlock(&xs_mutex);
	goto pl_cleanup;
	}


 /* Set song title information */
 xmms_sid_ip.set_info(
	plr_tune_title, 
	(plr_tune_len * 1000),
	(1000 * (plr_sidInf.songSpeed ? plr_sidInf.songSpeed : (plr_sidInf.clockSpeed == SIDTUNE_CLOCK_NTSC) ? 60 : 50)),
	xs_emuConf.frequency,
	xs_emuConf.channels);


 /* Run playing loop: loop as long as xs_playing is same as current tune number */
 while (xs_status.s_playing == plr_tune_num)
	{
	plr_fxlen = XMMS_SID_BUFSIZE;

	/* Run emulator to fill output buffer with audio data */
	sidEmuFillBuffer(xs_emuEngine, *xs_status.s_tune, plr_data, plr_fxlen);

	/* If Max Playtime option set, check playtime */
	if (xs_cfg.playUseMaxTime)
		{
		if ((xmms_sid_ip.output->output_time() / 1000) >= xs_cfg.playMaxTime)
			{
			pthread_mutex_lock(&xs_mutex);
			xs_status.s_playing = 0;
			pthread_mutex_unlock(&xs_mutex);
			}
		}

	/* Check playtime against database */
	if (xs_cfg.playMethod == XMMS_SID_PMETHOD_DATABASE)
		{
		if ((xmms_sid_ip.output->output_time() / 1000) >= plr_tune_len)
			{
			pthread_mutex_lock(&xs_mutex);
			xs_status.s_playing = 0;
			pthread_mutex_unlock(&xs_mutex);
			}

		}
#if 0
		else

	/* Check for silence */
	if (xs_cfg.playMethod == XMMS_SID_PMETHOD_MAXSILENCE)
		{
		}

	/* Add static noise */

	/* Muffle waveform (low-pass filter) */

#endif


		/* Send audio data to visualization plugin */
		xmms_sid_ip.add_vis_pcm(xmms_sid_ip.output->written_time(),
					plr_fmt, xs_emuConf.channels, plr_fxlen, plr_data);

		/* Wait for a while */
		while ((xs_status.s_playing == plr_tune_num) && (xmms_sid_ip.output->buffer_free() < plr_fxlen))
			xmms_usleep(10000);


		/* If playing, send final audio data to output plugin */
		if (xs_status.s_playing == plr_tune_num)
			xmms_sid_ip.output->write_audio(plr_data, plr_fxlen);

	}	/* End of playerloop */


pl_cleanup:
	XSDEBUG("cleaning up...\n");

	/* Cleanup & shutdown */
	xmms_sid_ip.output->close_audio();

	if (plr_tune_title) g_free(plr_tune_title);

	pthread_mutex_lock(&xs_mutex);
	xs_status.s_playing = 0;
	if (xs_status.s_tune) delete xs_status.s_tune;
	xs_status.s_allownext = 1;
	pthread_mutex_unlock(&xs_mutex);

	XSDEBUG("exiting thread, bye.\n");
	return NULL;
}


/*
 *	Start playing the given file
 */
void xs_play_file(char *fileName)
{
 sidTune *newTune;
 struct sidTuneInfo sidInf;

 XSDEBUG("request to start '%s'\n", fileName);

 /* Wait until the previous song has finished for sure */
 if (!xs_status.s_allownext)
	pthread_join(xs_decode_thread, NULL);

 /* Try to get the tune */
 newTune = new sidTune(fileName);
 if (newTune == NULL) return;

 XSDEBUG("tune ok, status %i\n", xs_status.s_playing);

 /* Get current configuration */
 xs_emuEngine.getConfig(xs_emuConf);


 /* Configure channels and stuff */
 switch (xs_cfg.fmtChannels) {

	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:
		XSERR("Internal: Invalid channels setting. Possibly corrupted configuration file.\n");
		delete newTune;
		return;
 }

 /* 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:
		XSERR("Internal: Invalid memoryMode setting. Possibly corrupted configuration file.\n");
		delete newTune;
		return;
 }


 /* 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:
		XSERR("Internal: Invalid clockSpeed setting. Possibly corrupted configuration file.\n");
		delete newTune;
		return;
 }


 /* Configure rest of the emulation */
 xs_emuConf.bitsPerSample = xs_cfg.fmtBitsPerSample;
 xs_emuConf.frequency = xs_cfg.fmtFrequency;
 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;

 XSDEBUG("configuring engine..\n");

 /* Now set the emulator configuration */
 xs_emuEngine.setConfig(xs_emuConf);
 newTune->getInfo(sidInf);

 pthread_mutex_lock(&xs_mutex);
 xs_status.s_error = 0;
 xs_status.s_playing = sidInf.startSong;
 xs_status.s_songs = sidInf.songs;
 xs_status.s_tune = newTune;
 pthread_mutex_unlock(&xs_mutex);

 XSDEBUG("starting thread!\n");

 /* Start the playing thread! */
 if (pthread_create(&xs_decode_thread, NULL, xs_play_loop, NULL) < 0)
	{
	XSERR("Couldn't start playing thread! Possible reason reported by system: %s\n", strerror(errno));
	delete newTune;
	}

 /* Exit */
}


/*
 * Stop playing
 */
void xs_stop(void)
{
 /* If playing, stop. */
 if (xs_status.s_playing)
	{
	pthread_mutex_lock(&xs_mutex);
	xs_status.s_playing = 0;
	pthread_mutex_unlock(&xs_mutex);

	pthread_join(xs_decode_thread, NULL);
	}
}


/*
 * Pause the playing
 */
void xs_pause(short pauseState)
{
 xmms_sid_ip.output->pause(pauseState);
}


/*
 * Set the time-seek position
 * (the playing thread will do the "seeking" aka song-change)
 */
void xs_seek(int iTime)
{
#ifdef HAVE_SONG_POSITION
 if ((iTime > 0) && (iTime <= xs_songs))
	{
	pthread_mutex_lock(&xs_mutex);
	xs_status.s_playing = iTime;
	pthread_mutex_unlock(&xs_mutex);
	}
#endif
}


/*
 * Return the playing "position/time"
 */
int xs_get_time(void)
{
 if (xs_status.s_error)
	return -2;

 if (!xs_status.s_playing)
	return -1;

#ifdef HAVE_SONG_POSITION
 set_song_position(xs_status.s_playing, 1, xs_status.s_songs);
#endif

 return xmms_sid_ip.output->output_time();
}


/*
 * Return song information
 */
void xs_get_song_info(char *songFilename, char **songTitle, int *songLength)
{
 struct sidTuneInfo sidInf;
 sidTune *testTune = new sidTune(songFilename);

 /* Check if the tune exists and is readable */
 if (!testTune) return;
 if (!testTune->getStatus())
	{
	delete testTune;
	return;
	}

 /* Get general tune information */
 testTune->getInfo(sidInf);
 delete testTune;

 /* Get titlestring */
 *songTitle = xs_get_filetitle(&sidInf, sidInf.startSong);

 /* Get song length (in seconds), negative if no known length */
 *songLength = xs_get_length(songFilename, sidInf.startSong);
}