Mercurial > hg > xmms-sid
view 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 source
/* 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; }