Mercurial > hg > xmms-sid
view src/xmms-sid.cc @ 35:3fcc147b253a
Updates towards 0.8beta0
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 19 Jun 2003 20:13:00 +0000 |
parents | 15250dd0c326 |
children | 1788f4ce6a44 |
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> 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 <pthread.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <errno.h> #include <xmms/plugin.h> #include <xmms/util.h> #include "xmms-sid.h" #include "xs_config.h" #include "xs_length.h" #include "xs_title.h" extern "C++" { #include <sidplay/player.h> #include <sidplay/myendian.h> #include <sidplay/fformat.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"); /* 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; /* 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; } /* Read song-length database */ if (xs_songlen_init() < 0) { XSERR("Error initializing song-length database!\n"); } /* 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 */ xs_plugin_ip.stop(); /* Close sub-song control window */ /* Free allocated memory */ xs_songlen_close(); // FIXME FIXME: STIL-entries } /* * 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; } /* * Main playing thread loop */ static void *xs_play_loop(void *argPointer) { guchar plr_data[XS_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_songlen_get(xs_status.s_fname, plr_tune_num); plr_tune_title = xs_filetitle_get(&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 (!xs_plugin_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 */ xs_plugin_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 = XS_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 ((xs_plugin_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 ((xs_plugin_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 */ xs_plugin_ip.add_vis_pcm(xs_plugin_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) && (xs_plugin_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) xs_plugin_ip.output->write_audio(plr_data, plr_fxlen); } /* End of playerloop */ pl_cleanup: XSDEBUG("cleaning up...\n"); /* Cleanup & shutdown */ xs_plugin_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); /* Initialize status information */ XSDEBUG("starting thread!\n"); 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); /* 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) { xs_plugin_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 xs_plugin_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_filetitle_get(&sidInf, sidInf.startSong); /* Get song length (in seconds), negative if no known length */ *songLength = xs_songlen_get(songFilename, sidInf.startSong); }