/*************************************************************************** config.cpp - Library Configuration Code ------------------- begin : Fri Jul 27 2001 copyright : (C) 2001 by Simon White email : s_a_white@email.com ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ /*************************************************************************** * $Log: config.cpp,v $ * Revision 1.29 2002/10/15 18:14:02 s_a_white * Removed upper limit frequency limit check to allow for oversampling. * * Revision 1.28 2002/10/02 19:46:36 s_a_white * RSID support & fix sid model forced operation. * * Revision 1.27 2002/09/09 18:09:06 s_a_white * Added error message for psid specific flag set in strict real C64 mode. * * Revision 1.26 2002/08/10 22:35:56 s_a_white * Small clock speed fix for when both clockSpeed and clockDefault are set * to SID2_CLOCK_CORRECT. * * Revision 1.25 2002/07/17 19:25:28 s_a_white * Temp fix to allow SID model to change for current builder. * * Revision 1.24 2002/04/14 21:46:50 s_a_white * PlaySID reads fixed to come from RAM only. * * Revision 1.23 2002/03/11 18:01:30 s_a_white * Prevent lockup if config call fails with existing and old configurations. * * Revision 1.22 2002/03/04 19:05:49 s_a_white * Fix C++ use of nothrow. * * Revision 1.21 2002/03/03 22:01:58 s_a_white * New clock speed & sid model interface. * * Revision 1.20 2002/02/18 21:59:10 s_a_white * Added two new clock modes (FIXED). Seems to be a requirement for * HVSC/sidplayw. * * Revision 1.19 2002/02/04 22:08:14 s_a_white * Fixed main voice/sample gains. * * Revision 1.18 2002/01/29 21:50:33 s_a_white * Auto switching to a better emulation mode. m_tuneInfo reloaded after a * config. Initial code added to support more than two sids. * * Revision 1.17 2002/01/16 19:11:38 s_a_white * Always release sid emulations now on a call to sidCreate until a better * method is implemented for hardware emulations with locked sids. * * Revision 1.16 2002/01/16 08:23:45 s_a_white * Force a clock speed when unknown. * * Revision 1.15 2002/01/15 19:12:54 s_a_white * PSID2NG update. * * Revision 1.14 2002/01/14 23:18:56 s_a_white * Make sure xsid releases the old sid emulation when there are errors gaining * a new one. * * Revision 1.13 2001/12/20 20:15:23 s_a_white * Fixed bad environment initialisation when switching to legacy modes. * * Revision 1.12 2001/12/13 08:28:08 s_a_white * Added namespace support to fix problems with xsidplay. * * Revision 1.11 2001/12/11 19:24:15 s_a_white * More GCC3 Fixes. * * Revision 1.10 2001/11/23 22:59:59 s_a_white * Added new header * * Revision 1.9 2001/10/02 18:27:55 s_a_white * Updated to use new sidbuilder classes. * * Revision 1.8 2001/09/20 20:33:54 s_a_white * sid2 now gets correctly set to nullsid for a bad create call. * * Revision 1.7 2001/09/20 19:34:11 s_a_white * Error checking added for the builder create calls. * * Revision 1.6 2001/09/17 19:02:38 s_a_white * Now uses fixed point maths for sample output and rtc. * * Revision 1.5 2001/09/01 11:13:56 s_a_white * Fixes sidplay1 environment modes. * * Revision 1.4 2001/08/20 18:24:50 s_a_white * tuneInfo in the info structure now correctly has the sid revision setup. * * Revision 1.3 2001/08/12 18:22:45 s_a_white * Fixed bug in Player::sidEmulation call. * * Revision 1.2 2001/07/27 12:51:40 s_a_white * Removed warning. * * Revision 1.1 2001/07/27 12:12:23 s_a_white * Initial release. * ***************************************************************************/ #include "sid2types.h" #include "player.h" #ifdef HAVE_EXCEPTIONS # include #endif SIDPLAY2_NAMESPACE_START // An instance of this structure is used to transport emulator settings // to and from the interface class. int Player::config (const sid2_config_t &cfg) { if (m_running) { m_errorString = ERR_CONF_WHILST_ACTIVE; goto Player_configure_error; } // Check for base sampling frequency if (cfg.frequency < 4000) { // Rev 1.6 (saw) - Added descriptive error m_errorString = ERR_UNSUPPORTED_FREQ; goto Player_configure_error; } // Check for legal precision switch (cfg.precision) { case 8: case 16: case 24: if (cfg.precision > SID2_MAX_PRECISION) { // Rev 1.6 (saw) - Added descriptive error m_errorString = ERR_UNSUPPORTED_PRECISION; goto Player_configure_error; } break; default: // Rev 1.6 (saw) - Added descriptive error m_errorString = ERR_UNSUPPORTED_PRECISION; goto Player_configure_error; } // Only do these if we have a loaded tune if (m_tune) { float64_t cpuFreq; // Reset tune info m_tune->getInfo(m_tuneInfo); // External Setups if (sidCreate (cfg.sidEmulation, cfg.sidModel, cfg.sidDefault) < 0) { m_errorString = cfg.sidEmulation->error (); m_cfg.sidEmulation = NULL; goto Player_configure_restore; } // Must be this order: // Determine clock speed cpuFreq = clockSpeed (cfg.clockSpeed, cfg.clockDefault, cfg.clockForced); // Fixed point conversion 16.16 m_samplePeriod = (event_clock_t) (cpuFreq / (float64_t) cfg.frequency * (1 << 16) * m_fastForwardFactor); // Setup fake cia sid6526.clock ((uint_least16_t)(cpuFreq / VIC_FREQ_PAL + 0.5)); if (m_tuneInfo.songSpeed == SIDTUNE_SPEED_CIA_1A || m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_NTSC) { sid6526.clock ((uint_least16_t)(cpuFreq / VIC_FREQ_NTSC + 0.5)); } // Configure, setup and install C64 environment/events if (environment (cfg.environment) < 0) goto Player_configure_restore; // Start the real time clock event rtc.clock (cpuFreq); } sidSamples (cfg.sidSamples); // All parameters check out, so configure player. m_info.channels = 1; if (cfg.playback == sid2_stereo) m_info.channels++; m_sidAddress[0] = m_tuneInfo.sidChipBase1; m_sidAddress[1] = m_tuneInfo.sidChipBase2; // Only force dual sids if second wasn't detected if (!m_sidAddress[1] && cfg.forceDualSids) m_sidAddress[1] = 0xd500; // Assumed m_leftVolume = cfg.leftVolume; m_rightVolume = cfg.rightVolume; if (cfg.playback != sid2_mono) { // Try Spliting channels across 2 sids if (!m_sidAddress[1]) { m_sidAddress[1] = m_sidAddress[0]; // Mute Voices sid[0]->voice (0, 0, true); sid[0]->voice (2, 0, true); sid[1]->voice (1, 0, true); // 2 Voices scaled to unity from 4 (was !SID_VOL) // m_leftVolume *= 2; // m_rightVolume *= 2; // 2 Voices scaled to unity from 3 (was SID_VOL) // m_leftVolume *= 3; // m_leftVolume /= 2; // m_rightVolume *= 3; // m_rightVolume /= 2; } if (cfg.playback == sid2_left) xsid.mute (true); } // Setup the audio side, depending on the audio hardware // and the information returned by sidtune switch (cfg.precision) { case 8: if (!m_sidAddress[1]) { if (cfg.playback == sid2_stereo) output = &Player::stereoOut8MonoIn; else output = &Player::monoOut8MonoIn; } else { switch (cfg.playback) { case sid2_stereo: // Stereo Hardware output = &Player::stereoOut8StereoIn; break; case sid2_right: // Mono Hardware, output = &Player::monoOut8StereoRIn; break; case sid2_left: output = &Player::monoOut8MonoIn; break; case sid2_mono: output = &Player::monoOut8StereoIn; break; } } break; case 16: if (!m_sidAddress[1]) { if (cfg.playback == sid2_stereo) output = &Player::stereoOut16MonoIn; else output = &Player::monoOut16MonoIn; } else { switch (cfg.playback) { case sid2_stereo: // Stereo Hardware output = &Player::stereoOut16StereoIn; break; case sid2_right: // Mono Hardware, output = &Player::monoOut16StereoRIn; break; case sid2_left: output = &Player::monoOut16MonoIn; break; case sid2_mono: output = &Player::monoOut16StereoIn; break; } } } // Update Configuration m_cfg = cfg; if (m_cfg.optimisation > SID2_MAX_OPTIMISATION) m_cfg.optimisation = SID2_MAX_OPTIMISATION; return 0; Player_configure_restore: // Try restoring old configuration if (&m_cfg != &cfg) config (m_cfg); Player_configure_error: return -1; } // Clock speed changes due to loading a new song float64_t Player::clockSpeed (sid2_clock_t userClock, sid2_clock_t defaultClock, bool forced) { float64_t cpuFreq = CLOCK_FREQ_PAL; // Detect the Correct Song Speed // Determine song speed when unknown if (m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_UNKNOWN) { switch (defaultClock) { case SID2_CLOCK_PAL: m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_PAL; break; case SID2_CLOCK_NTSC: m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_NTSC; break; case SID2_CLOCK_CORRECT: // No default so base it on emulation clock m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_ANY; } } // Since song will run correct at any clock speed // set tune speed to the current emulation if (m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_ANY) { if (userClock == SID2_CLOCK_CORRECT) userClock = defaultClock; switch (userClock) { case SID2_CLOCK_NTSC: m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_NTSC; break; case SID2_CLOCK_PAL: default: m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_PAL; break; } } if (userClock == SID2_CLOCK_CORRECT) { switch (m_tuneInfo.clockSpeed) { case SIDTUNE_CLOCK_NTSC: userClock = SID2_CLOCK_NTSC; break; case SIDTUNE_CLOCK_PAL: userClock = SID2_CLOCK_PAL; break; } } if (forced) { m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_PAL; if (userClock == SID2_CLOCK_NTSC) m_tuneInfo.clockSpeed = SIDTUNE_CLOCK_NTSC; } if (m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_PAL) vic.chip (MOS6569); else // if (tuneInfo.clockSpeed == SIDTUNE_CLOCK_NTSC) vic.chip (MOS6567R8); if (userClock == SID2_CLOCK_PAL) { cpuFreq = CLOCK_FREQ_PAL; m_tuneInfo.speedString = TXT_PAL_VBI; if (m_tuneInfo.songSpeed == SIDTUNE_SPEED_CIA_1A) m_tuneInfo.speedString = TXT_PAL_CIA; else if (m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_NTSC) m_tuneInfo.speedString = TXT_PAL_VBI_FIXED; } else // if (userClock == SID2_CLOCK_NTSC) { cpuFreq = CLOCK_FREQ_NTSC; m_tuneInfo.speedString = TXT_NTSC_VBI; if (m_tuneInfo.songSpeed == SIDTUNE_SPEED_CIA_1A) m_tuneInfo.speedString = TXT_NTSC_CIA; else if (m_tuneInfo.clockSpeed == SIDTUNE_CLOCK_PAL) m_tuneInfo.speedString = TXT_NTSC_VBI_FIXED; } return cpuFreq; } int Player::environment (sid2_env_t env) { switch (m_tuneInfo.compatibility) { case SIDTUNE_COMPATIBILITY_R64: env = sid2_envR; break; case SIDTUNE_COMPATIBILITY_PSID: if (env == sid2_envR) env = sid2_envBS; } // Environment already set? if (!(m_ram && (m_info.environment == env))) { // Setup new player environment m_info.environment = env; if (m_ram) { if (m_ram == m_rom) delete [] m_ram; else { delete [] m_rom; delete [] m_ram; } } #ifdef HAVE_EXCEPTIONS m_ram = new(std::nothrow) uint8_t[0x10000]; #else m_ram = new uint8_t[0x10000]; #endif // Setup the access functions to the environment // and the properties the memory has. if (m_info.environment == sid2_envPS) { // Playsid has no roms and SID exists in ram space m_rom = m_ram; m_readMemByte = &Player::readMemByte_player; m_writeMemByte = &Player::writeMemByte_playsid; m_readMemDataByte = &Player::readMemByte_plain; } else { #ifdef HAVE_EXCEPTIONS m_rom = new(std::nothrow) uint8_t[0x10000]; #else m_rom = new uint8_t[0x10000]; #endif switch (m_info.environment) { case sid2_envTP: m_readMemByte = &Player::readMemByte_player; m_writeMemByte = &Player::writeMemByte_sidplay; m_readMemDataByte = &Player::readMemByte_sidplaytp; break; //case sid2_envTR: case sid2_envBS: m_readMemByte = &Player::readMemByte_player; m_writeMemByte = &Player::writeMemByte_sidplay; m_readMemDataByte = &Player::readMemByte_sidplaybs; break; case sid2_envR: default: // <-- Just to please compiler m_readMemByte = &Player::readMemByte_player; m_writeMemByte = &Player::writeMemByte_sidplay; m_readMemDataByte = &Player::readMemByte_sidplaybs; break; } } } { // Have to reload the song into memory as // everything has changed int ret; sid2_env_t old = m_info.environment; m_info.environment = env; ret = initialise (); m_info.environment = old; return ret; } } // Integrate SID emulation from the builder class into // libsidplay2 int Player::sidCreate (sidbuilder *builder, sid2_model_t userModel, sid2_model_t defaultModel) { sid[0] = xsid.emulation (); /* @FIXME@ Removed as prevents SID * Model being updated **************************************** // If we are already using the emulation // then don't change if (builder == sid[0]->builder ()) { sid[0] = &xsid; return 0; } ****************************************/ // Make xsid forget it's emulation xsid.emulation (&nullsid); { // Release old sids for (int i = 0; i < SID2_MAX_SIDS; i++) { sidbuilder *b; b = sid[i]->builder (); if (b) b->unlock (sid[i]); } } if (!builder) { // No sid for (int i = 0; i < SID2_MAX_SIDS; i++) sid[i] = &nullsid; } else { // Detect the Correct SID model // Determine model when unknown if (m_tuneInfo.sidModel == SIDTUNE_SIDMODEL_UNKNOWN) { switch (defaultModel) { case SID2_MOS6581: m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_6581; break; case SID2_MOS8580: m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_8580; break; case SID2_MODEL_CORRECT: // No default so base it on emulation clock m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_ANY; } } // Since song will run correct on any sid model // set it to the current emulation if (m_tuneInfo.sidModel == SIDTUNE_SIDMODEL_ANY) { if (userModel == SID2_MODEL_CORRECT) userModel = defaultModel; switch (userModel) { case SID2_MOS8580: m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_8580; break; case SID2_MOS6581: default: m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_6581; break; } } switch (userModel) { case SID2_MODEL_CORRECT: switch (m_tuneInfo.sidModel) { case SIDTUNE_SIDMODEL_8580: userModel = SID2_MOS8580; break; case SIDTUNE_SIDMODEL_6581: userModel = SID2_MOS6581; break; } break; // Fixup tune information if model is forced case SID2_MOS6581: m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_6581; break; case SID2_MOS8580: m_tuneInfo.sidModel = SIDTUNE_SIDMODEL_8580; break; } for (int i = 0; i < SID2_MAX_SIDS; i++) { // Get first SID emulation sid[i] = builder->lock (this, userModel); if (!sid[i]) sid[i] = &nullsid; if ((i == 0) && !*builder) return -1; } } xsid.emulation (sid[0]); sid[0] = &xsid; return 0; } void Player::sidSamples (bool enable) { int_least8_t gain = 0; xsid.sidSamples (enable); // Now balance voices if (!enable) gain = -25; xsid.gain (-100 - gain); sid[0] = xsid.emulation (); for (int i = 0; i < SID2_MAX_SIDS; i++) sid[i]->gain (gain); sid[0] = &xsid; } SIDPLAY2_NAMESPACE_STOP