Mercurial > hg > xmms-sid
view src/xs_sidplayfp.cpp @ 985:9e85f5a2614f
Another update.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sun, 31 Mar 2013 11:45:01 +0300 |
parents | 9bd29db2d1bf |
children | 34259889b2b1 |
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-2013 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> # include <sidplayfp/SidTuneInfo.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::PAL #define SID2_CLOCK_NTSC SidConfig::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) { (void) 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) { 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: #ifdef HAVE_SIDPLAYFP_V1 engine->config.defaultC64Model = SID2_CLOCK_NTSC; #else engine->config.clockDefault = SID2_CLOCK_NTSC; #endif break; default: case XS_CLOCK_PAL: #ifdef HAVE_SIDPLAYFP_V1 #else engine->config.defaultC64Model = SID2_CLOCK_PAL; #endif xs_cfg.clockSpeed = XS_CLOCK_PAL; break; } /* Configure rest of the emulation */ #ifdef HAVE_SIDPLAYFP_V1 engine->config.forceC64Model = xs_cfg.forceSpeed; engine->config.defaultSidModel = xs_cfg.mos8580 ? SID2_MOS8580 : SID2_MOS6581; XSDEBUG("init builder #%i, maxsids=%i\n", xs_cfg.sid2Builder, (engine->emu.info()).maxsids()); #else engine->config.sidDefault = xs_cfg.mos8580 ? SID2_MOS8580 : SID2_MOS6581; engine->config.clockForced = xs_cfg.forceSpeed; 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); #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(), info->sidModel2() ); #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, info.sidModel2 ); #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->sidModel1 = info->sidModel1(); state->tuneInfo->sidModel2 = info->sidModel2(); 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->sidModel1 = info.sidModel1; state->tuneInfo->sidModel2 = info.sidModel2; 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 */