mirror of https://github.com/stella-emu/stella.git
Gut current audio code.
This commit is contained in:
parent
da473f4803
commit
e88751638b
|
@ -25,6 +25,20 @@
|
||||||
"string": "cpp",
|
"string": "cpp",
|
||||||
"string_view": "cpp",
|
"string_view": "cpp",
|
||||||
"system_error": "cpp",
|
"system_error": "cpp",
|
||||||
"vector": "cpp"
|
"vector": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"__bit_reference": "cpp",
|
||||||
|
"__functional_base": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"bitset": "cpp",
|
||||||
|
"chrono": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"locale": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"ratio": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,21 +53,6 @@ class SoundNull : public Sound
|
||||||
*/
|
*/
|
||||||
void setEnabled(bool state) override { }
|
void setEnabled(bool state) override { }
|
||||||
|
|
||||||
/**
|
|
||||||
Sets the number of channels (mono or stereo sound).
|
|
||||||
|
|
||||||
@param channels The number of channels
|
|
||||||
*/
|
|
||||||
void setChannels(uInt32 channels) override { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
Sets the display framerate. Sound generation for NTSC and PAL games
|
|
||||||
depends on the framerate, so we need to set it here.
|
|
||||||
|
|
||||||
@param framerate The base framerate depending on NTSC or PAL ROM
|
|
||||||
*/
|
|
||||||
void setFrameRate(float framerate) override { }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Initializes the sound device. This must be called before any
|
Initializes the sound device. This must be called before any
|
||||||
calls are made to derived methods.
|
calls are made to derived methods.
|
||||||
|
@ -92,15 +77,6 @@ class SoundNull : public Sound
|
||||||
*/
|
*/
|
||||||
void reset() override { }
|
void reset() override { }
|
||||||
|
|
||||||
/**
|
|
||||||
Sets the sound register to a given value.
|
|
||||||
|
|
||||||
@param addr The register address
|
|
||||||
@param value The value to save into the register
|
|
||||||
@param cycle The system cycle at which the register is being updated
|
|
||||||
*/
|
|
||||||
void set(uInt16 addr, uInt8 value, uInt64 cycle) override { }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sets the volume of the sound device to the specified level. The
|
Sets the volume of the sound device to the specified level. The
|
||||||
volume is given as a percentage from 0 to 100. Values outside
|
volume is given as a percentage from 0 to 100. Values outside
|
||||||
|
@ -118,54 +94,6 @@ class SoundNull : public Sound
|
||||||
*/
|
*/
|
||||||
void adjustVolume(Int8 direction) override { }
|
void adjustVolume(Int8 direction) override { }
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
Saves the current state of this device to the given Serializer.
|
|
||||||
|
|
||||||
@param out The serializer device to save to.
|
|
||||||
@return The result of the save. True on success, false on failure.
|
|
||||||
*/
|
|
||||||
bool save(Serializer& out) const override
|
|
||||||
{
|
|
||||||
out.putString("TIASound");
|
|
||||||
|
|
||||||
for(int i = 0; i < 6; ++i)
|
|
||||||
out.putByte(0);
|
|
||||||
|
|
||||||
// myLastRegisterSetCycle
|
|
||||||
out.putInt(0);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Loads the current state of this device from the given Serializer.
|
|
||||||
|
|
||||||
@param in The Serializer device to load from.
|
|
||||||
@return The result of the load. True on success, false on failure.
|
|
||||||
*/
|
|
||||||
bool load(Serializer& in) override
|
|
||||||
{
|
|
||||||
if(in.getString() != "TIASound")
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Read sound registers and discard
|
|
||||||
for(int i = 0; i < 6; ++i)
|
|
||||||
in.getByte();
|
|
||||||
|
|
||||||
// myLastRegisterSetCycle
|
|
||||||
in.getInt();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Get a descriptor for this console class (used in error checking).
|
|
||||||
|
|
||||||
@return The name of the object
|
|
||||||
*/
|
|
||||||
string name() const override { return "TIASound"; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Following constructors and assignment operators not supported
|
// Following constructors and assignment operators not supported
|
||||||
SoundNull() = delete;
|
SoundNull() = delete;
|
||||||
|
|
|
@ -22,8 +22,6 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "SDL_lib.hxx"
|
#include "SDL_lib.hxx"
|
||||||
#include "TIASnd.hxx"
|
|
||||||
#include "TIATypes.hxx"
|
|
||||||
#include "FrameBuffer.hxx"
|
#include "FrameBuffer.hxx"
|
||||||
#include "Settings.hxx"
|
#include "Settings.hxx"
|
||||||
#include "System.hxx"
|
#include "System.hxx"
|
||||||
|
@ -34,14 +32,7 @@
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
SoundSDL2::SoundSDL2(OSystem& osystem)
|
SoundSDL2::SoundSDL2(OSystem& osystem)
|
||||||
: Sound(osystem),
|
: Sound(osystem),
|
||||||
myIsEnabled(false),
|
|
||||||
myIsInitializedFlag(false),
|
myIsInitializedFlag(false),
|
||||||
myLastRegisterSetCycle(0),
|
|
||||||
myNumChannels(0),
|
|
||||||
myFragmentSizeLogBase2(0),
|
|
||||||
myFragmentSizeLogDiv1(0),
|
|
||||||
myFragmentSizeLogDiv2(0),
|
|
||||||
myIsMuted(true),
|
|
||||||
myVolume(100)
|
myVolume(100)
|
||||||
{
|
{
|
||||||
myOSystem.logMessage("SoundSDL2::SoundSDL2 started ...", 2);
|
myOSystem.logMessage("SoundSDL2::SoundSDL2 started ...", 2);
|
||||||
|
@ -80,13 +71,9 @@ SoundSDL2::SoundSDL2(OSystem& osystem)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-compute fragment-related variables as much as possible
|
|
||||||
myFragmentSizeLogBase2 = log(myHardwareSpec.samples) / log(2.0);
|
|
||||||
myFragmentSizeLogDiv1 = myFragmentSizeLogBase2 / 60.0;
|
|
||||||
myFragmentSizeLogDiv2 = (myFragmentSizeLogBase2 - 1) / 60.0;
|
|
||||||
|
|
||||||
myIsInitializedFlag = true;
|
myIsInitializedFlag = true;
|
||||||
SDL_PauseAudio(1);
|
|
||||||
|
mute(true);
|
||||||
|
|
||||||
myOSystem.logMessage("SoundSDL2::SoundSDL2 initialized", 2);
|
myOSystem.logMessage("SoundSDL2::SoundSDL2 initialized", 2);
|
||||||
}
|
}
|
||||||
|
@ -94,12 +81,9 @@ SoundSDL2::SoundSDL2(OSystem& osystem)
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
SoundSDL2::~SoundSDL2()
|
SoundSDL2::~SoundSDL2()
|
||||||
{
|
{
|
||||||
// Close the SDL audio system if it's initialized
|
if (!myIsInitializedFlag) return;
|
||||||
if(myIsInitializedFlag)
|
|
||||||
{
|
SDL_CloseAudio();
|
||||||
SDL_CloseAudio();
|
|
||||||
myIsEnabled = myIsInitializedFlag = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -115,22 +99,16 @@ void SoundSDL2::setEnabled(bool state)
|
||||||
void SoundSDL2::open()
|
void SoundSDL2::open()
|
||||||
{
|
{
|
||||||
myOSystem.logMessage("SoundSDL2::open started ...", 2);
|
myOSystem.logMessage("SoundSDL2::open started ...", 2);
|
||||||
myIsEnabled = false;
|
|
||||||
mute(true);
|
mute(true);
|
||||||
if(!myIsInitializedFlag || !myOSystem.settings().getBool("sound"))
|
|
||||||
|
if(!myOSystem.settings().getBool("sound"))
|
||||||
{
|
{
|
||||||
myOSystem.logMessage("Sound disabled\n", 1);
|
myOSystem.logMessage("Sound disabled\n", 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now initialize the TIASound object which will actually generate sound
|
|
||||||
myTIASound.outputFrequency(myHardwareSpec.freq);
|
|
||||||
const string& chanResult =
|
|
||||||
myTIASound.channels(myHardwareSpec.channels, myNumChannels == 2);
|
|
||||||
|
|
||||||
// Adjust volume to that defined in settings
|
// Adjust volume to that defined in settings
|
||||||
myVolume = myOSystem.settings().getInt("volume");
|
setVolume(myOSystem.settings().getInt("volume"));
|
||||||
setVolume(myVolume);
|
|
||||||
|
|
||||||
// Show some info
|
// Show some info
|
||||||
ostringstream buf;
|
ostringstream buf;
|
||||||
|
@ -139,12 +117,10 @@ void SoundSDL2::open()
|
||||||
<< " Frag size: " << uInt32(myHardwareSpec.samples) << endl
|
<< " Frag size: " << uInt32(myHardwareSpec.samples) << endl
|
||||||
<< " Frequency: " << uInt32(myHardwareSpec.freq) << endl
|
<< " Frequency: " << uInt32(myHardwareSpec.freq) << endl
|
||||||
<< " Channels: " << uInt32(myHardwareSpec.channels)
|
<< " Channels: " << uInt32(myHardwareSpec.channels)
|
||||||
<< " (" << chanResult << ")" << endl
|
|
||||||
<< endl;
|
<< endl;
|
||||||
myOSystem.logMessage(buf.str(), 1);
|
myOSystem.logMessage(buf.str(), 1);
|
||||||
|
|
||||||
// And start the SDL sound subsystem ...
|
// And start the SDL sound subsystem ...
|
||||||
myIsEnabled = true;
|
|
||||||
mute(false);
|
mute(false);
|
||||||
|
|
||||||
myOSystem.logMessage("SoundSDL2::open finished", 2);
|
myOSystem.logMessage("SoundSDL2::open finished", 2);
|
||||||
|
@ -153,15 +129,11 @@ void SoundSDL2::open()
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL2::close()
|
void SoundSDL2::close()
|
||||||
{
|
{
|
||||||
if(myIsInitializedFlag)
|
if(!myIsInitializedFlag) return;
|
||||||
{
|
|
||||||
myIsEnabled = false;
|
mute(true);
|
||||||
SDL_PauseAudio(1);
|
myOSystem.logMessage("SoundSDL2::close", 2);
|
||||||
myLastRegisterSetCycle = 0;
|
|
||||||
myTIASound.reset();
|
|
||||||
myRegWriteQueue.clear();
|
|
||||||
myOSystem.logMessage("SoundSDL2::close", 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -169,23 +141,13 @@ void SoundSDL2::mute(bool state)
|
||||||
{
|
{
|
||||||
if(myIsInitializedFlag)
|
if(myIsInitializedFlag)
|
||||||
{
|
{
|
||||||
myIsMuted = state;
|
SDL_PauseAudio(state ? 1 : 0);
|
||||||
SDL_PauseAudio(myIsMuted ? 1 : 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL2::reset()
|
void SoundSDL2::reset()
|
||||||
{
|
{}
|
||||||
if(myIsInitializedFlag)
|
|
||||||
{
|
|
||||||
SDL_PauseAudio(1);
|
|
||||||
myLastRegisterSetCycle = 0;
|
|
||||||
myTIASound.reset();
|
|
||||||
myRegWriteQueue.clear();
|
|
||||||
mute(myIsMuted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL2::setVolume(Int32 percent)
|
void SoundSDL2::setVolume(Int32 percent)
|
||||||
|
@ -195,7 +157,6 @@ void SoundSDL2::setVolume(Int32 percent)
|
||||||
myOSystem.settings().setValue("volume", percent);
|
myOSystem.settings().setValue("volume", percent);
|
||||||
SDL_LockAudio();
|
SDL_LockAudio();
|
||||||
myVolume = percent;
|
myVolume = percent;
|
||||||
myTIASound.volume(percent);
|
|
||||||
SDL_UnlockAudio();
|
SDL_UnlockAudio();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,286 +187,14 @@ void SoundSDL2::adjustVolume(Int8 direction)
|
||||||
myOSystem.frameBuffer().showMessage(message);
|
myOSystem.frameBuffer().showMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void SoundSDL2::setChannels(uInt32 channels)
|
|
||||||
{
|
|
||||||
if(channels == 1 || channels == 2)
|
|
||||||
myNumChannels = channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void SoundSDL2::setFrameRate(float framerate)
|
|
||||||
{
|
|
||||||
// Recalculate since frame rate has changed
|
|
||||||
// FIXME - should we clear out the queue or adjust the values in it?
|
|
||||||
myFragmentSizeLogDiv1 = myFragmentSizeLogBase2 / framerate;
|
|
||||||
myFragmentSizeLogDiv2 = (myFragmentSizeLogBase2 - 1) / framerate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void SoundSDL2::set(uInt16 addr, uInt8 value, uInt64 cycle)
|
|
||||||
{
|
|
||||||
SDL_LockAudio();
|
|
||||||
|
|
||||||
// First, calculate how many seconds would have past since the last
|
|
||||||
// register write on a real 2600
|
|
||||||
double delta = double(cycle - myLastRegisterSetCycle) / 1193191.66666667;
|
|
||||||
|
|
||||||
// Now, adjust the time based on the frame rate the user has selected. For
|
|
||||||
// the sound to "scale" correctly, we have to know the games real frame
|
|
||||||
// rate (e.g., 50 or 60) and the currently emulated frame rate. We use these
|
|
||||||
// values to "scale" the time before the register change occurs.
|
|
||||||
myRegWriteQueue.enqueue(addr, value, delta);
|
|
||||||
|
|
||||||
// Update last cycle counter to the current cycle
|
|
||||||
myLastRegisterSetCycle = cycle;
|
|
||||||
|
|
||||||
SDL_UnlockAudio();
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL2::processFragment(Int16* stream, uInt32 length)
|
void SoundSDL2::processFragment(Int16* stream, uInt32 length)
|
||||||
{
|
{}
|
||||||
uInt32 channels = myHardwareSpec.channels;
|
|
||||||
length = length / channels;
|
|
||||||
|
|
||||||
// If there are excessive items on the queue then we'll remove some
|
|
||||||
if(myRegWriteQueue.duration() > myFragmentSizeLogDiv1)
|
|
||||||
{
|
|
||||||
double removed = 0.0;
|
|
||||||
while(removed < myFragmentSizeLogDiv2)
|
|
||||||
{
|
|
||||||
RegWrite& info = myRegWriteQueue.front();
|
|
||||||
removed += info.delta;
|
|
||||||
myTIASound.set(info.addr, info.value);
|
|
||||||
myRegWriteQueue.dequeue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double position = 0.0;
|
|
||||||
double remaining = length;
|
|
||||||
|
|
||||||
while(remaining > 0.0)
|
|
||||||
{
|
|
||||||
if(myRegWriteQueue.size() == 0)
|
|
||||||
{
|
|
||||||
// There are no more pending TIA sound register updates so we'll
|
|
||||||
// use the current settings to finish filling the sound fragment
|
|
||||||
myTIASound.process(stream + (uInt32(position) * channels),
|
|
||||||
length - uInt32(position));
|
|
||||||
|
|
||||||
// Since we had to fill the fragment we'll reset the cycle counter
|
|
||||||
// to zero. NOTE: This isn't 100% correct, however, it'll do for
|
|
||||||
// now. We should really remember the overrun and remove it from
|
|
||||||
// the delta of the next write.
|
|
||||||
myLastRegisterSetCycle = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// There are pending TIA sound register updates so we need to
|
|
||||||
// update the sound buffer to the point of the next register update
|
|
||||||
RegWrite& info = myRegWriteQueue.front();
|
|
||||||
|
|
||||||
// How long will the remaining samples in the fragment take to play
|
|
||||||
double duration = remaining / myHardwareSpec.freq;
|
|
||||||
|
|
||||||
// Does the register update occur before the end of the fragment?
|
|
||||||
if(info.delta <= duration)
|
|
||||||
{
|
|
||||||
// If the register update time hasn't already passed then
|
|
||||||
// process samples upto the point where it should occur
|
|
||||||
if(info.delta > 0.0)
|
|
||||||
{
|
|
||||||
// Process the fragment upto the next TIA register write. We
|
|
||||||
// round the count passed to process up if needed.
|
|
||||||
double samples = (myHardwareSpec.freq * info.delta);
|
|
||||||
myTIASound.process(stream + (uInt32(position) * channels),
|
|
||||||
uInt32(samples) + uInt32(position + samples) -
|
|
||||||
(uInt32(position) + uInt32(samples)));
|
|
||||||
|
|
||||||
position += samples;
|
|
||||||
remaining -= samples;
|
|
||||||
}
|
|
||||||
myTIASound.set(info.addr, info.value);
|
|
||||||
myRegWriteQueue.dequeue();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The next register update occurs in the next fragment so finish
|
|
||||||
// this fragment with the current TIA settings and reduce the register
|
|
||||||
// update delay by the corresponding amount of time
|
|
||||||
myTIASound.process(stream + (uInt32(position) * channels),
|
|
||||||
length - uInt32(position));
|
|
||||||
info.delta -= duration;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL2::callback(void* udata, uInt8* stream, int len)
|
void SoundSDL2::callback(void* udata, uInt8* stream, int len)
|
||||||
{
|
{
|
||||||
SoundSDL2* sound = static_cast<SoundSDL2*>(udata);
|
SDL_memset(stream, 0, len); // Write 'silence'
|
||||||
if(sound->myIsEnabled)
|
|
||||||
{
|
|
||||||
// The callback is requesting 8-bit (unsigned) data, but the TIA sound
|
|
||||||
// emulator deals in 16-bit (signed) data
|
|
||||||
// So, we need to convert the pointer and half the length
|
|
||||||
sound->processFragment(reinterpret_cast<Int16*>(stream), uInt32(len) >> 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SDL_memset(stream, 0, len); // Write 'silence'
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
bool SoundSDL2::save(Serializer& out) const
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
out.putString(name());
|
|
||||||
|
|
||||||
// Only get the TIA sound registers if sound is enabled
|
|
||||||
if(myIsInitializedFlag)
|
|
||||||
{
|
|
||||||
out.putByte(myTIASound.get(TIARegister::AUDC0));
|
|
||||||
out.putByte(myTIASound.get(TIARegister::AUDC1));
|
|
||||||
out.putByte(myTIASound.get(TIARegister::AUDF0));
|
|
||||||
out.putByte(myTIASound.get(TIARegister::AUDF1));
|
|
||||||
out.putByte(myTIASound.get(TIARegister::AUDV0));
|
|
||||||
out.putByte(myTIASound.get(TIARegister::AUDV1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
for(int i = 0; i < 6; ++i)
|
|
||||||
out.putByte(0);
|
|
||||||
|
|
||||||
out.putLong(myLastRegisterSetCycle);
|
|
||||||
}
|
|
||||||
catch(...)
|
|
||||||
{
|
|
||||||
myOSystem.logMessage("ERROR: SoundSDL2::save", 0);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
bool SoundSDL2::load(Serializer& in)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if(in.getString() != name())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Only update the TIA sound registers if sound is enabled
|
|
||||||
// Make sure to empty the queue of previous sound fragments
|
|
||||||
if(myIsInitializedFlag)
|
|
||||||
{
|
|
||||||
SDL_PauseAudio(1);
|
|
||||||
myRegWriteQueue.clear();
|
|
||||||
myTIASound.set(TIARegister::AUDC0, in.getByte());
|
|
||||||
myTIASound.set(TIARegister::AUDC1, in.getByte());
|
|
||||||
myTIASound.set(TIARegister::AUDF0, in.getByte());
|
|
||||||
myTIASound.set(TIARegister::AUDF1, in.getByte());
|
|
||||||
myTIASound.set(TIARegister::AUDV0, in.getByte());
|
|
||||||
myTIASound.set(TIARegister::AUDV1, in.getByte());
|
|
||||||
if(!myIsMuted) SDL_PauseAudio(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
for(int i = 0; i < 6; ++i)
|
|
||||||
in.getByte();
|
|
||||||
|
|
||||||
myLastRegisterSetCycle = in.getLong();
|
|
||||||
}
|
|
||||||
catch(...)
|
|
||||||
{
|
|
||||||
myOSystem.logMessage("ERROR: SoundSDL2::load", 0);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
SoundSDL2::RegWriteQueue::RegWriteQueue(uInt32 capacity)
|
|
||||||
: myBuffer(make_unique<RegWrite[]>(capacity)),
|
|
||||||
myCapacity(capacity),
|
|
||||||
mySize(0),
|
|
||||||
myHead(0),
|
|
||||||
myTail(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void SoundSDL2::RegWriteQueue::clear()
|
|
||||||
{
|
|
||||||
myHead = myTail = mySize = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void SoundSDL2::RegWriteQueue::dequeue()
|
|
||||||
{
|
|
||||||
if(mySize > 0)
|
|
||||||
{
|
|
||||||
myHead = (myHead + 1) % myCapacity;
|
|
||||||
--mySize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
double SoundSDL2::RegWriteQueue::duration() const
|
|
||||||
{
|
|
||||||
double duration = 0.0;
|
|
||||||
for(uInt32 i = 0; i < mySize; ++i)
|
|
||||||
{
|
|
||||||
duration += myBuffer[(myHead + i) % myCapacity].delta;
|
|
||||||
}
|
|
||||||
return duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void SoundSDL2::RegWriteQueue::enqueue(uInt16 addr, uInt8 value, double delta)
|
|
||||||
{
|
|
||||||
// If an attempt is made to enqueue more than the queue can hold then
|
|
||||||
// we'll enlarge the queue's capacity.
|
|
||||||
if(mySize == myCapacity)
|
|
||||||
grow();
|
|
||||||
|
|
||||||
RegWrite& reg = myBuffer[myTail];
|
|
||||||
reg.addr = addr;
|
|
||||||
reg.value = value;
|
|
||||||
reg.delta = delta;
|
|
||||||
myTail = (myTail + 1) % myCapacity;
|
|
||||||
++mySize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
SoundSDL2::RegWrite& SoundSDL2::RegWriteQueue::front() const
|
|
||||||
{
|
|
||||||
assert(mySize != 0);
|
|
||||||
return myBuffer[myHead];
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
uInt32 SoundSDL2::RegWriteQueue::size() const
|
|
||||||
{
|
|
||||||
return mySize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void SoundSDL2::RegWriteQueue::grow()
|
|
||||||
{
|
|
||||||
unique_ptr<RegWrite[]> buffer = make_unique<RegWrite[]>(myCapacity*2);
|
|
||||||
for(uInt32 i = 0; i < mySize; ++i)
|
|
||||||
buffer[i] = myBuffer[(myHead + i) % myCapacity];
|
|
||||||
|
|
||||||
myHead = 0;
|
|
||||||
myTail = mySize;
|
|
||||||
myCapacity *= 2;
|
|
||||||
|
|
||||||
myBuffer = std::move(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SOUND_SUPPORT
|
#endif // SOUND_SUPPORT
|
||||||
|
|
|
@ -25,7 +25,6 @@ class OSystem;
|
||||||
#include "SDL_lib.hxx"
|
#include "SDL_lib.hxx"
|
||||||
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "TIASnd.hxx"
|
|
||||||
#include "Sound.hxx"
|
#include "Sound.hxx"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,28 +50,9 @@ class SoundSDL2 : public Sound
|
||||||
/**
|
/**
|
||||||
Enables/disables the sound subsystem.
|
Enables/disables the sound subsystem.
|
||||||
|
|
||||||
@param state True or false, to enable or disable the sound system
|
@param enable Either true or false, to enable or disable the sound system
|
||||||
*/
|
*/
|
||||||
void setEnabled(bool state) override;
|
void setEnabled(bool enable) override;
|
||||||
|
|
||||||
/**
|
|
||||||
Sets the number of channels (mono or stereo sound). Note that this
|
|
||||||
determines how the emulation should 'mix' the channels of the TIA sound
|
|
||||||
system (of which there are always two). It does not specify the actual
|
|
||||||
number of hardware channels that SDL should use; it will always attempt
|
|
||||||
to use two channels in hardware.
|
|
||||||
|
|
||||||
@param channels The number of channels
|
|
||||||
*/
|
|
||||||
void setChannels(uInt32 channels) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Sets the display framerate. Sound generation for NTSC and PAL games
|
|
||||||
depends on the framerate, so we need to set it here.
|
|
||||||
|
|
||||||
@param framerate The base framerate depending on NTSC or PAL ROM
|
|
||||||
*/
|
|
||||||
void setFrameRate(float framerate) override;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Initializes the sound device. This must be called before any
|
Initializes the sound device. This must be called before any
|
||||||
|
@ -98,15 +78,6 @@ class SoundSDL2 : public Sound
|
||||||
*/
|
*/
|
||||||
void reset() override;
|
void reset() override;
|
||||||
|
|
||||||
/**
|
|
||||||
Sets the sound register to a given value.
|
|
||||||
|
|
||||||
@param addr The register address
|
|
||||||
@param value The value to save into the register
|
|
||||||
@param cycle The system cycle at which the register is being updated
|
|
||||||
*/
|
|
||||||
void set(uInt16 addr, uInt8 value, uInt64 cycle) override;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sets the volume of the sound device to the specified level. The
|
Sets the volume of the sound device to the specified level. The
|
||||||
volume is given as a percentage from 0 to 100. Values outside
|
volume is given as a percentage from 0 to 100. Values outside
|
||||||
|
@ -124,30 +95,6 @@ class SoundSDL2 : public Sound
|
||||||
*/
|
*/
|
||||||
void adjustVolume(Int8 direction) override;
|
void adjustVolume(Int8 direction) override;
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
Saves the current state of this device to the given Serializer.
|
|
||||||
|
|
||||||
@param out The serializer device to save to.
|
|
||||||
@return The result of the save. True on success, false on failure.
|
|
||||||
*/
|
|
||||||
bool save(Serializer& out) const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Loads the current state of this device from the given Serializer.
|
|
||||||
|
|
||||||
@param in The Serializer device to load from.
|
|
||||||
@return The result of the load. True on success, false on failure.
|
|
||||||
*/
|
|
||||||
bool load(Serializer& in) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Get a descriptor for this console class (used in error checking).
|
|
||||||
|
|
||||||
@return The name of the object
|
|
||||||
*/
|
|
||||||
string name() const override { return "TIASound"; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
Invoked by the sound callback to process the next sound fragment.
|
Invoked by the sound callback to process the next sound fragment.
|
||||||
|
@ -159,123 +106,16 @@ class SoundSDL2 : public Sound
|
||||||
*/
|
*/
|
||||||
void processFragment(Int16* stream, uInt32 length);
|
void processFragment(Int16* stream, uInt32 length);
|
||||||
|
|
||||||
protected:
|
|
||||||
// Struct to hold information regarding a TIA sound register write
|
|
||||||
struct RegWrite
|
|
||||||
{
|
|
||||||
uInt16 addr;
|
|
||||||
uInt8 value;
|
|
||||||
double delta;
|
|
||||||
|
|
||||||
RegWrite(uInt16 a = 0, uInt8 v = 0, double d = 0.0)
|
|
||||||
: addr(a), value(v), delta(d) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
A queue class used to hold TIA sound register writes before being
|
|
||||||
processed while creating a sound fragment.
|
|
||||||
*/
|
|
||||||
class RegWriteQueue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
Create a new queue instance with the specified initial
|
|
||||||
capacity. If the queue ever reaches its capacity then it will
|
|
||||||
automatically increase its size.
|
|
||||||
*/
|
|
||||||
RegWriteQueue(uInt32 capacity = 512);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
Clear any items stored in the queue.
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
/**
|
|
||||||
Dequeue the first object in the queue.
|
|
||||||
*/
|
|
||||||
void dequeue();
|
|
||||||
|
|
||||||
/**
|
|
||||||
Return the duration of all the items in the queue.
|
|
||||||
*/
|
|
||||||
double duration() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Enqueue the specified object.
|
|
||||||
*/
|
|
||||||
void enqueue(uInt16 addr, uInt8 value, double delta);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Return the item at the front on the queue.
|
|
||||||
|
|
||||||
@return The item at the front of the queue.
|
|
||||||
*/
|
|
||||||
RegWrite& front() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Answers the number of items currently in the queue.
|
|
||||||
|
|
||||||
@return The number of items in the queue.
|
|
||||||
*/
|
|
||||||
uInt32 size() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Increase the size of the queue
|
|
||||||
void grow();
|
|
||||||
|
|
||||||
private:
|
|
||||||
unique_ptr<RegWrite[]> myBuffer;
|
|
||||||
uInt32 myCapacity;
|
|
||||||
uInt32 mySize;
|
|
||||||
uInt32 myHead;
|
|
||||||
uInt32 myTail;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Following constructors and assignment operators not supported
|
|
||||||
RegWriteQueue(const RegWriteQueue&) = delete;
|
|
||||||
RegWriteQueue(RegWriteQueue&&) = delete;
|
|
||||||
RegWriteQueue& operator=(const RegWriteQueue&) = delete;
|
|
||||||
RegWriteQueue& operator=(RegWriteQueue&&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// TIASound emulation object
|
|
||||||
TIASound myTIASound;
|
|
||||||
|
|
||||||
// Indicates if the sound subsystem is to be initialized
|
|
||||||
bool myIsEnabled;
|
|
||||||
|
|
||||||
// Indicates if the sound device was successfully initialized
|
// Indicates if the sound device was successfully initialized
|
||||||
bool myIsInitializedFlag;
|
bool myIsInitializedFlag;
|
||||||
|
|
||||||
// Indicates the cycle when a sound register was last set
|
|
||||||
uInt64 myLastRegisterSetCycle;
|
|
||||||
|
|
||||||
// Indicates the number of channels (mono or stereo)
|
|
||||||
uInt32 myNumChannels;
|
|
||||||
|
|
||||||
// Log base 2 of the selected fragment size
|
|
||||||
double myFragmentSizeLogBase2;
|
|
||||||
|
|
||||||
// The myFragmentSizeLogBase2 variable is used in only two places,
|
|
||||||
// both of which involve an expensive division in the sound
|
|
||||||
// processing callback
|
|
||||||
// These are pre-computed to speed up the callback as much as possible
|
|
||||||
double myFragmentSizeLogDiv1, myFragmentSizeLogDiv2;
|
|
||||||
|
|
||||||
// Indicates if the sound is currently muted
|
|
||||||
bool myIsMuted;
|
|
||||||
|
|
||||||
// Current volume as a percentage (0 - 100)
|
// Current volume as a percentage (0 - 100)
|
||||||
uInt32 myVolume;
|
uInt32 myVolume;
|
||||||
|
|
||||||
// Audio specification structure
|
// Audio specification structure
|
||||||
SDL_AudioSpec myHardwareSpec;
|
SDL_AudioSpec myHardwareSpec;
|
||||||
|
|
||||||
// Queue of TIA register writes
|
|
||||||
RegWriteQueue myRegWriteQueue;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Callback function invoked by the SDL Audio library when it needs data
|
// Callback function invoked by the SDL Audio library when it needs data
|
||||||
static void callback(void* udata, uInt8* stream, int len);
|
static void callback(void* udata, uInt8* stream, int len);
|
||||||
|
|
|
@ -89,7 +89,7 @@ Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
|
||||||
// Create subsystems for the console
|
// Create subsystems for the console
|
||||||
my6502 = make_unique<M6502>(myOSystem.settings());
|
my6502 = make_unique<M6502>(myOSystem.settings());
|
||||||
myRiot = make_unique<M6532>(*this, myOSystem.settings());
|
myRiot = make_unique<M6532>(*this, myOSystem.settings());
|
||||||
myTIA = make_unique<TIA>(*this, myOSystem.sound(), myOSystem.settings());
|
myTIA = make_unique<TIA>(*this, myOSystem.settings());
|
||||||
myFrameManager = make_unique<FrameManager>();
|
myFrameManager = make_unique<FrameManager>();
|
||||||
mySwitches = make_unique<Switches>(myEvent, myProperties);
|
mySwitches = make_unique<Switches>(myEvent, myProperties);
|
||||||
|
|
||||||
|
@ -562,11 +562,13 @@ void Console::initializeAudio()
|
||||||
// the commandline, but it can't be saved.
|
// the commandline, but it can't be saved.
|
||||||
int framerate = myOSystem.settings().getInt("framerate");
|
int framerate = myOSystem.settings().getInt("framerate");
|
||||||
if(framerate > 0) myFramerate = float(framerate);
|
if(framerate > 0) myFramerate = float(framerate);
|
||||||
const string& sound = myProperties.get(Cartridge_Sound);
|
|
||||||
|
|
||||||
myOSystem.sound().close();
|
myOSystem.sound().close();
|
||||||
myOSystem.sound().setChannels(sound == "STEREO" ? 2 : 1);
|
|
||||||
myOSystem.sound().setFrameRate(myFramerate);
|
// SND_TODO Communicate channels to TIA
|
||||||
|
// const string& sound = myProperties.get(Cartridge_Sound);
|
||||||
|
// myOSystem.sound().setChannels(sound == "STEREO" ? 2 : 1);
|
||||||
|
|
||||||
myOSystem.sound().open();
|
myOSystem.sound().open();
|
||||||
|
|
||||||
// Make sure auto-frame calculation is only enabled when necessary
|
// Make sure auto-frame calculation is only enabled when necessary
|
||||||
|
@ -942,7 +944,7 @@ void Console::setFramerate(float framerate)
|
||||||
{
|
{
|
||||||
myFramerate = framerate;
|
myFramerate = framerate;
|
||||||
myOSystem.setFramerate(framerate);
|
myOSystem.setFramerate(framerate);
|
||||||
myOSystem.sound().setFrameRate(framerate);
|
// myOSystem.sound().setFrameRate(framerate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
|
|
||||||
class OSystem;
|
class OSystem;
|
||||||
|
|
||||||
#include "Serializable.hxx"
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +28,7 @@ class OSystem;
|
||||||
|
|
||||||
@author Stephen Anthony
|
@author Stephen Anthony
|
||||||
*/
|
*/
|
||||||
class Sound : public Serializable
|
class Sound
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -47,21 +46,6 @@ class Sound : public Serializable
|
||||||
*/
|
*/
|
||||||
virtual void setEnabled(bool enable) = 0;
|
virtual void setEnabled(bool enable) = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
Sets the number of channels (mono or stereo sound).
|
|
||||||
|
|
||||||
@param channels The number of channels
|
|
||||||
*/
|
|
||||||
virtual void setChannels(uInt32 channels) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Sets the display framerate. Sound generation for NTSC and PAL games
|
|
||||||
depends on the framerate, so we need to set it here.
|
|
||||||
|
|
||||||
@param framerate The base framerate depending on NTSC or PAL ROM
|
|
||||||
*/
|
|
||||||
virtual void setFrameRate(float framerate) = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Start the sound system, initializing it if necessary. This must be
|
Start the sound system, initializing it if necessary. This must be
|
||||||
called before any calls are made to derived methods.
|
called before any calls are made to derived methods.
|
||||||
|
@ -86,15 +70,6 @@ class Sound : public Serializable
|
||||||
*/
|
*/
|
||||||
virtual void reset() = 0;
|
virtual void reset() = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
Sets the sound register to a given value.
|
|
||||||
|
|
||||||
@param addr The register address
|
|
||||||
@param value The value to save into the register
|
|
||||||
@param cycle The system cycle at which the register is being updated
|
|
||||||
*/
|
|
||||||
virtual void set(uInt16 addr, uInt8 value, uInt64 cycle) = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sets the volume of the sound device to the specified level. The
|
Sets the volume of the sound device to the specified level. The
|
||||||
volume is given as a percentage from 0 to 100. Values outside
|
volume is given as a percentage from 0 to 100. Values outside
|
||||||
|
|
|
@ -1,388 +0,0 @@
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// SSSS tt lll lll
|
|
||||||
// SS SS tt ll ll
|
|
||||||
// SS tttttt eeee ll ll aaaa
|
|
||||||
// SSSS tt ee ee ll ll aa
|
|
||||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
||||||
// SS SS tt ee ll ll aa aa
|
|
||||||
// SSSS ttt eeeee llll llll aaaaa
|
|
||||||
//
|
|
||||||
// Copyright (c) 1995-2017 by Bradford W. Mott, Stephen Anthony
|
|
||||||
// and the Stella Team
|
|
||||||
//
|
|
||||||
// See the file "License.txt" for information on usage and redistribution of
|
|
||||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
#include "System.hxx"
|
|
||||||
#include "TIATypes.hxx"
|
|
||||||
#include "TIASnd.hxx"
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
TIASound::TIASound(Int32 outputFrequency)
|
|
||||||
: myChannelMode(Hardware2Stereo),
|
|
||||||
myOutputFrequency(outputFrequency),
|
|
||||||
myOutputCounter(0),
|
|
||||||
myVolumePercentage(100)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void TIASound::reset()
|
|
||||||
{
|
|
||||||
// Fill the polynomials
|
|
||||||
polyInit(Bit4, 4, 4, 3);
|
|
||||||
polyInit(Bit5, 5, 5, 3);
|
|
||||||
polyInit(Bit9, 9, 9, 5);
|
|
||||||
|
|
||||||
// Initialize instance variables
|
|
||||||
for(int chan = 0; chan <= 1; ++chan)
|
|
||||||
{
|
|
||||||
myVolume[chan] = 0;
|
|
||||||
myDivNCnt[chan] = 0;
|
|
||||||
myDivNMax[chan] = 0;
|
|
||||||
myDiv3Cnt[chan] = 3;
|
|
||||||
myAUDC[chan] = 0;
|
|
||||||
myAUDF[chan] = 0;
|
|
||||||
myAUDV[chan] = 0;
|
|
||||||
myP4[chan] = 0;
|
|
||||||
myP5[chan] = 0;
|
|
||||||
myP9[chan] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
myOutputCounter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void TIASound::outputFrequency(Int32 freq)
|
|
||||||
{
|
|
||||||
myOutputFrequency = freq;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
string TIASound::channels(uInt32 hardware, bool stereo)
|
|
||||||
{
|
|
||||||
if(hardware == 1)
|
|
||||||
myChannelMode = Hardware1;
|
|
||||||
else
|
|
||||||
myChannelMode = stereo ? Hardware2Stereo : Hardware2Mono;
|
|
||||||
|
|
||||||
switch(myChannelMode)
|
|
||||||
{
|
|
||||||
case Hardware1: return "Hardware1";
|
|
||||||
case Hardware2Mono: return "Hardware2Mono";
|
|
||||||
case Hardware2Stereo: return "Hardware2Stereo";
|
|
||||||
}
|
|
||||||
return EmptyString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void TIASound::set(uInt16 address, uInt8 value)
|
|
||||||
{
|
|
||||||
int chan = ~address & 0x1;
|
|
||||||
switch(address)
|
|
||||||
{
|
|
||||||
case TIARegister::AUDC0:
|
|
||||||
case TIARegister::AUDC1:
|
|
||||||
myAUDC[chan] = value & 0x0f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TIARegister::AUDF0:
|
|
||||||
case TIARegister::AUDF1:
|
|
||||||
myAUDF[chan] = value & 0x1f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TIARegister::AUDV0:
|
|
||||||
case TIARegister::AUDV1:
|
|
||||||
myAUDV[chan] = (value & 0x0f) << AUDV_SHIFT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uInt16 newVal = 0;
|
|
||||||
|
|
||||||
// An AUDC value of 0 is a special case
|
|
||||||
if (myAUDC[chan] == SET_TO_1 || myAUDC[chan] == POLY5_POLY5)
|
|
||||||
{
|
|
||||||
// Indicate the clock is zero so no processing will occur,
|
|
||||||
// and set the output to the selected volume
|
|
||||||
newVal = 0;
|
|
||||||
myVolume[chan] = (myAUDV[chan] * myVolumePercentage) / 100;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Otherwise calculate the 'divide by N' value
|
|
||||||
newVal = myAUDF[chan] + 1;
|
|
||||||
|
|
||||||
// If bits 2 & 3 are set, then multiply the 'div by n' count by 3
|
|
||||||
if((myAUDC[chan] & DIV3_MASK) == DIV3_MASK && myAUDC[chan] != POLY5_DIV3)
|
|
||||||
newVal *= 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only reset those channels that have changed
|
|
||||||
if(newVal != myDivNMax[chan])
|
|
||||||
{
|
|
||||||
// Reset the divide by n counters
|
|
||||||
myDivNMax[chan] = newVal;
|
|
||||||
|
|
||||||
// If the channel is now volume only or was volume only,
|
|
||||||
// reset the counter (otherwise let it complete the previous)
|
|
||||||
if ((myDivNCnt[chan] == 0) || (newVal == 0))
|
|
||||||
myDivNCnt[chan] = newVal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
uInt8 TIASound::get(uInt16 address) const
|
|
||||||
{
|
|
||||||
switch(address)
|
|
||||||
{
|
|
||||||
case TIARegister::AUDC0: return myAUDC[0];
|
|
||||||
case TIARegister::AUDC1: return myAUDC[1];
|
|
||||||
case TIARegister::AUDF0: return myAUDF[0];
|
|
||||||
case TIARegister::AUDF1: return myAUDF[1];
|
|
||||||
case TIARegister::AUDV0: return myAUDV[0] >> AUDV_SHIFT;
|
|
||||||
case TIARegister::AUDV1: return myAUDV[1] >> AUDV_SHIFT;
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void TIASound::volume(uInt32 percent)
|
|
||||||
{
|
|
||||||
if(percent <= 100)
|
|
||||||
myVolumePercentage = percent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void TIASound::process(Int16* buffer, uInt32 samples)
|
|
||||||
{
|
|
||||||
// Make temporary local copy
|
|
||||||
uInt8 audc0 = myAUDC[0], audc1 = myAUDC[1];
|
|
||||||
uInt8 p5_0 = myP5[0], p5_1 = myP5[1];
|
|
||||||
uInt8 div_n_cnt0 = myDivNCnt[0], div_n_cnt1 = myDivNCnt[1];
|
|
||||||
Int16 v0 = myVolume[0], v1 = myVolume[1];
|
|
||||||
|
|
||||||
// Take external volume into account
|
|
||||||
Int16 audv0 = (myAUDV[0] * myVolumePercentage) / 100,
|
|
||||||
audv1 = (myAUDV[1] * myVolumePercentage) / 100;
|
|
||||||
|
|
||||||
// Loop until the sample buffer is full
|
|
||||||
while(samples > 0)
|
|
||||||
{
|
|
||||||
// Process channel 0
|
|
||||||
if (div_n_cnt0 > 1)
|
|
||||||
{
|
|
||||||
div_n_cnt0--;
|
|
||||||
}
|
|
||||||
else if (div_n_cnt0 == 1)
|
|
||||||
{
|
|
||||||
int prev_bit5 = Bit5[p5_0];
|
|
||||||
div_n_cnt0 = myDivNMax[0];
|
|
||||||
|
|
||||||
// The P5 counter has multiple uses, so we increment it here
|
|
||||||
p5_0++;
|
|
||||||
if (p5_0 == POLY5_SIZE)
|
|
||||||
p5_0 = 0;
|
|
||||||
|
|
||||||
// Check clock modifier for clock tick
|
|
||||||
if ((audc0 & 0x02) == 0 ||
|
|
||||||
((audc0 & 0x01) == 0 && Div31[p5_0]) ||
|
|
||||||
((audc0 & 0x01) == 1 && Bit5[p5_0]) ||
|
|
||||||
((audc0 & 0x0f) == POLY5_DIV3 && Bit5[p5_0] != prev_bit5))
|
|
||||||
{
|
|
||||||
if (audc0 & 0x04) // Pure modified clock selected
|
|
||||||
{
|
|
||||||
if ((audc0 & 0x0f) == POLY5_DIV3) // POLY5 -> DIV3 mode
|
|
||||||
{
|
|
||||||
if ( Bit5[p5_0] != prev_bit5 )
|
|
||||||
{
|
|
||||||
myDiv3Cnt[0]--;
|
|
||||||
if ( !myDiv3Cnt[0] )
|
|
||||||
{
|
|
||||||
myDiv3Cnt[0] = 3;
|
|
||||||
v0 = v0 ? 0 : audv0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If the output was set turn it off, else turn it on
|
|
||||||
v0 = v0 ? 0 : audv0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (audc0 & 0x08) // Check for p5/p9
|
|
||||||
{
|
|
||||||
if (audc0 == POLY9) // Check for poly9
|
|
||||||
{
|
|
||||||
// Increase the poly9 counter
|
|
||||||
myP9[0]++;
|
|
||||||
if (myP9[0] == POLY9_SIZE)
|
|
||||||
myP9[0] = 0;
|
|
||||||
|
|
||||||
v0 = Bit9[myP9[0]] ? audv0 : 0;
|
|
||||||
}
|
|
||||||
else if ( audc0 & 0x02 )
|
|
||||||
{
|
|
||||||
v0 = (v0 || audc0 & 0x01) ? 0 : audv0;
|
|
||||||
}
|
|
||||||
else // Must be poly5
|
|
||||||
{
|
|
||||||
v0 = Bit5[p5_0] ? audv0 : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Poly4 is the only remaining option
|
|
||||||
{
|
|
||||||
// Increase the poly4 counter
|
|
||||||
myP4[0]++;
|
|
||||||
if (myP4[0] == POLY4_SIZE)
|
|
||||||
myP4[0] = 0;
|
|
||||||
|
|
||||||
v0 = Bit4[myP4[0]] ? audv0 : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process channel 1
|
|
||||||
if (div_n_cnt1 > 1)
|
|
||||||
{
|
|
||||||
div_n_cnt1--;
|
|
||||||
}
|
|
||||||
else if (div_n_cnt1 == 1)
|
|
||||||
{
|
|
||||||
int prev_bit5 = Bit5[p5_1];
|
|
||||||
|
|
||||||
div_n_cnt1 = myDivNMax[1];
|
|
||||||
|
|
||||||
// The P5 counter has multiple uses, so we increment it here
|
|
||||||
p5_1++;
|
|
||||||
if (p5_1 == POLY5_SIZE)
|
|
||||||
p5_1 = 0;
|
|
||||||
|
|
||||||
// Check clock modifier for clock tick
|
|
||||||
if ((audc1 & 0x02) == 0 ||
|
|
||||||
((audc1 & 0x01) == 0 && Div31[p5_1]) ||
|
|
||||||
((audc1 & 0x01) == 1 && Bit5[p5_1]) ||
|
|
||||||
((audc1 & 0x0f) == POLY5_DIV3 && Bit5[p5_1] != prev_bit5))
|
|
||||||
{
|
|
||||||
if (audc1 & 0x04) // Pure modified clock selected
|
|
||||||
{
|
|
||||||
if ((audc1 & 0x0f) == POLY5_DIV3) // POLY5 -> DIV3 mode
|
|
||||||
{
|
|
||||||
if ( Bit5[p5_1] != prev_bit5 )
|
|
||||||
{
|
|
||||||
myDiv3Cnt[1]--;
|
|
||||||
if ( ! myDiv3Cnt[1] )
|
|
||||||
{
|
|
||||||
myDiv3Cnt[1] = 3;
|
|
||||||
v1 = v1 ? 0 : audv1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If the output was set turn it off, else turn it on
|
|
||||||
v1 = v1 ? 0 : audv1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (audc1 & 0x08) // Check for p5/p9
|
|
||||||
{
|
|
||||||
if (audc1 == POLY9) // Check for poly9
|
|
||||||
{
|
|
||||||
// Increase the poly9 counter
|
|
||||||
myP9[1]++;
|
|
||||||
if (myP9[1] == POLY9_SIZE)
|
|
||||||
myP9[1] = 0;
|
|
||||||
|
|
||||||
v1 = Bit9[myP9[1]] ? audv1 : 0;
|
|
||||||
}
|
|
||||||
else if ( audc1 & 0x02 )
|
|
||||||
{
|
|
||||||
v1 = (v1 || audc1 & 0x01) ? 0 : audv1;
|
|
||||||
}
|
|
||||||
else // Must be poly5
|
|
||||||
{
|
|
||||||
v1 = Bit5[p5_1] ? audv1 : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Poly4 is the only remaining option
|
|
||||||
{
|
|
||||||
// Increase the poly4 counter
|
|
||||||
myP4[1]++;
|
|
||||||
if (myP4[1] == POLY4_SIZE)
|
|
||||||
myP4[1] = 0;
|
|
||||||
|
|
||||||
v1 = Bit4[myP4[1]] ? audv1 : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
myOutputCounter += myOutputFrequency;
|
|
||||||
|
|
||||||
switch(myChannelMode)
|
|
||||||
{
|
|
||||||
case Hardware2Mono: // mono sampling with 2 hardware channels
|
|
||||||
while((samples > 0) && (myOutputCounter >= 31400))
|
|
||||||
{
|
|
||||||
Int16 byte = v0 + v1;
|
|
||||||
*(buffer++) = byte;
|
|
||||||
*(buffer++) = byte;
|
|
||||||
myOutputCounter -= 31400;
|
|
||||||
samples--;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Hardware2Stereo: // stereo sampling with 2 hardware channels
|
|
||||||
while((samples > 0) && (myOutputCounter >= 31400))
|
|
||||||
{
|
|
||||||
*(buffer++) = v0;
|
|
||||||
*(buffer++) = v1;
|
|
||||||
myOutputCounter -= 31400;
|
|
||||||
samples--;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Hardware1: // mono/stereo sampling with only 1 hardware channel
|
|
||||||
while((samples > 0) && (myOutputCounter >= 31400))
|
|
||||||
{
|
|
||||||
*(buffer++) = v0 + v1;
|
|
||||||
myOutputCounter -= 31400;
|
|
||||||
samples--;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save for next round
|
|
||||||
myP5[0] = p5_0;
|
|
||||||
myP5[1] = p5_1;
|
|
||||||
myVolume[0] = v0;
|
|
||||||
myVolume[1] = v1;
|
|
||||||
myDivNCnt[0] = div_n_cnt0;
|
|
||||||
myDivNCnt[1] = div_n_cnt1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void TIASound::polyInit(uInt8* poly, int size, int f0, int f1)
|
|
||||||
{
|
|
||||||
int mask = (1 << size) - 1, x = mask;
|
|
||||||
|
|
||||||
for(int i = 0; i < mask; i++)
|
|
||||||
{
|
|
||||||
int bit0 = ( ( size - f0 ) ? ( x >> ( size - f0 ) ) : x ) & 0x01;
|
|
||||||
int bit1 = ( ( size - f1 ) ? ( x >> ( size - f1 ) ) : x ) & 0x01;
|
|
||||||
poly[i] = x & 1;
|
|
||||||
// calculate next bit
|
|
||||||
x = ( x >> 1 ) | ( ( bit0 ^ bit1 ) << ( size - 1) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
const uInt8 TIASound::Div31[POLY5_SIZE] = {
|
|
||||||
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
||||||
};
|
|
|
@ -1,183 +0,0 @@
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// SSSS tt lll lll
|
|
||||||
// SS SS tt ll ll
|
|
||||||
// SS tttttt eeee ll ll aaaa
|
|
||||||
// SSSS tt ee ee ll ll aa
|
|
||||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
||||||
// SS SS tt ee ll ll aa aa
|
|
||||||
// SSSS ttt eeeee llll llll aaaaa
|
|
||||||
//
|
|
||||||
// Copyright (c) 1995-2017 by Bradford W. Mott, Stephen Anthony
|
|
||||||
// and the Stella Team
|
|
||||||
//
|
|
||||||
// See the file "License.txt" for information on usage and redistribution of
|
|
||||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
#ifndef TIASOUND_HXX
|
|
||||||
#define TIASOUND_HXX
|
|
||||||
|
|
||||||
#include "bspf.hxx"
|
|
||||||
|
|
||||||
/**
|
|
||||||
This class implements a fairly accurate emulation of the TIA sound
|
|
||||||
hardware. This class uses code/ideas from z26 and MESS.
|
|
||||||
|
|
||||||
Currently, the sound generation routines work at 31400Hz only.
|
|
||||||
Resampling can be done by passing in a different output frequency.
|
|
||||||
|
|
||||||
@author Bradford W. Mott, Stephen Anthony, z26 and MESS teams
|
|
||||||
*/
|
|
||||||
class TIASound
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
Create a new TIA Sound object using the specified output frequency
|
|
||||||
*/
|
|
||||||
TIASound(Int32 outputFrequency = 31400);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
Reset the sound emulation to its power-on state
|
|
||||||
*/
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
Set the frequency output samples should be generated at
|
|
||||||
*/
|
|
||||||
void outputFrequency(Int32 freq);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Selects the number of audio channels per sample. There are two factors
|
|
||||||
to consider: hardware capability and desired mixing.
|
|
||||||
|
|
||||||
@param hardware The number of channels supported by the sound system
|
|
||||||
@param stereo Whether to output the internal sound signals into 1
|
|
||||||
or 2 channels
|
|
||||||
|
|
||||||
@return Status of the channel configuration used
|
|
||||||
*/
|
|
||||||
string channels(uInt32 hardware, bool stereo);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
Sets the specified sound register to the given value
|
|
||||||
|
|
||||||
@param address Register address
|
|
||||||
@param value Value to store in the register
|
|
||||||
*/
|
|
||||||
void set(uInt16 address, uInt8 value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Gets the specified sound register's value
|
|
||||||
|
|
||||||
@param address Register address
|
|
||||||
*/
|
|
||||||
uInt8 get(uInt16 address) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Create sound samples based on the current sound register settings
|
|
||||||
in the specified buffer. NOTE: If channels is set to stereo then
|
|
||||||
the buffer will need to be twice as long as the number of samples.
|
|
||||||
|
|
||||||
@param buffer The location to store generated samples
|
|
||||||
@param samples The number of samples to generate
|
|
||||||
*/
|
|
||||||
void process(Int16* buffer, uInt32 samples);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Set the volume of the samples created (0-100)
|
|
||||||
*/
|
|
||||||
void volume(uInt32 percent);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void polyInit(uInt8* poly, int size, int f0, int f1);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Definitions for AUDCx (15, 16)
|
|
||||||
enum AUDCxRegister
|
|
||||||
{
|
|
||||||
SET_TO_1 = 0x00, // 0000
|
|
||||||
POLY4 = 0x01, // 0001
|
|
||||||
DIV31_POLY4 = 0x02, // 0010
|
|
||||||
POLY5_POLY4 = 0x03, // 0011
|
|
||||||
PURE1 = 0x04, // 0100
|
|
||||||
PURE2 = 0x05, // 0101
|
|
||||||
DIV31_PURE = 0x06, // 0110
|
|
||||||
POLY5_2 = 0x07, // 0111
|
|
||||||
POLY9 = 0x08, // 1000
|
|
||||||
POLY5 = 0x09, // 1001
|
|
||||||
DIV31_POLY5 = 0x0a, // 1010
|
|
||||||
POLY5_POLY5 = 0x0b, // 1011
|
|
||||||
DIV3_PURE = 0x0c, // 1100
|
|
||||||
DIV3_PURE2 = 0x0d, // 1101
|
|
||||||
DIV93_PURE = 0x0e, // 1110
|
|
||||||
POLY5_DIV3 = 0x0f // 1111
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
POLY4_SIZE = 0x000f,
|
|
||||||
POLY5_SIZE = 0x001f,
|
|
||||||
POLY9_SIZE = 0x01ff,
|
|
||||||
DIV3_MASK = 0x0c,
|
|
||||||
AUDV_SHIFT = 10 // shift 2 positions for AUDV,
|
|
||||||
// then another 8 for 16-bit sound
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ChannelMode {
|
|
||||||
Hardware2Mono, // mono sampling with 2 hardware channels
|
|
||||||
Hardware2Stereo, // stereo sampling with 2 hardware channels
|
|
||||||
Hardware1 // mono/stereo sampling with only 1 hardware channel
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Structures to hold the 6 tia sound control bytes
|
|
||||||
uInt8 myAUDC[2]; // AUDCx (15, 16)
|
|
||||||
uInt8 myAUDF[2]; // AUDFx (17, 18)
|
|
||||||
Int16 myAUDV[2]; // AUDVx (19, 1A)
|
|
||||||
|
|
||||||
Int16 myVolume[2]; // Last output volume for each channel
|
|
||||||
|
|
||||||
uInt8 myP4[2]; // Position pointer for the 4-bit POLY array
|
|
||||||
uInt8 myP5[2]; // Position pointer for the 5-bit POLY array
|
|
||||||
uInt16 myP9[2]; // Position pointer for the 9-bit POLY array
|
|
||||||
|
|
||||||
uInt8 myDivNCnt[2]; // Divide by n counter. one for each channel
|
|
||||||
uInt8 myDivNMax[2]; // Divide by n maximum, one for each channel
|
|
||||||
uInt8 myDiv3Cnt[2]; // Div 3 counter, used for POLY5_DIV3 mode
|
|
||||||
|
|
||||||
ChannelMode myChannelMode;
|
|
||||||
Int32 myOutputFrequency;
|
|
||||||
Int32 myOutputCounter;
|
|
||||||
uInt32 myVolumePercentage;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Initialize the bit patterns for the polynomials (at runtime).
|
|
||||||
|
|
||||||
The 4bit and 5bit patterns are the identical ones used in the tia chip.
|
|
||||||
Though the patterns could be packed with 8 bits per byte, using only a
|
|
||||||
single bit per byte keeps the math simple, which is important for
|
|
||||||
efficient processing.
|
|
||||||
*/
|
|
||||||
uInt8 Bit4[POLY4_SIZE];
|
|
||||||
uInt8 Bit5[POLY5_SIZE];
|
|
||||||
uInt8 Bit9[POLY9_SIZE];
|
|
||||||
|
|
||||||
/*
|
|
||||||
The 'Div by 31' counter is treated as another polynomial because of
|
|
||||||
the way it operates. It does not have a 50% duty cycle, but instead
|
|
||||||
has a 13:18 ratio (of course, 13+18 = 31). This could also be
|
|
||||||
implemented by using counters.
|
|
||||||
*/
|
|
||||||
static const uInt8 Div31[POLY5_SIZE];
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Following constructors and assignment operators not supported
|
|
||||||
TIASound(const TIASound&) = delete;
|
|
||||||
TIASound(TIASound&&) = delete;
|
|
||||||
TIASound& operator=(const TIASound&) = delete;
|
|
||||||
TIASound& operator=(TIASound&&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -76,7 +76,6 @@ MODULE_OBJS := \
|
||||||
src/emucore/Settings.o \
|
src/emucore/Settings.o \
|
||||||
src/emucore/Switches.o \
|
src/emucore/Switches.o \
|
||||||
src/emucore/System.o \
|
src/emucore/System.o \
|
||||||
src/emucore/TIASnd.o \
|
|
||||||
src/emucore/TIASurface.o \
|
src/emucore/TIASurface.o \
|
||||||
src/emucore/Thumbulator.o
|
src/emucore/Thumbulator.o
|
||||||
|
|
||||||
|
|
|
@ -65,9 +65,8 @@ enum ResxCounter: uInt8 {
|
||||||
static constexpr uInt8 resxLateHblankThreshold = 73;
|
static constexpr uInt8 resxLateHblankThreshold = 73;
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
TIA::TIA(Console& console, Sound& sound, Settings& settings)
|
TIA::TIA(Console& console, Settings& settings)
|
||||||
: myConsole(console),
|
: myConsole(console),
|
||||||
mySound(sound),
|
|
||||||
mySettings(settings),
|
mySettings(settings),
|
||||||
myFrameManager(nullptr),
|
myFrameManager(nullptr),
|
||||||
myPlayfield(~CollisionMask::playfield & 0x7FFF),
|
myPlayfield(~CollisionMask::playfield & 0x7FFF),
|
||||||
|
@ -163,7 +162,6 @@ void TIA::reset()
|
||||||
for (PaddleReader& paddleReader : myPaddleReaders)
|
for (PaddleReader& paddleReader : myPaddleReaders)
|
||||||
paddleReader.reset(myTimestamp);
|
paddleReader.reset(myTimestamp);
|
||||||
|
|
||||||
mySound.reset();
|
|
||||||
myDelayQueue.reset();
|
myDelayQueue.reset();
|
||||||
|
|
||||||
if (myFrameManager) myFrameManager->reset();
|
if (myFrameManager) myFrameManager->reset();
|
||||||
|
@ -225,8 +223,6 @@ bool TIA::save(Serializer& out) const
|
||||||
{
|
{
|
||||||
out.putString(name());
|
out.putString(name());
|
||||||
|
|
||||||
if(!mySound.save(out)) return false;
|
|
||||||
|
|
||||||
if(!myDelayQueue.save(out)) return false;
|
if(!myDelayQueue.save(out)) return false;
|
||||||
if(!myFrameManager->save(out)) return false;
|
if(!myFrameManager->save(out)) return false;
|
||||||
|
|
||||||
|
@ -296,8 +292,6 @@ bool TIA::load(Serializer& in)
|
||||||
if(in.getString() != name())
|
if(in.getString() != name())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!mySound.load(in)) return false;
|
|
||||||
|
|
||||||
if(!myDelayQueue.load(in)) return false;
|
if(!myDelayQueue.load(in)) return false;
|
||||||
if(!myFrameManager->load(in)) return false;
|
if(!myFrameManager->load(in)) return false;
|
||||||
|
|
||||||
|
@ -519,33 +513,24 @@ bool TIA::poke(uInt16 address, uInt8 value)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
// FIXME - rework this when we add the new sound core
|
|
||||||
case AUDV0:
|
case AUDV0:
|
||||||
mySound.set(address, value, mySystem->cycles());
|
|
||||||
myShadowRegisters[address] = value;
|
myShadowRegisters[address] = value;
|
||||||
break;
|
break;
|
||||||
case AUDV1:
|
case AUDV1:
|
||||||
mySound.set(address, value, mySystem->cycles());
|
|
||||||
myShadowRegisters[address] = value;
|
myShadowRegisters[address] = value;
|
||||||
break;
|
break;
|
||||||
case AUDF0:
|
case AUDF0:
|
||||||
mySound.set(address, value, mySystem->cycles());
|
|
||||||
myShadowRegisters[address] = value;
|
myShadowRegisters[address] = value;
|
||||||
break;
|
break;
|
||||||
case AUDF1:
|
case AUDF1:
|
||||||
mySound.set(address, value, mySystem->cycles());
|
|
||||||
myShadowRegisters[address] = value;
|
myShadowRegisters[address] = value;
|
||||||
break;
|
break;
|
||||||
case AUDC0:
|
case AUDC0:
|
||||||
mySound.set(address, value, mySystem->cycles());
|
|
||||||
myShadowRegisters[address] = value;
|
myShadowRegisters[address] = value;
|
||||||
break;
|
break;
|
||||||
case AUDC1:
|
case AUDC1:
|
||||||
mySound.set(address, value, mySystem->cycles());
|
|
||||||
myShadowRegisters[address] = value;
|
myShadowRegisters[address] = value;
|
||||||
break;
|
break;
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
case HMOVE:
|
case HMOVE:
|
||||||
myDelayQueue.push(HMOVE, value, Delay::hmove);
|
myDelayQueue.push(HMOVE, value, Delay::hmove);
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "Console.hxx"
|
#include "Console.hxx"
|
||||||
#include "Sound.hxx"
|
|
||||||
#include "Settings.hxx"
|
#include "Settings.hxx"
|
||||||
#include "Device.hxx"
|
#include "Device.hxx"
|
||||||
#include "Serializer.hxx"
|
#include "Serializer.hxx"
|
||||||
|
@ -97,10 +96,9 @@ class TIA : public Device
|
||||||
Create a new TIA for the specified console
|
Create a new TIA for the specified console
|
||||||
|
|
||||||
@param console The console the TIA is associated with
|
@param console The console the TIA is associated with
|
||||||
@param sound The sound object the TIA is associated with
|
|
||||||
@param settings The settings object for this TIA device
|
@param settings The settings object for this TIA device
|
||||||
*/
|
*/
|
||||||
TIA(Console& console, Sound& sound, Settings& settings);
|
TIA(Console& console, Settings& settings);
|
||||||
|
|
||||||
virtual ~TIA() = default;
|
virtual ~TIA() = default;
|
||||||
|
|
||||||
|
@ -585,7 +583,6 @@ class TIA : public Device
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Console& myConsole;
|
Console& myConsole;
|
||||||
Sound& mySound;
|
|
||||||
Settings& mySettings;
|
Settings& mySettings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue