Mercurial > hg > xmms-sid
diff src/xs_sidplay2.cpp @ 834:a7ee5dc23e78
Rename .cc -> .cpp.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 08 Nov 2012 20:47:05 +0200 |
parents | src/xs_sidplay2.cc@c0e892fa914a |
children | ae1f6418d093 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xs_sidplay2.cpp Thu Nov 08 20:47:05 2012 +0200 @@ -0,0 +1,441 @@ +/* + XMMS-SID - SIDPlay input plugin for X MultiMedia System (XMMS) + + libSIDPlay v2 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_SIDPLAY2 + +#include "xs_sidplay2.h" +#include "xs_slsup.h" +#include "xs_config.h" + +#include <sidplay/sidplay2.h> +#if G_BYTE_ORDER == G_BIG_ENDIAN +# define SID2_NATIVE_UNSIGNED SID2_BIG_UNSIGNED +# define SID2_NATIVE_SIGNED SID2_BIG_SIGNED +#elif G_BYTE_ORDER == G_LITTLE_ENDIAN +# define SID2_NATIVE_UNSIGNED SID2_LITTLE_UNSIGNED +# define SID2_NATIVE_SIGNED SID2_LITTLE_SIGNED +#else +# error Unsupported endianess! +#endif + + +class XSSIDPlay2 { +public: + sidplay2 emu; + sid2_config_t config; + SidTune tune; + + XSSIDPlay2(void); + virtual ~XSSIDPlay2(void); +}; + + +#ifdef HAVE_RESID_BUILDER +# include <sidplay/builders/resid.h> +#endif + +#ifdef HAVE_HARDSID_BUILDER +# include <sidplay/builders/hardsid.h> +#endif + + +XSSIDPlay2::XSSIDPlay2(void) : tune(0) +{ +} + + +XSSIDPlay2::~XSSIDPlay2(void) +{ + emu.load(NULL); +} + + +/* We need to 'export' all this pseudo-C++ crap */ +extern "C" { + + +/* Return song information + */ +#define TFUNCTION xs_sidplay2_getinfo +#define TFUNCTION2 xs_sidplay2_updateinfo +#define TTUNEINFO SidTuneInfo +#define TTUNE SidTune +#define TENGINE XSSIDPlay2 +#include "xs_sidplay.h" + + +/* Check if we can play the given file + */ +gboolean xs_sidplay2_probe(XSFile *f) +{ + gchar tmpBuf[5]; + + if (!f) return FALSE; + + if (xs_fread(tmpBuf, sizeof(gchar), 4, f) != 4) + return FALSE; + + if (!strncmp(tmpBuf, "PSID", 4) || !strncmp(tmpBuf, "RSID", 4)) + return TRUE; + else + return FALSE; +} + + +/* Initialize SIDPlay2 + */ +gboolean xs_sidplay2_init(XSEngineState * state) +{ + XSSIDPlay2 *engine; + sid_filter_t tmpFilter; + xs_sid_filter_t *f; + gint i; + assert(state); + + /* Allocate internal structures */ + engine = new XSSIDPlay2(); + state->internal = engine; + if (!engine) + return FALSE; + + /* Get current configuration */ + engine->config = engine->emu.config(); + + /* Configure channels and stuff */ + switch (state->audioChannels) + { + case XS_CHN_AUTOPAN: + engine->config.playback = sid2_stereo; + break; + + case XS_CHN_STEREO: + engine->config.playback = sid2_stereo; + break; + + case XS_CHN_MONO: + default: + engine->config.playback = sid2_mono; + state->audioChannels = XS_CHN_MONO; + break; + } + + + /* Memory mode settings */ + switch (xs_cfg.memoryMode) + { + case XS_MPU_BANK_SWITCHING: + engine->config.environment = sid2_envBS; + break; + + case XS_MPU_TRANSPARENT_ROM: + engine->config.environment = sid2_envTP; + break; + + case XS_MPU_PLAYSID_ENVIRONMENT: + engine->config.environment = sid2_envPS; + break; + + case XS_MPU_REAL: + default: + engine->config.environment = sid2_envR; + xs_cfg.memoryMode = XS_MPU_REAL; + break; + } + + + /* Audio parameters sanity checking and setup */ + engine->config.precision = state->audioBitsPerSample; + engine->config.frequency = state->audioFrequency; + + + switch (state->audioBitsPerSample) + { + case XS_RES_8BIT: + state->audioFormat = FMT_U8; + engine->config.sampleFormat = SID2_LITTLE_UNSIGNED; + break; + + case XS_RES_16BIT: + default: + switch (state->audioFormat) + { + case FMT_U16_LE: + engine->config.sampleFormat = SID2_LITTLE_UNSIGNED; + break; + + case FMT_U16_BE: + engine->config.sampleFormat = SID2_BIG_UNSIGNED; + break; + + case FMT_U16_NE: + engine->config.sampleFormat = SID2_NATIVE_UNSIGNED; + break; + + case FMT_S16_LE: + engine->config.sampleFormat = SID2_LITTLE_SIGNED; + break; + + case FMT_S16_BE: + engine->config.sampleFormat = SID2_BIG_SIGNED; + break; + + default: + state->audioFormat = FMT_S16_NE; + engine->config.sampleFormat = SID2_NATIVE_SIGNED; + break; + } + break; + } + + /* Convert filter */ + f = &(xs_cfg.sid2Filter); + XSDEBUG("using filter '%s', %d points\n", f->name, f->npoints); + if (f->npoints > XS_SIDPLAY2_NFPOINTS) + { + xs_error("[SIDPlay2] Invalid number of filter curve points (%d > %d)\n", + f->npoints, XS_SIDPLAY2_NFPOINTS); + f->npoints = XS_SIDPLAY2_NFPOINTS; + } + + tmpFilter.points = f->npoints; + for (i = 0; i < f->npoints; i++) + { + tmpFilter.cutoff[i][0] = f->points[i].x; + tmpFilter.cutoff[i][1] = f->points[i].y; + } + + /* Initialize builder object */ + XSDEBUG("init builder #%i, maxsids=%i\n", xs_cfg.sid2Builder, (engine->emu.info()).maxsids); + + switch (xs_cfg.sid2Builder) + { +#ifdef HAVE_RESID_BUILDER + case XS_BLD_RESID: + { + ReSIDBuilder *rs = new ReSIDBuilder("ReSID builder"); + if (rs) + { + engine->config.sidEmulation = rs; + if (!*rs) return FALSE; + rs->create((engine->emu.info()).maxsids); + if (!*rs) return FALSE; + } + } + break; +#endif + +#ifdef HAVE_HARDSID_BUILDER + case XS_BLD_HARDSID: + { + HardSIDBuilder *hs = new HardSIDBuilder("HardSID builder (FP)"); + engine->config.sidEmulation = (sidbuilder *) hs; + if (hs) + { + hs->create((engine->emu.info()).maxsids); + if (!*hs) + { + xs_error("hardSID->create() failed.\n"); + return FALSE; + } + } + } + break; +#endif + + default: + xs_error("[SIDPlay2] Invalid or unsupported builder selected.\n"); + break; + } + + if (!engine->config.sidEmulation) + { + xs_error("[SIDPlay2] Could not initialize SIDBuilder object.\n"); + return FALSE; + } + +#if 0 + // Setup filter + engine->config.sidEmulation->filter(xs_cfg.emulateFilters); + if (!*(engine->config.sidEmulation)) + { + xs_error("builder->filter(%d) failed.\n", xs_cfg.emulateFilters); + return FALSE; + } +#endif + + XSDEBUG("%s\n", engine->config.sidEmulation->credits()); + + /* 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 */ + if (xs_cfg.forceSpeed) + { + engine->config.clockForced = true; + engine->config.clockSpeed = engine->config.clockDefault; + } + else + { + engine->config.clockForced = false; + engine->config.clockSpeed = SID2_CLOCK_CORRECT; + } + + + if (xs_cfg.sid2OptLevel < 0 || xs_cfg.sid2OptLevel > SID2_MAX_OPTIMISATION) + { + xs_error("Invalid sid2OptLevel=%d, falling back to %d.\n", + xs_cfg.sid2OptLevel, SID2_DEFAULT_OPTIMISATION); + + xs_cfg.sid2OptLevel = SID2_DEFAULT_OPTIMISATION; + } + engine->config.optimisation = xs_cfg.sid2OptLevel; + + engine->config.sidDefault = xs_cfg.mos8580 ? SID2_MOS8580 : SID2_MOS6581; + engine->config.sidModel = xs_cfg.forceModel ? engine->config.sidDefault : SID2_MODEL_CORRECT; + engine->config.sidSamples = TRUE; + + return TRUE; +} + + +/* Close SIDPlay2 engine + */ +void xs_sidplay2_close(XSEngineState * state) +{ + XSSIDPlay2 *engine = (XSSIDPlay2 *) state->internal; + + xs_sidplay2_delete(state); + + if (engine) + { + delete engine; + engine = NULL; + } + + state->internal = NULL; +} + + +/* Initialize current song and sub-tune + */ +gboolean xs_sidplay2_initsong(XSEngineState * state) +{ + XSSIDPlay2 *engine = (XSSIDPlay2 *) state->internal; + + if (!engine) + return FALSE; + + if (!engine->tune.selectSong(state->currSong)) + { + xs_error("[SIDPlay2] tune.selectSong() failed\n"); + return FALSE; + } + + if (engine->emu.load(&(engine->tune)) < 0) + { + xs_error("[SIDPlay2] emu.load() failed\n"); + return FALSE; + } + + if (engine->emu.config(engine->config) < 0) + { + xs_error("[SIDPlay2] Emulator engine configuration failed!\n"); + return FALSE; + } + + return TRUE; +} + + +/* Emulate and render audio data to given buffer + */ +guint xs_sidplay2_fillbuffer(XSEngineState * state, gchar * audioBuffer, guint audioBufSize) +{ + XSSIDPlay2 *engine = (XSSIDPlay2 *) state->internal; + + if (!engine) + return 0; + + return engine->emu.play(audioBuffer, audioBufSize); +} + + +/* Load a given SID-tune file + */ +gboolean xs_sidplay2_load(XSEngineState * state, gchar * filename) +{ + XSSIDPlay2 *engine = (XSSIDPlay2 *) state->internal; + + if (!engine || !filename) + return FALSE; + + engine->tune.load(filename); + if (!engine->tune) + { + xs_error("Could not load file '%s': %s\n", + filename, (engine->tune.getInfo()).statusString); + return FALSE; + } + + return TRUE; +} + + +/* Delete INTERNAL information + */ +void xs_sidplay2_delete(XSEngineState * state) +{ + XSSIDPlay2 *engine = (XSSIDPlay2 *) state->internal; +} + + +/* Hardware backend flushing + */ +void xs_sidplay2_flush(XSEngineState * state) +{ + XSSIDPlay2 *engine = (XSSIDPlay2 *) state->internal; + +#ifdef HAVE_HARDSID_BUILDER + if (xs_cfg.sid2Builder == XS_BLD_HARDSID) + { + ((HardSIDBuilder *) engine->config.sidEmulation)->flush(); + } +#endif +} + + +} /* extern "C" */ +#endif /* HAVE_SIDPLAY2 */