Mercurial > hg > xmms-sid
view src/xmms-sid.c @ 75:653c9b0d1320
SIDPlay2 support "works" now. Borked problems with threads.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 15 Sep 2003 11:10:03 +0000 |
parents | 8cb66a3f75f7 |
children | ab522ab65c85 |
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_support.h" #include "xs_config.h" #include "xs_length.h" #include "xs_interface.h" #include "xs_glade.h" /* * Include player engines */ #ifdef HAVE_SIDPLAY1 #include "xs_sidplay1.h" #endif #ifdef HAVE_SIDPLAY2 #include "xs_sidplay2.h" #endif typedef struct { gint plrIdent; gboolean (*plrIsOurFile)(gchar *); gboolean (*plrInit)(t_xs_status *); void (*plrClose)(t_xs_status *); gboolean (*plrInitSong)(t_xs_status *); gboolean (*plrFillBuffer)(t_xs_status *, gchar *, gint); gboolean (*plrLoadSID)(t_xs_status *, gchar *); gint (*plrGetTuneSpeed)(t_xs_status *); void (*plrDeleteSID)(t_xs_status *); void (*plrGetSIDInfo)(gchar *, gchar **, gint *); } t_xs_player; t_xs_player xs_playerlist[] = { #ifdef HAVE_SIDPLAY1 { XS_ENG_SIDPLAY1, xs_sidplay1_isourfile, xs_sidplay1_init, xs_sidplay1_close, xs_sidplay1_initsong, xs_sidplay1_fillbuffer, xs_sidplay1_loadsid, xs_sidplay1_gettunespeed, xs_sidplay1_deletesid, xs_sidplay1_getsidinfo }, #endif #ifdef HAVE_SIDPLAY2 { XS_ENG_SIDPLAY2, xs_sidplay2_isourfile, xs_sidplay2_init, xs_sidplay2_close, xs_sidplay2_initsong, xs_sidplay2_fillbuffer, xs_sidplay2_loadsid, xs_sidplay2_gettunespeed, xs_sidplay2_deletesid, xs_sidplay2_getsidinfo }, #endif #ifdef HAVE_NANOSID { XS_ENG_NANOSID, xs_nanosid_isourfile, xs_nanosid_init, xs_nanosid_close, xs_nanosid_initsong, xs_nanosid_fillbuffer, xs_nanosid_loadsid, xs_nanosid_gettunespeed, xs_nanosid_deletesid, xs_nanosid_getsidinfo }, #endif }; const gint xs_nplayerlist = (sizeof(xs_playerlist) / sizeof(t_xs_player)); /* * Global variables */ 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; t_xs_player *xs_player; /* * 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) { gint iPlayer; gboolean isInitialized; 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; /* Try to initialize emulator engine */ XSDEBUG("initializing emulator engine...\n"); iPlayer = 0; isInitialized = FALSE; while ((iPlayer < xs_nplayerlist) && !isInitialized) { if (xs_playerlist[iPlayer].plrIdent == xs_cfg.playerEngine) { if (xs_playerlist[iPlayer].plrInit(&xs_status)) { isInitialized = TRUE; xs_player = (t_xs_player *) &xs_playerlist[iPlayer]; } } iPlayer++; } iPlayer = 0; while ((iPlayer < xs_nplayerlist) && !isInitialized) { if (xs_playerlist[iPlayer].plrInit(&xs_status)) { isInitialized = TRUE; xs_player = (t_xs_player *) &xs_playerlist[iPlayer]; } iPlayer++; } /* 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(); xs_player->plrDeleteSID(&xs_status); xs_player->plrClose(&xs_status); /* 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; /* Check the filename */ if (pcFileName == NULL) return FALSE; /* Try to detect via detection routine, if required */ if (xs_cfg.detectMagic && xs_player->plrIsOurFile(pcFileName)) return TRUE; /* Detect just by checking filename extension */ pcExt = strrchr(pcFileName, '.'); 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 */ void *xs_play_loop(void *argPointer) { t_xs_status myStatus; gboolean audioOpen, doPlay; gint audioFreq, audioChannels, songLength, audioFmt; gchar audioBuffer[XS_BUFSIZE]; /* Initialize */ pthread_mutex_lock(&xs_mutex); XSDEBUG("entering play thread\n"); 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 */ doPlay = TRUE; while (xs_status.isPlaying && doPlay) { pthread_mutex_lock(&xs_mutex); myStatus.currSong = xs_status.currSong; myStatus.isPlaying = TRUE; xs_status.allowNext = TRUE; pthread_mutex_unlock(&xs_mutex); XSDEBUG("subtune #%i selected, initializing...\n", myStatus.currSong); /* Initialize song */ if (!xs_player->plrInitSong(&myStatus)) { XSERR("Couldn't initialize SID-tune '%s' (sub-tune #%i)!\n", myStatus.currFileName, myStatus.currSong); goto err_exit; } /* Get song information for current subtune */ songLength = xs_songlen_get(myStatus.currFileName, myStatus.currSong); /* xs_plugin_ip.set_info( myTuneInfo.titleStr, (songLength > 0) ? (songLength * 1000) : -1, 1000 * myTuneInfo.songSpeed, audioFreq, audioChannels); */ /* 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 */ xs_player->plrFillBuffer(&myStatus, audioBuffer, XS_BUFSIZE); /* 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) doPlay = FALSE; } err_exit: /* Close audio */ if (audioOpen) { XSDEBUG("close audio #2\n"); xs_plugin_ip.output->close_audio(); } /* Exit the playing thread */ XSDEBUG("exiting thread, bye.\n"); pthread_mutex_lock(&xs_mutex); xs_player->plrDeleteSID(&myStatus); if (xs_status.currFileName) { g_free(xs_status.currFileName); xs_status.currFileName = NULL; } xs_status.isPlaying = FALSE; xs_status.allowNext = TRUE; pthread_mutex_unlock(&xs_mutex); pthread_exit(NULL); } /* * Start playing the given file */ void xs_play_file(gchar *pcFileName) { while (!xs_status.allowNext) usleep(5000); pthread_mutex_lock(&xs_mutex); xs_status.allowNext = FALSE; pthread_mutex_unlock(&xs_mutex); fprintf(stderr, "\n\n"); XSDEBUG("play '%s'\n", pcFileName); /* Initialize the tune */ if (!xs_player->plrLoadSID(&xs_status, pcFileName)) return; /* Set general status information */ xs_status.isPlaying = FALSE; xs_status.isError = FALSE; 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)); xs_player->plrDeleteSID(&xs_status); } XSDEBUG("systems should be up?\n"); } /* * Stop playing */ void xs_stop(void) { /* If playing, stop. */ XSDEBUG("STOP_REQ\n"); if (xs_status.isPlaying) { 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.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) { xs_player->plrGetSIDInfo(songFileName, songTitle, songLength); }