Mercurial > hg > xmms-sid
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; +} + +