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 */