Mercurial > hg > xmms-sid
view src/xs_sidplay2.cc @ 776:bb7b3ded919a
Indeed, libsidplay-fp's audio renderer expects buffer of 16-bit shorts and
number of samples as input, AND returns number of samples. Thus, we need to
divide and multiply the values accordingly. However, it also seems that some
internal buffers in resid-fp are hardcoded to certain size, and using too
big audio buffers cause overflows .. trying to compensate for that by
limiting the amount of audio rendered.
Unfortunately there are still memory corruptions.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 06 Nov 2012 11:59:24 +0200 |
parents | 0e9f2894b0a9 |
children | 8fb51f9ef44e |
line wrap: on
line source
/* XMMS-SID - SIDPlay input plugin for X MultiMedia System (XMMS) libSIDPlay v2 and libSIDPlay v2-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_SIDPLAY2 #include <stdio.h> #include "xs_sidplay2.h" #include "xs_config.h" #ifdef HAVE_SIDPLAY2_FP # include <sidplayfp/sidplay2.h> # include <sidplayfp/SidTuneMod.h> # include <sidplayfp/event.h> #else # include <sidplay/sidplay2.h> # include <sidplay/sidlazyiptr.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 #endif class XSSIDPlay2 { public: #ifdef HAVE_SIDPLAY2_FP sidplay2 emu; #else SidIPtr<ISidplay2> emu; #endif sid2_config_t config; SidTune *tune; XSSIDPlay2(void); virtual ~XSSIDPlay2(void) { ; } }; #ifdef HAVE_RESID_BUILDER # ifdef HAVE_SIDPLAY2_FP # include <sidplayfp/builders/residfp.h> # include <sidplayfp/builders/resid.h> # else # include <sidplay/builders/resid.h> # endif #endif #ifdef HAVE_HARDSID_BUILDER # ifdef HAVE_SIDPLAY2_FP # include <sidplayfp/builders/hardsid.h> # else # include <sidplay/builders/hardsid.h> # endif #endif XSSIDPlay2::XSSIDPlay2(void) { tune = 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; #ifndef HAVE_SIDPLAY2_FP sid_filter_t tmpFilter; xs_sid_filter_t *f; gint i; #endif 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 */ #ifdef HAVE_SIDPLAY2_FP state->audioBitsPerSample = XS_RES_16BIT; engine->config.samplingMethod = SID2_RESAMPLE_INTERPOLATE; #else engine->config.precision = state->audioBitsPerSample; #endif engine->config.frequency = state->audioFrequency; switch (state->audioBitsPerSample) { #ifndef HAVE_SIDPLAY2_FP case XS_RES_8BIT: state->audioFormat = FMT_U8; engine->config.sampleFormat = SID2_LITTLE_UNSIGNED; break; #endif case XS_RES_16BIT: default: state->audioFormat = FMT_S16_NE; #ifndef HAVE_SIDPLAY2_FP 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: engine->config.sampleFormat = SID2_NATIVE_SIGNED; break; } #endif break; } #if !defined(HAVE_SIDPLAY2_FP) /* 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; } #endif /* 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 #ifdef HAVE_SIDPLAY2_FP 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; rs->bias(0.0f); } } break; case XS_BLD_RESID_FP: { ReSIDfpBuilder *rs = new ReSIDfpBuilder("ReSID builder FP!"); if (rs) { engine->config.sidEmulation = rs; if (!*rs) return FALSE; rs->create((engine->emu.info()).maxsids); if (!*rs) return FALSE; /* if (m_filter.filterCurve6581) rs->filter6581Curve(m_filter.filterCurve6581); if (m_filter.filterCurve8580) rs->filter8580Curve((double)m_filter.filterCurve8580); */ } } break; #else case XS_BLD_RESID: { engine->config.sidEmulation = ReSIDBuilderCreate("ReSID builder"); SidLazyIPtr<IReSIDBuilder> rs(engine->config.sidEmulation); if (rs) { engine->config.sidEmulation = rs->iaggregate(); rs->create((engine->emu.info()).maxsids); if (!*rs) return FALSE; } } break; #endif #endif #ifdef HAVE_HARDSID_BUILDER case XS_BLD_HARDSID: #ifdef HAVE_SIDPLAY2_FP { HardSIDBuilder *hs = new HardSIDBuilder("HardSID builder (FP)"); engine->config.sidEmulation = (sidbuilder *) hs; if (hs) { #else engine->config.sidEmulation = HardSIDBuilderCreate("HardSID builder"); SidLazyIPtr<IHardSIDBuilder> hs(engine->config.sidEmulation); if (hs) { engine->config.sidEmulation = hs->iaggregate(); #endif /* Builder object created, initialize it */ 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; } // 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; } #ifdef HAVE_SIDPLAY2_FP engine->config.sidEmulation = engine->config.sidEmulation; XSDEBUG("%s\n", engine->config.sidEmulation->credits()); #endif /* 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; } #ifndef HAVE_SIDPLAY2_FP 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; #endif 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; /* Now set the emulator configuration */ if (engine->emu.config(engine->config) < 0) { xs_error("[SIDPlay2] Emulator engine configuration failed!\n"); return FALSE; } return TRUE; } /* Close SIDPlay2 engine */ void xs_sidplay2_close(XSEngineState * state) { XSSIDPlay2 *engine = (XSSIDPlay2 *) state->internal; xs_sidplay2_delete(state); if (engine) { #ifdef HAVE_SIDPLAY2_FP if (engine->config.sidEmulation) { sidbuilder *builder = engine->config.sidEmulation; engine->config.sidEmulation = NULL; engine->emu.config(engine->config); delete builder; } #endif 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; } state->isInitialized = TRUE; 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; int len = audioBufSize / sizeof(short); if (len > 512) len = 512; return engine->emu.play((short *) audioBuffer, len) * sizeof(short); } /* Load a given SID-tune file */ gboolean xs_sidplay2_load(XSEngineState * state, gchar * filename) { XSSIDPlay2 *engine = (XSSIDPlay2 *) state->internal; state->isInitialized = FALSE; if (!engine) return FALSE; if (!(engine->tune = new SidTune(filename))) return FALSE; return TRUE; } /* Delete INTERNAL information */ void xs_sidplay2_delete(XSEngineState * state) { XSSIDPlay2 *engine = (XSSIDPlay2 *) state->internal; if (engine) { if (engine->tune) { delete engine->tune; engine->tune = NULL; } } } /* Hardware backend flushing */ void xs_sidplay2_flush(XSEngineState * state) { (void) state; #ifdef HAVE_HARDSID_BUILDER if (xs_cfg.sid2Builder == XS_BLD_HARDSID) { #ifdef HSID_SID2_COM IfPtr<HardSIDBuilder> hs(state->config.sidEmulation); if (hs) hs->flush(); #else ((HardSIDBuilder *) state->config.sidEmulation)->flush(); #endif } #endif } } /* extern "C" */ #endif /* HAVE_SIDPLAY2 */