Mercurial > hg > xmms-sid
view src/xs_sidplayfp.cpp @ 957:0e60e5d56fdd
Change how the backend emulator library is initialized for libSIDPlay2 and
FP, as it seems the engine configuration has some persistence despite
reconfiguration between loaded files if same engine object is retained. This
caused, for example, 2SID stereo tunes being played "mono" if played after a
normal 1-SID tune. Duh.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 20 Nov 2012 22:13:48 +0200 |
parents | a5b118c853f5 |
children | 32435407eb9c |
line wrap: on
line source
/* XMMS-SID - SIDPlay input plugin for X MultiMedia System (XMMS) libSIDPlay2-FP support Programmed and designed by Matti 'ccr' Hamalainen <ccr@tnsp.org> (C) Copyright 1999-2012 Tecnic Software productions (TNSP) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "xmms-sid.h" #ifdef HAVE_SIDPLAYFP #include "xs_sidplayfp.h" #include "xs_slsup.h" #include "xs_config.h" #ifdef HAVE_SIDPLAYFP_V1 # include <sidplayfp/SidTune.h> # include <sidplayfp/sidplayfp.h> # include <sidplayfp/event.h> # include <sidplayfp/SidConfig.h> # include <sidplayfp/SidInfo.h> #else # include <sidplayfp/sidplay2.h> # include <sidplayfp/SidTuneMod.h> # include <sidplayfp/event.h> #endif class XSSIDPlayFP { public: #ifdef HAVE_SIDPLAYFP_V1 sidplayfp emu; SidConfig config; #else sidplay2 emu; sid2_config_t config; #endif SidTune tune; XSSIDPlayFP(void); virtual ~XSSIDPlayFP(void); }; static guint8 *xs_rom_imagedata[XS_C64_ROM_IMAGES]; #ifdef HAVE_RESID_FP_BUILDER # include <sidplayfp/builders/residfp.h> #endif #ifdef HAVE_RESID_FP_BUILDER # include <sidplayfp/builders/resid.h> #endif #ifdef HAVE_HARDSID_BUILDER # include <sidplayfp/builders/hardsid.h> #endif XSSIDPlayFP::XSSIDPlayFP(void) : tune(0) { emu.load(NULL); } XSSIDPlayFP::~XSSIDPlayFP(void) { emu.load(NULL); } /* We need to 'export' all this pseudo-C++ crap */ extern "C" { /* Return song information */ #ifdef HAVE_SIDPLAYFP_V1 #define sid2_mono SidConfig::MONO #define sid2_stereo SidConfig::STEREO #define SID2_INTERPOLATE SidConfig::INTERPOLATE #define SID2_RESAMPLE_INTERPOLATE SidConfig::RESAMPLE_INTERPOLATE #define SID2_MOS8580 SidConfig::MOS8580 #define SID2_MOS6581 SidConfig::MOS6581 #define SID2_CLOCK_PAL SidConfig::CLOCK_PAL #define SID2_CLOCK_NTSC SidConfig::CLOCK_NTSC #define SIDTUNE_CLOCK_UNKNOWN SidTuneInfo::CLOCK_UNKNOWN #define SIDTUNE_CLOCK_PAL SidTuneInfo::CLOCK_PAL #define SIDTUNE_CLOCK_NTSC SidTuneInfo::CLOCK_NTSC #define SIDTUNE_CLOCK_ANY SidTuneInfo::CLOCK_ANY #define SIDTUNE_SPEED_VBI SidTuneInfo::SPEED_VBI #define SIDTUNE_SPEED_CIA_1A SidTuneInfo::SPEED_CIA_1A #endif /* Check if we can play the given file */ gboolean xs_sidplayfp_probe(XSFile *f) { gchar probe[16]; if (f == NULL || !xs_fread_str(f, (guint8 *) probe, sizeof(probe))) return FALSE; // Basically support all variants .. if (!strncmp(probe, "PSID", 4) && probe[4] == 0 && probe[5] >= 1 && probe[5] <= 3) return TRUE; else if (!strncmp(probe, "RSID", 4) && probe[4] == 0 && probe[5] >= 2 && probe[5] <= 3) return TRUE; else return FALSE; } /* Initialize SIDPlayFP */ gboolean xs_sidplayfp_init(XSEngineState * state) { assert(state); XSDEBUG("SIDPlayFP backend initializing.\n"); memset(xs_rom_imagedata, 0, sizeof(xs_rom_imagedata)); if (!xs_load_rom_images(xs_rom_imagedata)) return FALSE; return TRUE; } /* Close SIDPlayFP engine */ void xs_sidplayfp_close(XSEngineState * state) { XSSIDPlayFP *engine = (XSSIDPlayFP *) state->internal; XSDEBUG("SIDPlayFP backend shutdown.\n"); xs_sidplayfp_delete(state); } /* Initialize current song and sub-tune */ gboolean xs_sidplayfp_initsong(XSEngineState * state) { XSSIDPlayFP *engine = (XSSIDPlayFP *) state->internal; if (!engine) return FALSE; if (!engine->tune.selectSong(state->currSong)) { xs_error("[SIDPlayFP] tune.selectSong() failed\n"); return FALSE; } if (engine->emu.load(&(engine->tune)) < 0) { xs_error("[SIDPlayFP] emu.load() failed\n"); return FALSE; } if (engine->emu.config(engine->config) < 0) { xs_error("[SIDPlayFP] Emulator engine configuration failed!\n"); return FALSE; } return TRUE; } /* Emulate and render audio data to given buffer */ guint xs_sidplayfp_fillbuffer(XSEngineState * state, gchar * audioBuffer, guint audioBufSize) { XSSIDPlayFP *engine = (XSSIDPlayFP *) state->internal; if (!engine) return 0; return engine->emu.play((short *) audioBuffer, audioBufSize / sizeof(short)) * sizeof(short); } /* Load a given SID-tune file */ gboolean xs_sidplayfp_load(XSEngineState * state, gchar * filename) { XSSIDPlayFP *engine; guint8 *buf = NULL; size_t bufSize; /* Allocate internal structures */ engine = new XSSIDPlayFP(); state->internal = engine; if (!engine) { xs_error("Allocating XSSIDPlayFP compound backend object failed.\n"); goto error; } /* Get current configuration */ XSDEBUG("SIDPlayFP emulation configuration\n"); engine->config = engine->emu.config(); /* Configure channels and stuff */ engine->config.playback = (state->audioChannels == XS_CHN_MONO) ? sid2_mono : sid2_stereo; /* Audio parameters sanity checking and setup */ state->audioBitsPerSample = XS_RES_16BIT; state->audioFormat = FMT_S16_NE; engine->config.frequency = state->audioFrequency; switch (xs_cfg.residSampling) { case XS_RESID_RESAMPLE_FIR: engine->config.samplingMethod = SID2_RESAMPLE_INTERPOLATE; break; case XS_RESID_INTERPOLATE: default: xs_cfg.residSampling = XS_RESID_INTERPOLATE; engine->config.samplingMethod = SID2_INTERPOLATE; break; } /* Clockspeed settings */ switch (xs_cfg.clockSpeed) { case XS_CLOCK_NTSC: engine->config.clockDefault = SID2_CLOCK_NTSC; break; default: case XS_CLOCK_PAL: engine->config.clockDefault = SID2_CLOCK_PAL; xs_cfg.clockSpeed = XS_CLOCK_PAL; break; } /* Configure rest of the emulation */ engine->config.sidDefault = xs_cfg.mos8580 ? SID2_MOS8580 : SID2_MOS6581; engine->config.clockForced = xs_cfg.forceSpeed; #ifndef HAVE_SIDPLAYFP_V1 engine->config.sidSamples = TRUE; engine->config.sidModel = xs_cfg.forceModel ? engine->config.sidDefault : SID2_MODEL_CORRECT; engine->config.clockSpeed = xs_cfg.forceSpeed ? engine->config.clockDefault : SID2_CLOCK_CORRECT; /* Initialize builder object */ XSDEBUG("init builder #%i, maxsids=%i\n", xs_cfg.sid2Builder, (engine->emu.info()).maxsids); #else XSDEBUG("init builder #%i, maxsids=%i\n", xs_cfg.sid2Builder, (engine->emu.info()).maxsids()); #endif switch (xs_cfg.sid2Builder) { #ifdef HAVE_RESID_BUILDER case XS_BLD_RESID: { ReSIDBuilder *rs = new ReSIDBuilder("ReSID builder"); #ifdef HAVE_SIDPLAYFP_V1 if (rs && rs->getStatus()) { engine->config.sidEmulation = rs; if (!rs->getStatus()) goto error; rs->create((engine->emu.info()).maxsids()); if (!rs->getStatus()) goto error; } #else if (rs && *rs) { engine->config.sidEmulation = rs; if (!*rs) goto error; rs->create((engine->emu.info()).maxsids); if (!*rs) goto error; } #endif rs->bias(0.0f); } break; #endif // HAVE_RESID_BUILDER #ifdef HAVE_RESID_FP_BUILDER case XS_BLD_RESID_FP: { ReSIDfpBuilder *rs = new ReSIDfpBuilder("ReSID builder FP!"); #ifdef HAVE_SIDPLAYFP_V1 if (rs && rs->getStatus()) { engine->config.sidEmulation = rs; if (!rs->getStatus()) goto error; rs->create((engine->emu.info()).maxsids()); if (!rs->getStatus()) goto error; } #else if (rs && *rs) { engine->config.sidEmulation = rs; if (!*rs) goto error; rs->create((engine->emu.info()).maxsids); if (!*rs) goto error; } #endif // rs->filter6581Curve(0.0); // rs->filter8580Curve(0.0); } break; #endif #ifdef HAVE_HARDSID_BUILDER case XS_BLD_HARDSID: { HardSIDBuilder *hs = new HardSIDBuilder("HardSID builder (FP)"); engine->config.sidEmulation = (sidbuilder *) hs; #ifdef HAVE_SIDPLAYFP_V1 if (hs && hs->getStatus()) { hs->create((engine->emu.info()).maxsids()); if (!hs->getStatus()) goto error; } #else if (hs && *hs) { hs->create((engine->emu.info()).maxsids); if (!*hs) goto error; } #endif } break; #endif default: xs_error("[SIDPlayFP] Invalid or unsupported builder selected.\n"); goto error; } if (!engine->config.sidEmulation) { xs_error("[SIDPlayFP] Could not initialize SIDBuilder object.\n"); goto error; } // Setup filter engine->config.sidEmulation->filter(xs_cfg.emulateFilters); #ifdef HAVE_SIDPLAYFP_V1 if (!engine->config.sidEmulation->getStatus()) #else if (!*(engine->config.sidEmulation)) #endif { xs_error("builder->filter(%d) failed.\n", xs_cfg.emulateFilters); goto error; } XSDEBUG("%s\n", engine->config.sidEmulation->credits()); #ifdef HAVE_SIDPLAYFP_V1 engine->emu.setRoms(xs_rom_imagedata[0], xs_rom_imagedata[1], xs_rom_imagedata[2]); #endif if (!xs_fload_buffer(filename, &buf, &bufSize, XS_SIDBUF_SIZE, TRUE)) goto error; engine->tune.read(buf, bufSize); #ifdef HAVE_SIDPLAYFP_V1 if (!engine->tune.getStatus()) #else if (!engine->tune) #endif { xs_error("Could not load file '%s'\n", filename); goto error; } g_free(buf); return TRUE; error: if (engine) delete engine; state->internal = NULL; g_free(buf); return FALSE; } /* Delete INTERNAL information */ void xs_sidplayfp_delete(XSEngineState * state) { XSSIDPlayFP *engine = (XSSIDPlayFP *) state->internal; if (engine) delete engine; state->internal = NULL; } /* Hardware backend flushing */ void xs_sidplayfp_flush(XSEngineState * state) { XSSIDPlayFP *engine = (XSSIDPlayFP *) state->internal; #ifdef HAVE_HARDSID_BUILDER if (xs_cfg.sid2Builder == XS_BLD_HARDSID) { ((HardSIDBuilder *) engine->config.sidEmulation)->flush(); } #endif } /* This function gets most of the information, though we do miss some * (those variables that are only set by libSIDPlay when tune is initialized). * Rest of the information is acquired in xs_sidplayfp_updateinfo() */ XSTuneInfo *xs_sidplayfp_getinfo(const gchar *filename) { XSTuneInfo *res = NULL; SidTune *tune = NULL; guint8 *buf = NULL; size_t bufSize; /* Check if the tune exists and is readable */ if (!xs_fload_buffer(filename, &buf, &bufSize, XS_SIDBUF_SIZE, TRUE)) { XSDEBUG("could not load file '%s'.\n", filename); goto error; } if ((tune = new SidTune(buf, bufSize)) == NULL) { XSDEBUG("could not initialize tune from '%s'.\n", filename); goto error; } if (!tune->getStatus()) { XSDEBUG("tune->getStatus() returned false for '%s'.\n", filename); goto error; } /* Get general tune information */ { #ifdef HAVE_SIDPLAYFP_V1 const SidTuneInfo *info = tune->getInfo(); res = xs_tuneinfo_new(filename, info->songs(), info->startSong(), info->infoString(0), info->infoString(1), info->infoString(2), info->loadAddr(), info->initAddr(), info->playAddr(), info->dataFileLen(), info->formatString(), info->sidModel1() ); #else const SidTuneInfo info = tune->getInfo(); res = xs_tuneinfo_new(filename, info.songs, info.startSong, info.infoString[0], info.infoString[1], info.infoString[2], info.loadAddr, info.initAddr, info.playAddr, info.dataFileLen, info.formatString, info.sidModel1 ); #endif } error: if (tune) delete tune; g_free(buf); return res; } /* Updates the information of currently playing tune */ gboolean xs_sidplayfp_updateinfo(XSEngineState *state) { XSSIDPlayFP *engine; /* Check if we have required structures initialized */ if (!state || !state->tuneInfo || !state->internal) return FALSE; engine = (XSSIDPlayFP *) state->internal; #ifdef HAVE_SIDPLAYFP_V1 if (!engine->tune.getStatus()) return FALSE; const SidTuneInfo *info = engine->tune.getInfo(); state->tuneInfo->sidModel = info->sidModel1(); if (state->currSong > 0 && state->currSong <= state->tuneInfo->nsubTunes) { gint tmpSpeed = info->clockSpeed(); switch (info->clockSpeed()) { case SIDTUNE_CLOCK_PAL: tmpSpeed = XS_CLOCK_PAL; break; case SIDTUNE_CLOCK_NTSC: tmpSpeed = XS_CLOCK_NTSC; break; case SIDTUNE_CLOCK_ANY: tmpSpeed = XS_CLOCK_ANY; break; case SIDTUNE_CLOCK_UNKNOWN: switch (info->songSpeed()) { case SIDTUNE_SPEED_VBI: tmpSpeed = XS_CLOCK_VBI; break; case SIDTUNE_SPEED_CIA_1A: tmpSpeed = XS_CLOCK_CIA; break; default: tmpSpeed = info->songSpeed(); break; } break; } state->tuneInfo->subTunes[state->currSong - 1].tuneSpeed = tmpSpeed; } #else if (!(engine->tune)) return FALSE; const SidTuneInfo info = engine->tune.getInfo(); state->tuneInfo->sidModel = info.sidModel1; if (state->currSong > 0 && state->currSong <= state->tuneInfo->nsubTunes) { gint tmpSpeed = info.clockSpeed; switch (info.clockSpeed) { case SIDTUNE_CLOCK_PAL: tmpSpeed = XS_CLOCK_PAL; break; case SIDTUNE_CLOCK_NTSC: tmpSpeed = XS_CLOCK_NTSC; break; case SIDTUNE_CLOCK_ANY: tmpSpeed = XS_CLOCK_ANY; break; case SIDTUNE_CLOCK_UNKNOWN: switch (info.songSpeed) { case SIDTUNE_SPEED_VBI: tmpSpeed = XS_CLOCK_VBI; break; case SIDTUNE_SPEED_CIA_1A: tmpSpeed = XS_CLOCK_CIA; break; default: tmpSpeed = info.songSpeed; break; } break; } state->tuneInfo->subTunes[state->currSong - 1].tuneSpeed = tmpSpeed; } #endif return TRUE; } } /* extern "C" */ #endif /* HAVE_SIDPLAYFP */