820 lines
21 KiB
Text
820 lines
21 KiB
Text
/*
|
|
* This file is part of sidplayfp, a console SID player.
|
|
*
|
|
* Copyright 2011-2013 Leandro Nini
|
|
* Copyright 2000-2001 Simon White
|
|
*
|
|
* 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 "player.h"
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <new>
|
|
|
|
using std::cout;
|
|
using std::cerr;
|
|
using std::endl;
|
|
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#include "utils.h"
|
|
#include "keyboard.h"
|
|
#include "audio/AudioDrv.h"
|
|
#include "audio/wav/WavFile.h"
|
|
#include "ini/types.h"
|
|
|
|
#include <sidplayfp/sidbuilder.h>
|
|
#include <sidplayfp/SidInfo.h>
|
|
#include <sidplayfp/SidTuneInfo.h>
|
|
|
|
// Previous song select timeout (3 secs)
|
|
#define SID2_PREV_SONG_TIMEOUT 4
|
|
|
|
#ifdef HAVE_SIDPLAYFP_BUILDERS_RESIDFP_H
|
|
# include <sidplayfp/builders/residfp.h>
|
|
const char ConsolePlayer::RESIDFP_ID[] = "ReSIDfp";
|
|
#endif
|
|
|
|
#ifdef HAVE_SIDPLAYFP_BUILDERS_RESID_H
|
|
# include <sidplayfp/builders/resid.h>
|
|
const char ConsolePlayer::RESID_ID[] = "ReSID";
|
|
#endif
|
|
|
|
#ifdef HAVE_SIDPLAYFP_BUILDERS_HARDSID_H
|
|
# include <sidplayfp/builders/hardsid.h>
|
|
const char ConsolePlayer::HARDSID_ID[] = "HardSID";
|
|
#endif
|
|
|
|
|
|
uint8_t* loadRom(const SID_STRING &romPath, const int size)
|
|
{
|
|
SID_IFSTREAM is(romPath.c_str(), std::ios::binary);
|
|
|
|
if (is.is_open())
|
|
{
|
|
try
|
|
{
|
|
uint8_t *buffer = new uint8_t[size];
|
|
|
|
is.read((char*)buffer, size);
|
|
if (!is.fail())
|
|
{
|
|
is.close();
|
|
return buffer;
|
|
}
|
|
delete [] buffer;
|
|
}
|
|
catch (std::bad_alloc const &ba) {}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint8_t* loadRom(const SID_STRING &romPath, const int size, const TCHAR defaultRom[])
|
|
{
|
|
// Try to load given rom
|
|
if (!romPath.empty())
|
|
{
|
|
uint8_t* buffer = loadRom(romPath, size);
|
|
if (buffer)
|
|
return buffer;
|
|
}
|
|
|
|
// Fallback to default rom path
|
|
try
|
|
{
|
|
SID_STRING dataPath(utils::getDataPath());
|
|
|
|
dataPath.append(SEPARATOR).append(TEXT("sidplayfp")).append(SEPARATOR).append(defaultRom);
|
|
|
|
#if !defined _WIN32 && defined HAVE_UNISTD_H
|
|
if (::access(dataPath.c_str(), R_OK) != 0)
|
|
{
|
|
dataPath = PKGDATADIR;
|
|
dataPath.append(defaultRom);
|
|
}
|
|
#endif
|
|
|
|
return loadRom(dataPath, size);
|
|
}
|
|
catch (utils::error const &e)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
ConsolePlayer::ConsolePlayer (const char * const name) :
|
|
Event("External Timer\n"),
|
|
m_name(name),
|
|
m_tune(0),
|
|
m_state(playerStopped),
|
|
m_outfile(NULL),
|
|
m_context(NULL),
|
|
m_filename(""),
|
|
m_quietLevel(0),
|
|
m_verboseLevel(0),
|
|
m_cpudebug(false)
|
|
{ // Other defaults
|
|
m_filter.enabled = true;
|
|
m_driver.device = NULL;
|
|
m_driver.sid = EMU_RESIDFP;
|
|
m_timer.start = 0;
|
|
m_timer.length = 0; // FOREVER
|
|
m_timer.valid = false;
|
|
m_track.first = 0;
|
|
m_track.selected = 0;
|
|
m_track.loop = false;
|
|
m_track.single = false;
|
|
m_speed.current = 1;
|
|
m_speed.max = 32;
|
|
|
|
// Read default configuration
|
|
m_iniCfg.read ();
|
|
m_engCfg = m_engine.config ();
|
|
|
|
{ // Load ini settings
|
|
IniConfig::audio_section audio = m_iniCfg.audio();
|
|
IniConfig::emulation_section emulation = m_iniCfg.emulation();
|
|
|
|
// INI Configuration Settings
|
|
m_engCfg.forceC64Model = emulation.modelForced;
|
|
m_engCfg.defaultC64Model = emulation.modelDefault;
|
|
m_engCfg.defaultSidModel = emulation.sidModel;
|
|
m_engCfg.forceSidModel = emulation.forceModel;
|
|
m_engCfg.frequency = audio.frequency;
|
|
m_engCfg.playback = audio.playback;
|
|
m_precision = audio.precision;
|
|
m_filter.enabled = emulation.filter;
|
|
m_filter.bias = emulation.bias;
|
|
m_filter.filterCurve6581 = emulation.filterCurve6581;
|
|
m_filter.filterCurve8580 = emulation.filterCurve8580;
|
|
|
|
if (!emulation.engine.empty())
|
|
{
|
|
if (emulation.engine.compare(TEXT("RESIDFP")) == 0)
|
|
{
|
|
m_driver.sid = EMU_RESIDFP;
|
|
}
|
|
else if (emulation.engine.compare(TEXT("RESID")) == 0)
|
|
{
|
|
m_driver.sid = EMU_RESID;
|
|
}
|
|
#ifdef HAVE_SIDPLAYFP_BUILDERS_HARDSID_H
|
|
else if (emulation.engine.compare(TEXT("HARDSID")) == 0)
|
|
{
|
|
m_driver.sid = EMU_HARDSID;
|
|
m_driver.output = OUT_NULL;
|
|
}
|
|
#endif
|
|
else if (emulation.engine.compare(TEXT("NONE")) == 0)
|
|
{
|
|
m_driver.sid = EMU_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
createOutput (OUT_NULL, NULL);
|
|
createSidEmu (EMU_NONE);
|
|
|
|
uint8_t *kernalRom = loadRom((m_iniCfg.sidplay2()).kernalRom, 8192, TEXT("kernal"));
|
|
uint8_t *basicRom = loadRom((m_iniCfg.sidplay2()).basicRom, 8192, TEXT("basic"));
|
|
uint8_t *chargenRom = loadRom((m_iniCfg.sidplay2()).chargenRom, 4096, TEXT("chargen"));
|
|
m_engine.setRoms(kernalRom, basicRom, chargenRom);
|
|
delete [] kernalRom;
|
|
delete [] basicRom;
|
|
delete [] chargenRom;
|
|
}
|
|
|
|
IAudio* ConsolePlayer::getWavFile(const SidTuneInfo *tuneInfo)
|
|
{
|
|
const char *title = m_outfile;
|
|
|
|
// Generate a name for the wav file
|
|
if (title == NULL)
|
|
{
|
|
title = tuneInfo->dataFileName();
|
|
const size_t length = strlen(title);
|
|
size_t i = length;
|
|
while (i > 0)
|
|
{
|
|
if (title[--i] == '.')
|
|
break;
|
|
}
|
|
if (!i) i = length;
|
|
|
|
std::string name(title, i);
|
|
|
|
// Change name based on subtune
|
|
if (tuneInfo->songs() > 1)
|
|
{
|
|
std::ostringstream sstream;
|
|
sstream << "[" << tuneInfo->currentSong() << "]";
|
|
name.append(sstream.str());
|
|
}
|
|
name.append(WavFile::extension());
|
|
title = name.c_str();
|
|
}
|
|
|
|
return new WavFile(title);
|
|
}
|
|
|
|
// Create the output object to process sound buffer
|
|
bool ConsolePlayer::createOutput (OUTPUTS driver, const SidTuneInfo *tuneInfo)
|
|
{
|
|
// Remove old audio driver
|
|
m_driver.null.close ();
|
|
m_driver.selected = &m_driver.null;
|
|
if (m_driver.device != NULL)
|
|
{
|
|
if (m_driver.device != &m_driver.null)
|
|
delete m_driver.device;
|
|
m_driver.device = NULL;
|
|
}
|
|
|
|
// Create audio driver
|
|
switch (driver)
|
|
{
|
|
case OUT_NULL:
|
|
m_driver.device = &m_driver.null;
|
|
break;
|
|
|
|
case OUT_SOUNDCARD:
|
|
try
|
|
{
|
|
m_driver.device = new audioDrv();
|
|
}
|
|
catch (std::bad_alloc const &ba)
|
|
{
|
|
m_driver.device = 0;
|
|
}
|
|
break;
|
|
|
|
case OUT_WAV:
|
|
try
|
|
{
|
|
m_driver.device = getWavFile(tuneInfo);
|
|
}
|
|
catch (std::bad_alloc const &ba)
|
|
{
|
|
m_driver.device = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Audio driver failed
|
|
if (!m_driver.device)
|
|
{
|
|
m_driver.device = &m_driver.null;
|
|
displayError (ERR_NOT_ENOUGH_MEMORY);
|
|
return false;
|
|
}
|
|
|
|
// Configure with user settings
|
|
m_driver.cfg.frequency = m_engCfg.frequency;
|
|
m_driver.cfg.precision = m_precision;
|
|
m_driver.cfg.channels = 1; // Mono
|
|
m_driver.cfg.bufSize = 0; // Recalculate
|
|
if (m_engCfg.playback == SidConfig::STEREO)
|
|
m_driver.cfg.channels = 2;
|
|
|
|
{ // Open the hardware
|
|
bool err = false;
|
|
if (!m_driver.device->open (m_driver.cfg))
|
|
err = true;
|
|
|
|
// Can't open the same driver twice
|
|
if (driver != OUT_NULL)
|
|
{
|
|
if (!m_driver.null.open (m_driver.cfg))
|
|
err = true;
|
|
}
|
|
|
|
if (err)
|
|
{
|
|
displayError(m_driver.device->getErrorString());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// See what we got
|
|
m_engCfg.frequency = m_driver.cfg.frequency;
|
|
m_precision = m_driver.cfg.precision;
|
|
switch (m_driver.cfg.channels)
|
|
{
|
|
case 1:
|
|
if (m_engCfg.playback == SidConfig::STEREO)
|
|
m_engCfg.playback = SidConfig::MONO;
|
|
break;
|
|
case 2:
|
|
if (m_engCfg.playback != SidConfig::STEREO)
|
|
m_engCfg.playback = SidConfig::STEREO;
|
|
break;
|
|
default:
|
|
cerr << m_name << ": " << "ERROR: " << m_driver.cfg.channels
|
|
<< " audio channels not supported" << endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// Create the sid emulation
|
|
bool ConsolePlayer::createSidEmu (SIDEMUS emu)
|
|
{
|
|
// Remove old driver and emulation
|
|
if (m_engCfg.sidEmulation)
|
|
{
|
|
sidbuilder *builder = m_engCfg.sidEmulation;
|
|
m_engCfg.sidEmulation = NULL;
|
|
m_engine.config (m_engCfg);
|
|
delete builder;
|
|
}
|
|
|
|
// Now setup the sid emulation
|
|
switch (emu)
|
|
{
|
|
#ifdef HAVE_SIDPLAYFP_BUILDERS_RESIDFP_H
|
|
case EMU_RESIDFP:
|
|
{
|
|
try
|
|
{
|
|
ReSIDfpBuilder *rs = new ReSIDfpBuilder( RESIDFP_ID );
|
|
|
|
m_engCfg.sidEmulation = rs;
|
|
if (!rs->getStatus()) goto createSidEmu_error;
|
|
rs->create ((m_engine.info ()).maxsids());
|
|
if (!rs->getStatus()) goto createSidEmu_error;
|
|
|
|
if (m_filter.filterCurve6581)
|
|
rs->filter6581Curve(m_filter.filterCurve6581);
|
|
if (m_filter.filterCurve8580)
|
|
rs->filter8580Curve((double)m_filter.filterCurve8580);
|
|
}
|
|
catch (std::bad_alloc const &ba) {}
|
|
break;
|
|
}
|
|
#endif // HAVE_SIDPLAYFP_BUILDERS_RESIDFP_H
|
|
|
|
#ifdef HAVE_SIDPLAYFP_BUILDERS_RESID_H
|
|
case EMU_RESID:
|
|
{
|
|
try
|
|
{
|
|
ReSIDBuilder *rs = new ReSIDBuilder( RESID_ID );
|
|
|
|
m_engCfg.sidEmulation = rs;
|
|
if (!rs->getStatus()) goto createSidEmu_error;
|
|
rs->create ((m_engine.info ()).maxsids());
|
|
if (!rs->getStatus()) goto createSidEmu_error;
|
|
|
|
rs->bias(m_filter.bias);
|
|
}
|
|
catch (std::bad_alloc const &ba) {}
|
|
break;
|
|
}
|
|
#endif // HAVE_SIDPLAYFP_BUILDERS_RESID_H
|
|
|
|
#ifdef HAVE_SIDPLAYFP_BUILDERS_HARDSID_H
|
|
case EMU_HARDSID:
|
|
{
|
|
try
|
|
{
|
|
HardSIDBuilder *hs = new HardSIDBuilder( HARDSID_ID );
|
|
|
|
m_engCfg.sidEmulation = hs;
|
|
if (!hs->getStatus()) goto createSidEmu_error;
|
|
hs->create ((m_engine.info ()).maxsids());
|
|
if (!hs->getStatus()) goto createSidEmu_error;
|
|
}
|
|
catch (std::bad_alloc const &ba) {}
|
|
break;
|
|
}
|
|
#endif // HAVE_SIDPLAYFP_BUILDERS_HARDSID_H
|
|
|
|
default:
|
|
// Emulation Not yet handled
|
|
// This default case results in the default
|
|
// emulation
|
|
break;
|
|
}
|
|
|
|
if (!m_engCfg.sidEmulation)
|
|
{
|
|
if (emu > EMU_DEFAULT)
|
|
{ // No sid emulation?
|
|
displayError (ERR_NOT_ENOUGH_MEMORY);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (m_engCfg.sidEmulation) {
|
|
/* set up SID filter. HardSID just ignores call with def. */
|
|
m_engCfg.sidEmulation->filter(m_filter.enabled);
|
|
}
|
|
|
|
return true;
|
|
|
|
createSidEmu_error:
|
|
displayError (m_engCfg.sidEmulation->error ());
|
|
delete m_engCfg.sidEmulation;
|
|
m_engCfg.sidEmulation = NULL;
|
|
return false;
|
|
}
|
|
|
|
|
|
bool ConsolePlayer::open (void)
|
|
{
|
|
if ((m_state & ~playerFast) == playerRestart)
|
|
{
|
|
if (m_quietLevel < 2)
|
|
cerr << endl;
|
|
if (m_state & playerFast)
|
|
m_driver.selected->reset ();
|
|
m_state = playerStopped;
|
|
}
|
|
|
|
// Select the required song
|
|
m_track.selected = m_tune.selectSong (m_track.selected);
|
|
if (m_engine.load (&m_tune) < 0)
|
|
{
|
|
displayError (m_engine.error ());
|
|
return false;
|
|
}
|
|
|
|
// Get tune details
|
|
const SidTuneInfo *tuneInfo = m_tune.getInfo ();
|
|
if (!m_track.single)
|
|
m_track.songs = tuneInfo->songs();
|
|
if (!createOutput (m_driver.output, tuneInfo))
|
|
return false;
|
|
if (!createSidEmu (m_driver.sid))
|
|
return false;
|
|
|
|
// Configure engine with settings
|
|
if (!m_engine.config (m_engCfg))
|
|
{ // Config failed
|
|
displayError (m_engine.error ());
|
|
return false;
|
|
}
|
|
|
|
// Start the player. Do this by fast
|
|
// forwarding to the start position
|
|
m_driver.selected = &m_driver.null;
|
|
m_speed.current = m_speed.max;
|
|
m_engine.fastForward (100 * m_speed.current);
|
|
|
|
m_engine.mute(0, 0, v1mute);
|
|
m_engine.mute(0, 1, v2mute);
|
|
m_engine.mute(0, 2, v3mute);
|
|
m_engine.mute(1, 0, v4mute);
|
|
m_engine.mute(1, 1, v5mute);
|
|
m_engine.mute(1, 2, v6mute);
|
|
|
|
// As yet we don't have a required songlength
|
|
// so try the songlength database
|
|
if (!m_timer.valid)
|
|
{
|
|
const int_least32_t length = m_database.length (m_tune);
|
|
if (length > 0)
|
|
m_timer.length = length;
|
|
}
|
|
|
|
// Set up the play timer
|
|
m_context = m_engine.getEventContext();
|
|
m_timer.stop = 0;
|
|
m_timer.stop += m_timer.length;
|
|
|
|
if (m_timer.valid)
|
|
{ // Length relative to start
|
|
m_timer.stop += m_timer.start;
|
|
}
|
|
else
|
|
{ // Check to make start time dosen't exceed end
|
|
if (m_timer.stop & (m_timer.start >= m_timer.stop))
|
|
{
|
|
displayError ("ERROR: Start time exceeds song length!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_timer.current = ~0;
|
|
m_state = playerRunning;
|
|
|
|
// Update display
|
|
menu();
|
|
event();
|
|
return true;
|
|
}
|
|
|
|
void ConsolePlayer::close ()
|
|
{
|
|
m_engine.stop();
|
|
if (m_state == playerExit)
|
|
{ // Natural finish
|
|
emuflush ();
|
|
if (m_driver.file)
|
|
cerr << (char) 7; // Bell
|
|
}
|
|
else // Destroy buffers
|
|
m_driver.selected->reset ();
|
|
|
|
// Shutdown drivers, etc
|
|
createOutput (OUT_NULL, NULL);
|
|
createSidEmu (EMU_NONE);
|
|
m_engine.load (NULL);
|
|
m_engine.config (m_engCfg);
|
|
|
|
if (m_quietLevel < 2)
|
|
{ // Correctly leave ansi mode and get prompt to
|
|
// end up in a suitable location
|
|
if ((m_iniCfg.console ()).ansi)
|
|
cerr << '\x1b' << "[0m";
|
|
#ifndef _WIN32
|
|
cerr << endl;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Flush any hardware sid fifos so all music is played
|
|
void ConsolePlayer::emuflush ()
|
|
{
|
|
switch (m_driver.sid)
|
|
{
|
|
#ifdef HAVE_SIDPLAYFP_BUILDERS_HARDSID_H
|
|
case EMU_HARDSID:
|
|
((HardSIDBuilder *)m_engCfg.sidEmulation)->flush ();
|
|
break;
|
|
#endif // HAVE_LIBHARDSID_BUILDER
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// Out play loop to be externally called
|
|
bool ConsolePlayer::play ()
|
|
{
|
|
if (m_state == playerRunning)
|
|
{
|
|
// Fill buffer
|
|
short *buffer = m_driver.selected->buffer ();
|
|
const uint_least32_t length = m_driver.cfg.bufSize;
|
|
const uint_least32_t ret = m_engine.play (buffer, length);
|
|
if (ret < length)
|
|
{
|
|
if (m_engine.isPlaying ())
|
|
{
|
|
m_state = playerError;
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
switch (m_state)
|
|
{
|
|
case playerRunning:
|
|
m_driver.selected->write ();
|
|
// Deliberate run on
|
|
case playerPaused:
|
|
// Check for a keypress (approx 250ms rate, but really depends
|
|
// on music buffer sizes). Don't do this for high quiet levels
|
|
// as chances are we are under remote control.
|
|
if ((m_quietLevel < 2) && _kbhit ())
|
|
decodeKeys ();
|
|
return true;
|
|
default:
|
|
if (m_quietLevel < 2)
|
|
cerr << endl;
|
|
m_engine.stop ();
|
|
#if HAVE_TSID == 1
|
|
if (m_tsid)
|
|
{
|
|
m_tsid.addTime ((int) m_timer.current, m_track.selected,
|
|
m_filename);
|
|
}
|
|
#elif HAVE_TSID == 2
|
|
if (m_tsid)
|
|
{
|
|
char md5[SidTune::MD5_LENGTH + 1];
|
|
m_tune.createMD5 (md5);
|
|
int_least32_t length = m_database.length (md5, m_track.selected);
|
|
// ignore errors
|
|
if (length < 0)
|
|
length = 0;
|
|
m_tsid.addTime (md5, m_filename, (uint) m_timer.current,
|
|
m_track.selected, (uint) length);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void ConsolePlayer::stop ()
|
|
{
|
|
m_state = playerStopped;
|
|
m_engine.stop ();
|
|
}
|
|
|
|
|
|
// External Timer Event
|
|
void ConsolePlayer::event (void)
|
|
{
|
|
const uint_least32_t seconds = m_engine.time();
|
|
if ( !m_quietLevel )
|
|
{
|
|
cerr << "\b\b\b\b\b" << std::setw(2) << std::setfill('0')
|
|
<< ((seconds / 60) % 100) << ':' << std::setw(2)
|
|
<< std::setfill('0') << (seconds % 60) << std::flush;
|
|
}
|
|
|
|
if (seconds != m_timer.current)
|
|
{
|
|
m_timer.current = seconds;
|
|
|
|
if (seconds == m_timer.start)
|
|
{ // Switch audio drivers.
|
|
m_driver.selected = m_driver.device;
|
|
memset (m_driver.selected->buffer (), 0, m_driver.cfg.bufSize);
|
|
m_speed.current = 1;
|
|
m_engine.fastForward (100);
|
|
if (m_cpudebug)
|
|
m_engine.debug (true, NULL);
|
|
}
|
|
else if (m_timer.stop && (seconds == m_timer.stop))
|
|
{
|
|
m_state = playerExit;
|
|
for (;;)
|
|
{
|
|
if (m_track.single)
|
|
return;
|
|
// Move to next track
|
|
m_track.selected++;
|
|
if (m_track.selected > m_track.songs)
|
|
m_track.selected = 1;
|
|
if (m_track.selected == m_track.first)
|
|
return;
|
|
m_state = playerRestart;
|
|
break;
|
|
}
|
|
if (m_track.loop)
|
|
m_state = playerRestart;
|
|
}
|
|
}
|
|
|
|
// Units in C64 clock cycles
|
|
m_context->schedule (*this, 900000, EVENT_CLOCK_PHI1);
|
|
}
|
|
|
|
|
|
void ConsolePlayer::displayError (const char *error)
|
|
{
|
|
cerr << m_name << ": " << error << endl;
|
|
}
|
|
|
|
|
|
// Keyboard handling
|
|
void ConsolePlayer::decodeKeys ()
|
|
{
|
|
do
|
|
{
|
|
const int action = keyboard_decode ();
|
|
if (action == A_INVALID)
|
|
continue;
|
|
|
|
switch (action)
|
|
{
|
|
case A_RIGHT_ARROW:
|
|
m_state = playerFastRestart;
|
|
if (!m_track.single)
|
|
{
|
|
m_track.selected++;
|
|
if (m_track.selected > m_track.songs)
|
|
m_track.selected = 1;
|
|
}
|
|
break;
|
|
|
|
case A_LEFT_ARROW:
|
|
m_state = playerFastRestart;
|
|
if (!m_track.single)
|
|
{ // Only select previous song if less than timeout
|
|
// else restart current song
|
|
if ((m_engine.time()) < SID2_PREV_SONG_TIMEOUT)
|
|
{
|
|
m_track.selected--;
|
|
if (m_track.selected < 1)
|
|
m_track.selected = m_track.songs;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case A_UP_ARROW:
|
|
m_speed.current *= 2;
|
|
if (m_speed.current > m_speed.max)
|
|
m_speed.current = m_speed.max;
|
|
m_engine.fastForward (100 * m_speed.current);
|
|
break;
|
|
|
|
case A_DOWN_ARROW:
|
|
m_speed.current = 1;
|
|
m_engine.fastForward (100);
|
|
break;
|
|
|
|
case A_HOME:
|
|
m_state = playerFastRestart;
|
|
m_track.selected = 1;
|
|
break;
|
|
|
|
case A_END:
|
|
m_state = playerFastRestart;
|
|
m_track.selected = m_track.songs;
|
|
break;
|
|
|
|
case A_PAUSED:
|
|
if (m_state == playerPaused)
|
|
{
|
|
cerr << "\b\b\b\b\b\b\b\b\b";
|
|
// Just to make sure PAUSED is removed from screen
|
|
cerr << " ";
|
|
cerr << "\b\b\b\b\b\b\b\b\b";
|
|
m_state = playerRunning;
|
|
}
|
|
else
|
|
{
|
|
cerr << " [PAUSED]";
|
|
m_state = playerPaused;
|
|
m_driver.selected->pause ();
|
|
}
|
|
break;
|
|
|
|
case A_TOGGLE_VOICE1:
|
|
v1mute = !v1mute;
|
|
m_engine.mute(0, 0, v1mute);
|
|
break;
|
|
|
|
case A_TOGGLE_VOICE2:
|
|
v2mute = !v2mute;
|
|
m_engine.mute(0, 1, v2mute);
|
|
break;
|
|
|
|
case A_TOGGLE_VOICE3:
|
|
v3mute = !v3mute;
|
|
m_engine.mute(0, 2, v3mute);
|
|
break;
|
|
|
|
case A_TOGGLE_VOICE4:
|
|
v4mute = !v4mute;
|
|
m_engine.mute(1, 0, v4mute);
|
|
break;
|
|
|
|
case A_TOGGLE_VOICE5:
|
|
v5mute = !v5mute;
|
|
m_engine.mute(1, 1, v5mute);
|
|
break;
|
|
|
|
case A_TOGGLE_VOICE6:
|
|
v6mute = !v6mute;
|
|
m_engine.mute(1, 2, v6mute);
|
|
break;
|
|
|
|
case A_TOGGLE_FILTER:
|
|
m_filter.enabled = !m_filter.enabled;
|
|
m_engCfg.sidEmulation->filter(m_filter.enabled);
|
|
break;
|
|
|
|
case A_QUIT:
|
|
m_state = playerFastExit;
|
|
return;
|
|
break;
|
|
}
|
|
} while (_kbhit ());
|
|
}
|