Mercurial > hg > xmms-sid
view src/xmms-sid.cc @ 69:bf6a524cf7ca dev-0-8-0a1
Fixes, cleanups, etc.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 30 Jun 2003 17:16:02 +0000 |
parents | bf7b647b3239 |
children |
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> extern "C" { #include <xmms/plugin.h> #include <xmms/util.h> #include <xmms/titlestring.h> #include "xs_interface.h" #include "xs_glade.h" } #include "xmms-sid.h" #include "xs_support.h" #include "xs_config.h" #include "xs_length.h" #ifdef HAVE_SIDPLAY1 #include <sidplay/player.h> #include <sidplay/myendian.h> #include <sidplay/fformat.h> #endif #ifdef HAVE_SIDPLAY2 #include <sidplay/sidplay2.h> #endif /* * Global variables */ #ifdef HAVE_SIDPLAY1 typedef sidTune t_xs_tune; typedef struct sidTuneInfo t_xs_tuneinfo; #endif #ifdef HAVE_SIDPLAY2 typedef SidTune t_xs_tune; typedef struct SidTuneInfo t_xs_tuneinfo; #endif #ifdef HAVE_SIDPLAY1 struct emuConfig xs_emuConf; emuEngine xs_emuEngine; #endif #ifdef HAVE_SIDPLAY2 struct sid2_config_t xs_emuConf; sidplay2 xs_emuEngine; #endif typedef struct { gboolean isError, isPlaying, allowNext; gint currSong, nSongs; t_xs_tune *currTune; gchar *currFileName; } t_xs_status; static GtkWidget *xs_ctrlwin = NULL; static pthread_t xs_decode_thread; static pthread_mutex_t xs_mutex = PTHREAD_MUTEX_INITIALIZER; struct t_xs_cfg xs_cfg; t_xs_status xs_status; gchar *xs_filetitle_get(gchar *, t_xs_tuneinfo *, gint); /* * Create sub-song control window */ void xs_ctrlwin_open(void) { /* Create sub-song control window */ if (xs_ctrlwin != NULL) { if (xs_cfg.alwaysRaise) gdk_window_raise(xs_ctrlwin->window); return; } xs_ctrlwin = create_xs_ctrlwin(); gtk_widget_show(xs_ctrlwin); } /* * Initialize XMMS-SID */ void xs_init(void) { XSDEBUG("xs_init()\n"); /* Initialize and get configuration */ memset(&xs_cfg, 0, sizeof(xs_cfg)); xs_read_configuration(); /* Initialize status */ memset(&xs_status, 0, sizeof(xs_status)); xs_status.allowNext = TRUE; // Initialize to TRUE to allow first song /* Try to initialize emulator engine(s) */ XSDEBUG("initializing emulator engine(s)...\n"); #ifdef HAVE_SIDPLAY1 if (!xs_emuEngine || !xs_emuEngine.verifyEndianess()) { XSERR("Couldn't initialize SIDPlay1 emulator engine!\n"); return; } #endif /* Read song-length database */ if (xs_cfg.songlenDBEnable && (xs_songlen_init() < 0)) { XSERR("Error initializing song-length database!\n"); } /* Initialize STIL structures */ // xs_ctrlwin_open(); // FIXME FIXME FIx ME XSDEBUG("OK\n"); } /* * Shut down XMMS-SID */ void xs_close(void) { XSDEBUG("xs_close(): shutting down...\n"); /* Stop playing */ xs_stop(); /* Shutdown libSIDPlay(s) */ #ifdef HAVE_SIDPLAY1 #endif #ifdef HAVE_SIDPLAY2 #endif /* Close sub-song control window */ /* Free allocated memory */ xs_songlen_close(); // FIXME FIXME: STIL-entries XSDEBUG("shutdown finished.\n"); } /* * Check whether the given file is handled by this plugin */ gint xs_is_our_file(gchar *pcFileName) { char *pcExt; t_xs_tune *testTune; /* Check the filename */ if (pcFileName == NULL) return FALSE; /* Try to detect via libSIDPlay's detection routine, if required */ if (xs_cfg.detectMagic) { testTune = new t_xs_tune(pcFileName); if (testTune == NULL) return FALSE; if (testTune->getStatus()) { delete testTune; return TRUE; } /* We DON'T fall back to filename extension checking ... */ delete testTune; return FALSE; } /* Detect just by checking filename extension */ pcExt = xs_strrchr(pcFileName, '.'); if (pcExt) { pcExt++; if (!g_strcasecmp(pcExt, "psid")) return TRUE; if (!g_strcasecmp(pcExt, "sid")) return TRUE; if (!g_strcasecmp(pcExt, "dat")) return TRUE; if (!g_strcasecmp(pcExt, "inf")) return TRUE; if (!g_strcasecmp(pcExt, "info")) return TRUE; } return FALSE; } /* * Main playing thread loop */ void *xs_play_loop(void *argPointer) { t_xs_status myStatus; t_xs_tuneinfo tuneInfo; gboolean audioOpen; gint audioFreq, audioChannels, songLength; enum AFormat audioFmt; gchar audioBuffer[XS_BUFSIZE]; gchar *tmpStr; pthread_mutex_lock(&xs_mutex); XSDEBUG("entering play thread\n"); /* No idea, if this is really required here, but better be * careful since we're dealing with EVIL threads ... */ if (!xs_status.allowNext) { pthread_mutex_unlock(&xs_mutex); pthread_exit(NULL); } /* Don't allow next song to be set yet */ xs_status.allowNext = FALSE; xs_status.isPlaying = TRUE; memcpy(&myStatus, &xs_status, sizeof(t_xs_status)); pthread_mutex_unlock(&xs_mutex); /* Copy and check audio options here (they might change in config while running) */ #ifdef HAVE_UNSIGNEDPCM audioFmt = (xs_cfg.fmtBitsPerSample == XS_RES_16BIT) ? FMT_U16_NE : FMT_U8; #else audioFmt = (xs_cfg.fmtBitsPerSample == XS_RES_16BIT) ? FMT_S16_NE : FMT_S8; #endif audioFreq = xs_cfg.fmtFrequency; audioChannels = (xs_cfg.fmtChannels == XS_CHN_MONO) ? 1 : 2; audioOpen = FALSE; /* * Main player loop: while not stopped, loop here - play subtunes */ while (xs_status.isPlaying) { pthread_mutex_lock(&xs_mutex); myStatus.currSong = xs_status.currSong; myStatus.isPlaying = TRUE; pthread_mutex_unlock(&xs_mutex); XSDEBUG("subtune #%i selected, initializing...\n", myStatus.currSong); /* Get song length for current subtune */ songLength = xs_songlen_get(myStatus.currFileName, myStatus.currSong); /* Initialize song */ #ifdef HAVE_SIDPLAY1 if ((myStatus.currTune == NULL) || !myStatus.currTune->getStatus() || !sidEmuInitializeSong(xs_emuEngine, *myStatus.currTune, myStatus.currSong)) #endif #ifdef HAVE_SIDPLAY2 if () #endif { XSERR("Couldn't initialize SID-tune '%s' (sub-tune #%i)!\n", myStatus.currFileName, myStatus.currSong); goto err_exit; } myStatus.currTune->getInfo(tuneInfo); /* Set information for current sub-tune */ tmpStr = xs_filetitle_get(myStatus.currFileName, &tuneInfo, myStatus.currSong); xs_plugin_ip.set_info(tmpStr, (songLength > 0) ? songLength * 1000 : -1, 1000 * (tuneInfo.songSpeed ? tuneInfo.songSpeed : (tuneInfo.clockSpeed == SIDTUNE_CLOCK_NTSC) ? 60 : 50), audioFreq, audioChannels); g_free(tmpStr); /* Open the audio output */ if (!xs_plugin_ip.output->open_audio(audioFmt, audioFreq, audioChannels)) { XSERR("Couldn't open XMMS audio output!\n"); pthread_mutex_lock(&xs_mutex); xs_status.isError = TRUE; pthread_mutex_unlock(&xs_mutex); goto err_exit; } audioOpen = TRUE; /* * Play the subtune */ while (xs_status.isPlaying && myStatus.isPlaying && (xs_status.currSong == myStatus.currSong)) { /* Render audio data */ #ifdef HAVE_SIDPLAY1 sidEmuFillBuffer(xs_emuEngine, *myStatus.currTune, audioBuffer, XS_BUFSIZE); #endif #ifdef HAVE_SIDPLAY2 #endif /* I <3 visualice/haujobb */ xs_plugin_ip.add_vis_pcm( xs_plugin_ip.output->written_time(), audioFmt, audioChannels, XS_BUFSIZE, audioBuffer); /* Wait a little */ while (xs_status.isPlaying && (xs_status.currSong == myStatus.currSong) && (xs_plugin_ip.output->buffer_free() < XS_BUFSIZE)) xmms_usleep(10000); /* Output audio */ if (xs_status.isPlaying && (xs_status.currSong == myStatus.currSong)) xs_plugin_ip.output->write_audio(audioBuffer, XS_BUFSIZE); /* Check if we have played enough */ if (xs_cfg.playMaxTimeEnable) { if (xs_cfg.playMaxTimeUnknown) { if ((songLength == -1) && (xs_plugin_ip.output->output_time() >= (xs_cfg.playMaxTime * 1000))) myStatus.isPlaying = FALSE; } else { if (xs_plugin_ip.output->output_time() >= (xs_cfg.playMaxTime * 1000)) myStatus.isPlaying = FALSE; } } if (songLength > 0) { if (xs_plugin_ip.output->output_time() >= (songLength * 1000)) myStatus.isPlaying = FALSE; } } XSDEBUG("subtune ended/stopped\n"); /* Close audio */ if (audioOpen) { XSDEBUG("close audio #1\n"); xs_plugin_ip.output->close_audio(); audioOpen = FALSE; } /* Now determine if we continue by selecting other subtune or something */ if (!myStatus.isPlaying) xs_status.isPlaying = FALSE; } /* When exiting, delete data */ err_exit: pthread_mutex_lock(&xs_mutex); xs_status.isPlaying = FALSE; pthread_mutex_unlock(&xs_mutex); if (audioOpen) { XSDEBUG("close audio #2\n"); xs_plugin_ip.output->close_audio(); } if (myStatus.currTune != NULL) { delete myStatus.currTune; myStatus.currTune = NULL; } g_free(myStatus.currFileName); /* Exit the playing thread */ XSDEBUG("exiting thread, bye.\n"); /* Last thing we do is set allowNext to TRUE to flag * that we have ended all action in the thread */ pthread_mutex_lock(&xs_mutex); xs_status.allowNext = TRUE; pthread_mutex_unlock(&xs_mutex); pthread_exit(NULL); } /* * Start playing the given file */ void xs_play_file(char *pcFileName) { t_xs_tune *newTune; t_xs_tuneinfo tuneInfo; /* Can we set the next tune? */ XSDEBUG("request to play '%s'\n", pcFileName); /* Try to get the tune */ newTune = new t_xs_tune(pcFileName); if (newTune == NULL) return; #ifdef HAVE_SIDPLAY1 /* Get current configuration */ xs_emuEngine.getConfig(xs_emuConf); /* Configure channels and stuff */ switch (xs_cfg.fmtChannels) { case XS_CHN_AUTOPAN: xs_emuConf.channels = SIDEMU_STEREO; xs_emuConf.autoPanning = SIDEMU_CENTEREDAUTOPANNING; xs_emuConf.volumeControl = SIDEMU_FULLPANNING; break; case XS_CHN_STEREO: xs_emuConf.channels = SIDEMU_STEREO; xs_emuConf.autoPanning = SIDEMU_NONE; xs_emuConf.volumeControl = SIDEMU_NONE; break; case XS_CHN_MONO: default: xs_emuConf.channels = SIDEMU_MONO; xs_emuConf.autoPanning = SIDEMU_NONE; xs_emuConf.volumeControl = SIDEMU_NONE; break; } /* Memory mode settings */ switch (xs_cfg.memoryMode) { case XS_MPU_BANK_SWITCHING: xs_emuConf.memoryMode = MPU_BANK_SWITCHING; break; case XS_MPU_TRANSPARENT_ROM: xs_emuConf.memoryMode = MPU_TRANSPARENT_ROM; break; case XS_MPU_PLAYSID_ENVIRONMENT: xs_emuConf.memoryMode = MPU_PLAYSID_ENVIRONMENT; break; default: xs_emuConf.memoryMode = MPU_BANK_SWITCHING; break; } /* Clockspeed settings */ switch (xs_cfg.clockSpeed) { case XS_CLOCK_NTSC: xs_emuConf.clockSpeed = SIDTUNE_CLOCK_NTSC; break; case XS_CLOCK_PAL: default: xs_emuConf.clockSpeed = SIDTUNE_CLOCK_PAL; break; } /* Configure rest of the emulation */ xs_emuConf.bitsPerSample = xs_cfg.fmtBitsPerSample; xs_emuConf.frequency = xs_cfg.fmtFrequency; #ifdef HAVE_UNSIGNEDPCM xs_emuConf.sampleFormat = SIDEMU_UNSIGNED_PCM; #else xs_emuConf.sampleFormat = SIDEMU_SIGNED_PCM; #endif xs_emuConf.mos8580 = xs_cfg.mos8580; xs_emuConf.emulateFilter = xs_cfg.emulateFilters; 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); #endif #ifdef HAVE_SIDPLAY2 /* Get current configuration */ xs_emuConf = xs_emuEngine.config(); /* Configure channels and stuff */ switch (xs_cfg.fmtChannels) { case XS_CHN_AUTOPAN: xs_emuConf.playback = sid2_stereo; break; case XS_CHN_STEREO: xs_emuConf.playback = sid2_stereo; break; case XS_CHN_MONO: default: xs_emuConf.playback = sid2_mono; break; } /* Memory mode settings */ switch (xs_cfg.memoryMode) { case XS_MPU_BANK_SWITCHING: xs_emuConf.environment = sid2_envBS; break; case XS_MPU_TRANSPARENT_ROM: xs_emuConf.environment = sid2_envTP; break; case XS_MPU_PLAYSID_ENVIRONMENT: xs_emuConf.environment = sid2_envPS; break; case XS_MPU_REAL: default: xs_emuConf.environment = sid2_envR; break; } /* Clockspeed settings */ switch (xs_cfg.clockSpeed) { case XS_CLOCK_NTSC: xs_emuConf.clockSpeed = xs_emuConf.clockDefault = SID2_CLOCK_NTSC; break; case XS_CLOCK_PAL: default: xs_emuConf.clockSpeed = xs_emuConf.clockDefault = SID2_CLOCK_PAL; break; } /* Configure rest of the emulation */ xs_emuConf.precision = xs_cfg.fmtBitsPerSample; xs_emuConf.frequency = xs_cfg.fmtFrequency; xs_emuConf.clockForced = xs_cfg.forceSpeed; xs_emuConf.optimisation = (xs_cfg.optimiseLevel) ? 1 : 0; xs_emuConf.sidDefault = xs_emuConf.sidModel = (xs_cfg.mos8580) ? SID2_MOS8580 : SID2_MOS6581; xs_emuConf.sidSamples = TRUE; // FIXME FIX ME, make configurable! #ifdef HAVE_UNSIGNEDPCM #ifdef WORDS_BIGENDIAN xs_emuConf.sampleFormat = SID2_BIG_UNSIGNED; #else xs_emuConf.sampleFormat = SID2_LITTLE_UNSIGNED; #endif #else #ifdef WORDS_BIGENDIAN xs_emuConf.sampleFormat = SID2_BIG_SIGNED; #else xs_emuConf.sampleFormat = SID2_LITTLE_SIGNED; #endif #endif /* Now set the emulator configuration */ xs_emuEngine.config(xs_emuConf); #endif /* Initialize status information */ XSDEBUG("starting playing thread!\n"); newTune->getInfo(tuneInfo); xs_status.isPlaying = TRUE; xs_status.isError = FALSE; xs_status.currSong = tuneInfo.startSong; xs_status.nSongs = tuneInfo.songs; xs_status.currTune = newTune; xs_status.currFileName = g_strdup(pcFileName); /* 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; } XSDEBUG("systems should be up?\n"); /* Exit */ } /* * Stop playing */ void xs_stop(void) { /* If playing, stop. */ XSDEBUG("STOP_REQ\n"); if (xs_status.isPlaying || !xs_status.allowNext) { XSDEBUG("stopping...\n"); pthread_mutex_lock(&xs_mutex); xs_status.isPlaying = FALSE; 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(gint iTime) { /* If we have song-position patch, check settings */ #ifdef HAVE_SONG_POSITION pthread_mutex_lock(&xs_mutex); if ((iTime > 0) && (iTime <= xs_status.nSongs) && xs_status.isPlaying) xs_status.currSong = iTime; pthread_mutex_unlock(&xs_mutex); #endif } /* * Return the playing "position/time" */ gint xs_get_time(void) { /* If errorflag is set, return -2 to signal it to XMMS's idle callback */ if (xs_status.isError) return -2; /* If tune has ended, return -1 */ if (xs_status.allowNext && !xs_status.isPlaying) return -1; /* Obsolete? */ #ifdef HAVE_SONG_POSITION pthread_mutex_lock(&xs_mutex); set_song_position(xs_status.currSong, 1, xs_status.nSongs); pthread_mutex_unlock(&xs_mutex); #endif /* Else, return output time reported by audio output plugin */ return xs_plugin_ip.output->output_time(); } /* * Return song information */ void xs_get_song_info(gchar *songFileName, gchar **songTitle, gint *songLength) { t_xs_tuneinfo tuneInfo; t_xs_tune *testTune; gint tmpInt; /* Check if the tune exists and is readable */ testTune = new t_xs_tune(songFileName); if (testTune == NULL) return; if (!testTune->getStatus()) { delete testTune; return; } /* Get general tune information */ testTune->getInfo(tuneInfo); delete testTune; /* Get titlestring */ *songTitle = xs_filetitle_get(songFileName, &tuneInfo, tuneInfo.startSong); /* Get song length (in milliseconds), negative if no known length */ tmpInt = xs_songlen_get(songFileName, tuneInfo.startSong); if (tmpInt >= 0) *songLength = (tmpInt * 1000); else *songLength = -1; } /* * Create the SID-tune description string from the tune's information * formatted by the user-specified format-string. */ gchar *xs_filetitle_get(gchar *pcFileName, t_xs_tuneinfo *pfInfo, gint iSubTune) { gint j, iLength; gchar *pcStr, *pcResult; #ifdef HAVE_XMMSEXTRA TitleInput *ptInput; #endif /* FIXME FIXME: get STIL-information */ /* Check the info strings */ if (pfInfo->numberOfInfoStrings < 3) { if (pfInfo->numberOfInfoStrings < 1) return 0; return g_strdup(pfInfo->infoString[0]); } #ifdef HAVE_XMMSEXTRA /* Check if the titles are overridden or not */ if (!xs_cfg.titleOverride) { /* Use generic XMMS titles */ /* XMMS_NEW_TITLEINPUT(ptInput); * We duplicate and add typecast to the code here due to XMMS's braindead headers */ ptInput = (TitleInput *) g_malloc0(sizeof(TitleInput)); ptInput->__size = XMMS_TITLEINPUT_SIZE; ptInput->__version = XMMS_TITLEINPUT_VERSION; /* Create the input fields */ ptInput->file_name = pfInfo->dataFileName; ptInput->file_ext = g_strdup("sid"); ptInput->file_path = pfInfo->path; ptInput->track_name = pfInfo->infoString[0]; ptInput->track_number = iSubTune; ptInput->album_name = NULL; ptInput->performer = pfInfo->infoString[1]; ptInput->date = g_strdup((pfInfo->sidModel == SIDTUNE_SIDMODEL_6581) ? "6581" : "8580"); ptInput->year = 0; ptInput->genre = g_strdup("SID-tune"); ptInput->comment = pfInfo->infoString[2]; /* Create the string */ pcResult = xmms_get_titlestring(xmms_get_gentitle_format(), ptInput); /* Dispose all allocated memory */ g_free(ptInput->file_ext); g_free(ptInput->date); g_free(ptInput->genre); g_free(ptInput); } else #endif { /* Estimate the length of the string */ pcStr = xs_cfg.titleFormat; iLength = 0; while (*pcStr) { if (*pcStr == '%') { switch (*(++pcStr)) { case '1': iLength += strlen(pfInfo->infoString[1]); break; case '2': iLength += strlen(pfInfo->infoString[0]); break; case '3': iLength += strlen(pfInfo->infoString[2]); break; case '4': iLength += strlen(pfInfo->formatString); break; case '%': iLength++; } } else iLength++; pcStr++; } /* Allocate memory */ pcResult = (gchar *) g_malloc(iLength + 2); if (pcResult == NULL) return g_strdup(pfInfo->infoString[0]); /* Create the string */ pcStr = xs_cfg.titleFormat; j = 0; while (*pcStr) { if (*pcStr == '%') { switch (*(++pcStr)) { case '1': xs_strpcat(pcResult, &j, pfInfo->infoString[1]); break; case '2': xs_strpcat(pcResult, &j, pfInfo->infoString[0]); break; case '3': xs_strpcat(pcResult, &j, pfInfo->infoString[2]); break; case '4': xs_strpcat(pcResult, &j, pfInfo->formatString); break; case '%': pcResult[j++] = '%'; break; } } else pcResult[j++] = *pcStr; pcStr++; } pcResult[j] = 0; } return pcResult; }