mirror of https://github.com/stella-emu/stella.git
A fairly major rewrite of the class to improve the sound support. The
enhanced sound in games like Pitfall II and Quadrun should be working now. Also the overall sound in most has should be improved since the TIA sound register writes and sample generation are somewhat synchronized. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@262 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
parent
3d231acd53
commit
2e09e2c00c
|
@ -13,26 +13,24 @@
|
||||||
// See the file "license" for information on usage and redistribution of
|
// See the file "license" for information on usage and redistribution of
|
||||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
//
|
//
|
||||||
// $Id: SoundSDL.cxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
|
// $Id: SoundSDL.cxx,v 1.2 2004-06-13 05:03:26 bwmott Exp $
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
#include "TIASound.h"
|
#include "TIASound.h"
|
||||||
|
#include "Console.hxx"
|
||||||
#include "Serializer.hxx"
|
#include "Serializer.hxx"
|
||||||
#include "Deserializer.hxx"
|
#include "Deserializer.hxx"
|
||||||
#include "System.hxx"
|
#include "System.hxx"
|
||||||
|
|
||||||
#include "SoundSDL.hxx"
|
#include "SoundSDL.hxx"
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
SoundSDL::SoundSDL(uInt32 fragsize, uInt32 queuesize)
|
SoundSDL::SoundSDL(uInt32 fragsize)
|
||||||
: myCurrentVolume(SDL_MIX_MAXVOLUME),
|
: myIsInitializedFlag(false),
|
||||||
myFragmentSize(fragsize),
|
|
||||||
myIsInitializedFlag(false),
|
|
||||||
myIsMuted(false),
|
myIsMuted(false),
|
||||||
mySampleRate(31400),
|
myVolume(100)
|
||||||
mySampleQueue(queuesize)
|
|
||||||
{
|
{
|
||||||
if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
|
if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
|
||||||
{
|
{
|
||||||
|
@ -43,10 +41,10 @@ SoundSDL::SoundSDL(uInt32 fragsize, uInt32 queuesize)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SDL_AudioSpec desired;
|
SDL_AudioSpec desired;
|
||||||
desired.freq = mySampleRate;
|
desired.freq = 31400;
|
||||||
desired.format = AUDIO_U8;
|
desired.format = AUDIO_U8;
|
||||||
desired.channels = 1;
|
desired.channels = 1;
|
||||||
desired.samples = myFragmentSize;
|
desired.samples = fragsize;
|
||||||
desired.callback = callback;
|
desired.callback = callback;
|
||||||
desired.userdata = (void*)this;
|
desired.userdata = (void*)this;
|
||||||
|
|
||||||
|
@ -71,18 +69,18 @@ SoundSDL::SoundSDL(uInt32 fragsize, uInt32 queuesize)
|
||||||
|
|
||||||
myIsInitializedFlag = true;
|
myIsInitializedFlag = true;
|
||||||
myIsMuted = false;
|
myIsMuted = false;
|
||||||
mySampleRate = myHardwareSpec.freq;
|
|
||||||
myFragmentSize = myHardwareSpec.samples;
|
|
||||||
|
|
||||||
|
/*
|
||||||
cerr << "Freq: " << (int)myHardwareSpec.freq << endl;
|
cerr << "Freq: " << (int)myHardwareSpec.freq << endl;
|
||||||
cerr << "Format: " << (int)myHardwareSpec.format << endl;
|
cerr << "Format: " << (int)myHardwareSpec.format << endl;
|
||||||
cerr << "Channels: " << (int)myHardwareSpec.channels << endl;
|
cerr << "Channels: " << (int)myHardwareSpec.channels << endl;
|
||||||
cerr << "Silence: " << (int)myHardwareSpec.silence << endl;
|
cerr << "Silence: " << (int)myHardwareSpec.silence << endl;
|
||||||
cerr << "Samples: " << (int)myHardwareSpec.samples << endl;
|
cerr << "Samples: " << (int)myHardwareSpec.samples << endl;
|
||||||
cerr << "Size: " << (int)myHardwareSpec.size << endl;
|
cerr << "Size: " << (int)myHardwareSpec.size << endl;
|
||||||
|
*/
|
||||||
|
|
||||||
// Now initialize the TIASound object which will actually generate sound
|
// Now initialize the TIASound object which will actually generate sound
|
||||||
Tia_sound_init(31400, mySampleRate);
|
Tia_sound_init(31400, myHardwareSpec.freq);
|
||||||
|
|
||||||
// And start the SDL sound subsystem ...
|
// And start the SDL sound subsystem ...
|
||||||
SDL_PauseAudio(0);
|
SDL_PauseAudio(0);
|
||||||
|
@ -92,8 +90,10 @@ SoundSDL::SoundSDL(uInt32 fragsize, uInt32 queuesize)
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
SoundSDL::~SoundSDL()
|
SoundSDL::~SoundSDL()
|
||||||
{
|
{
|
||||||
|
// Close the SDL audio system if it's initialized
|
||||||
if(myIsInitializedFlag)
|
if(myIsInitializedFlag)
|
||||||
{
|
{
|
||||||
|
SDL_PauseAudio(1);
|
||||||
SDL_CloseAudio();
|
SDL_CloseAudio();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,37 +120,186 @@ void SoundSDL::mute(bool state)
|
||||||
myIsMuted = state;
|
myIsMuted = state;
|
||||||
|
|
||||||
SDL_PauseAudio(myIsMuted ? 1 : 0);
|
SDL_PauseAudio(myIsMuted ? 1 : 0);
|
||||||
|
myRegWriteQueue.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundSDL::reset()
|
||||||
|
{
|
||||||
|
if(myIsInitializedFlag)
|
||||||
|
{
|
||||||
|
SDL_PauseAudio(1);
|
||||||
|
myIsMuted = false;
|
||||||
|
myLastRegisterSetCycle = 0;
|
||||||
|
myRegWriteQueue.clear();
|
||||||
|
SDL_PauseAudio(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL::setVolume(Int32 percent)
|
void SoundSDL::setVolume(Int32 percent)
|
||||||
{
|
{
|
||||||
// TODO: Verify that this works...
|
|
||||||
if(myIsInitializedFlag)
|
if(myIsInitializedFlag)
|
||||||
{
|
{
|
||||||
if((percent >= 0) && (percent <= 100))
|
if((percent >= 0) && (percent <= 100))
|
||||||
{
|
{
|
||||||
SDL_LockAudio();
|
SDL_LockAudio();
|
||||||
myCurrentVolume = (uInt32)(((float)percent / 100.0) * SDL_MIX_MAXVOLUME);
|
myVolume = percent;
|
||||||
|
Tia_volume(percent);
|
||||||
SDL_UnlockAudio();
|
SDL_UnlockAudio();
|
||||||
}
|
}
|
||||||
else if(percent == -1) // If -1 has been specified, play sound at default volume
|
|
||||||
{
|
|
||||||
myCurrentVolume = SDL_MIX_MAXVOLUME;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL::update()
|
void SoundSDL::set(uInt16 addr, uInt8 value, Int32 cycle)
|
||||||
{
|
{
|
||||||
|
SDL_LockAudio();
|
||||||
|
|
||||||
|
// First, calulate 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
|
||||||
|
delta = delta * (60.0 / (double)myConsole->frameRate());
|
||||||
|
|
||||||
|
RegWrite info;
|
||||||
|
info.addr = addr;
|
||||||
|
info.value = value;
|
||||||
|
info.delta = delta;
|
||||||
|
myRegWriteQueue.enqueue(info);
|
||||||
|
|
||||||
|
// Update last cycle counter to the current cycle
|
||||||
|
myLastRegisterSetCycle = cycle;
|
||||||
|
|
||||||
|
SDL_UnlockAudio();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL::set(uInt16 addr, uInt8 value)
|
void SoundSDL::processFragment(uInt8* stream, Int32 length)
|
||||||
{
|
{
|
||||||
Update_tia_sound(addr, value);
|
if(!myIsInitializedFlag)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are excessive items on the queue then we'll remove some
|
||||||
|
if(myRegWriteQueue.duration() > (10.0 / 60.0))
|
||||||
|
{
|
||||||
|
double removed = 0.0;
|
||||||
|
while(removed < (8.0 / 60.0))
|
||||||
|
{
|
||||||
|
RegWrite& info = myRegWriteQueue.front();
|
||||||
|
removed += info.delta;
|
||||||
|
Update_tia_sound(info.addr, info.value);
|
||||||
|
myRegWriteQueue.dequeue();
|
||||||
|
}
|
||||||
|
// cout << "Removed Items from RegWriteQueue!" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
Tia_process(stream + (uInt32)position, 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 remaing samples in the fragment take to play
|
||||||
|
double duration = remaining / (double)myHardwareSpec.freq;
|
||||||
|
|
||||||
|
// Does the register update occur before the end of the fragment?
|
||||||
|
if(info.delta <= duration)
|
||||||
|
{
|
||||||
|
if(info.delta > 0.0)
|
||||||
|
{
|
||||||
|
// Process the fragment upto the next TIA register write
|
||||||
|
double samples = (myHardwareSpec.freq * info.delta);
|
||||||
|
Tia_process(stream + (uInt32)position, (uInt32)samples +
|
||||||
|
(uInt32)(position + samples) -
|
||||||
|
((uInt32)position + (uInt32)samples));
|
||||||
|
position += samples;
|
||||||
|
remaining -= samples;
|
||||||
|
}
|
||||||
|
Update_tia_sound(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
|
||||||
|
Tia_process(stream + (uInt32)position, length - (uInt32)position);
|
||||||
|
info.delta -= duration;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//cout << myRegWriteQueue.size() << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundSDL::callback(void* udata, uInt8* stream, int len)
|
||||||
|
{
|
||||||
|
SoundSDL* sound = (SoundSDL*)udata;
|
||||||
|
sound->processFragment(stream, (Int32)len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool SoundSDL::load(Deserializer& in)
|
||||||
|
{
|
||||||
|
string device = "TIASound";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(in.getString() != device)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uInt8 reg1 = 0, reg2 = 0, reg3 = 0, reg4 = 0, reg5 = 0, reg6 = 0;
|
||||||
|
reg1 = (uInt8) in.getLong();
|
||||||
|
reg2 = (uInt8) in.getLong();
|
||||||
|
reg3 = (uInt8) in.getLong();
|
||||||
|
reg4 = (uInt8) in.getLong();
|
||||||
|
reg5 = (uInt8) in.getLong();
|
||||||
|
reg6 = (uInt8) in.getLong();
|
||||||
|
|
||||||
|
myLastRegisterSetCycle = (Int32) in.getLong();
|
||||||
|
|
||||||
|
// Only update the TIA sound registers if sound is enabled
|
||||||
|
if(myIsInitializedFlag)
|
||||||
|
Tia_set_registers(reg1, reg2, reg3, reg4, reg5, reg6);
|
||||||
|
}
|
||||||
|
catch(char *msg)
|
||||||
|
{
|
||||||
|
cerr << msg << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
cerr << "Unknown error in load state for " << device << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -175,7 +324,7 @@ bool SoundSDL::save(Serializer& out)
|
||||||
out.putLong(reg5);
|
out.putLong(reg5);
|
||||||
out.putLong(reg6);
|
out.putLong(reg6);
|
||||||
|
|
||||||
out.putLong(myLastSoundUpdateCycle);
|
out.putLong(myLastRegisterSetCycle);
|
||||||
}
|
}
|
||||||
catch(char *msg)
|
catch(char *msg)
|
||||||
{
|
{
|
||||||
|
@ -192,143 +341,88 @@ bool SoundSDL::save(Serializer& out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool SoundSDL::load(Deserializer& in)
|
SoundSDL::RegWriteQueue::RegWriteQueue(uInt32 capacity)
|
||||||
{
|
|
||||||
string device = "TIASound";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if(in.getString() != device)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uInt8 reg1 = 0, reg2 = 0, reg3 = 0, reg4 = 0, reg5 = 0, reg6 = 0;
|
|
||||||
reg1 = (uInt8) in.getLong();
|
|
||||||
reg2 = (uInt8) in.getLong();
|
|
||||||
reg3 = (uInt8) in.getLong();
|
|
||||||
reg4 = (uInt8) in.getLong();
|
|
||||||
reg5 = (uInt8) in.getLong();
|
|
||||||
reg6 = (uInt8) in.getLong();
|
|
||||||
|
|
||||||
myLastSoundUpdateCycle = (Int32) in.getLong();
|
|
||||||
|
|
||||||
// Only update the TIA sound registers if sound is enabled
|
|
||||||
if(myIsInitializedFlag)
|
|
||||||
Tia_set_registers(reg1, reg2, reg3, reg4, reg5, reg6);
|
|
||||||
}
|
|
||||||
catch(char *msg)
|
|
||||||
{
|
|
||||||
cerr << msg << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch(...)
|
|
||||||
{
|
|
||||||
cerr << "Unknown error in load state for " << device << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
void SoundSDL::callback(void* udata, uInt8* stream, int len)
|
|
||||||
{
|
|
||||||
SoundSDL* sound = (SoundSDL*)udata;
|
|
||||||
|
|
||||||
if(sound->isSuccessfullyInitialized())
|
|
||||||
{
|
|
||||||
Tia_process(stream, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
SoundSDL::SampleQueue::SampleQueue(uInt32 capacity)
|
|
||||||
: myCapacity(capacity),
|
: myCapacity(capacity),
|
||||||
myBuffer(0),
|
myBuffer(0),
|
||||||
mySize(0),
|
mySize(0),
|
||||||
myHead(0),
|
myHead(0),
|
||||||
myTail(0)
|
myTail(0)
|
||||||
{
|
{
|
||||||
myBuffer = new uInt8[myCapacity];
|
myBuffer = new RegWrite[myCapacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
SoundSDL::SampleQueue::~SampleQueue()
|
SoundSDL::RegWriteQueue::~RegWriteQueue()
|
||||||
{
|
{
|
||||||
delete[] myBuffer;
|
delete[] myBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL::SampleQueue::clear()
|
void SoundSDL::RegWriteQueue::clear()
|
||||||
{
|
{
|
||||||
myHead = myTail = mySize = 0;
|
myHead = myTail = mySize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 SoundSDL::SampleQueue::dequeue(uInt8* buffer, uInt32 size)
|
void SoundSDL::RegWriteQueue::dequeue()
|
||||||
{
|
{
|
||||||
// We can only dequeue up to the number of items in the queue
|
if(mySize > 0)
|
||||||
if(size > mySize)
|
|
||||||
{
|
{
|
||||||
size = mySize;
|
myHead = (myHead + 1) % myCapacity;
|
||||||
|
--mySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((myHead + size) < myCapacity)
|
|
||||||
{
|
|
||||||
memcpy((void*)buffer, (const void*)(myBuffer + myHead), size);
|
|
||||||
myHead += size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uInt32 s1 = myCapacity - myHead;
|
|
||||||
uInt32 s2 = size - s1;
|
|
||||||
memcpy((void*)buffer, (const void*)(myBuffer + myHead), s1);
|
|
||||||
memcpy((void*)(buffer + s1), (const void*)myBuffer, s2);
|
|
||||||
myHead = (myHead + size) % myCapacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
mySize -= size;
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundSDL::SampleQueue::enqueue(uInt8* buffer, uInt32 size)
|
double SoundSDL::RegWriteQueue::duration()
|
||||||
|
{
|
||||||
|
double duration = 0.0;
|
||||||
|
for(uInt32 i = 0; i < mySize; ++i)
|
||||||
|
{
|
||||||
|
duration += myBuffer[(myHead + i) % myCapacity].delta;
|
||||||
|
}
|
||||||
|
return duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundSDL::RegWriteQueue::enqueue(const RegWrite& info)
|
||||||
{
|
{
|
||||||
// If an attempt is made to enqueue more than the queue can hold then
|
// If an attempt is made to enqueue more than the queue can hold then
|
||||||
// we'll only enqueue the last myCapacity elements.
|
// we'll enlarge the queue's capacity.
|
||||||
if(size > myCapacity)
|
if(mySize == myCapacity)
|
||||||
{
|
{
|
||||||
buffer += (size - myCapacity);
|
grow();
|
||||||
size = myCapacity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if((myTail + size) < myCapacity)
|
myBuffer[myTail] = info;
|
||||||
{
|
myTail = (myTail + 1) % myCapacity;
|
||||||
memcpy((void*)(myBuffer + myTail), (const void*)buffer, size);
|
++mySize;
|
||||||
myTail += size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uInt32 s1 = myCapacity - myTail;
|
|
||||||
uInt32 s2 = size - s1;
|
|
||||||
memcpy((void*)(myBuffer + myTail), (const void*)buffer, s1);
|
|
||||||
memcpy((void*)myBuffer, (const void*)(buffer + s1), s2);
|
|
||||||
myTail = (myTail + size) % myCapacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((mySize + size) > myCapacity)
|
|
||||||
{
|
|
||||||
myHead = (myHead + ((mySize + size) - myCapacity)) % myCapacity;
|
|
||||||
mySize = myCapacity;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mySize += size;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
uInt32 SoundSDL::SampleQueue::size() const
|
SoundSDL::RegWrite& SoundSDL::RegWriteQueue::front()
|
||||||
|
{
|
||||||
|
assert(mySize != 0);
|
||||||
|
return myBuffer[myHead];
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
uInt32 SoundSDL::RegWriteQueue::size() const
|
||||||
{
|
{
|
||||||
return mySize;
|
return mySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundSDL::RegWriteQueue::grow()
|
||||||
|
{
|
||||||
|
RegWrite* buffer = new RegWrite[myCapacity * 2];
|
||||||
|
for(uInt32 i = 0; i < mySize; ++i)
|
||||||
|
{
|
||||||
|
buffer[i] = myBuffer[(myHead + i) % myCapacity];
|
||||||
|
}
|
||||||
|
myHead = 0;
|
||||||
|
myTail = mySize;
|
||||||
|
myCapacity = myCapacity * 2;
|
||||||
|
delete[] myBuffer;
|
||||||
|
myBuffer = buffer;
|
||||||
|
}
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
// SS SS tt ee ll ll aa aa
|
// SS SS tt ee ll ll aa aa
|
||||||
// SSSS ttt eeeee llll llll aaaaa
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
//
|
//
|
||||||
// Copyright (c) 1995-2002 by Bradford W. Mott
|
// Copyright (c) 1995-2004 by Bradford W. Mott
|
||||||
//
|
//
|
||||||
// See the file "license" for information on usage and redistribution of
|
// See the file "license" for information on usage and redistribution of
|
||||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
//
|
//
|
||||||
// $Id: SoundSDL.hxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
|
// $Id: SoundSDL.hxx,v 1.2 2004-06-13 05:03:26 bwmott Exp $
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
#ifndef SOUNDSDL_HXX
|
#ifndef SOUNDSDL_HXX
|
||||||
|
@ -29,15 +29,16 @@
|
||||||
This class implements the sound API for SDL.
|
This class implements the sound API for SDL.
|
||||||
|
|
||||||
@author Stephen Anthony and Bradford W. Mott
|
@author Stephen Anthony and Bradford W. Mott
|
||||||
@version $Id: SoundSDL.hxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
|
@version $Id: SoundSDL.hxx,v 1.2 2004-06-13 05:03:26 bwmott Exp $
|
||||||
*/
|
*/
|
||||||
class SoundSDL : public Sound
|
class SoundSDL : public Sound
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
Create a new sound object
|
Create a new sound object. The init method must be invoked before
|
||||||
|
using the object.
|
||||||
*/
|
*/
|
||||||
SoundSDL(uInt32 fragsize, uInt32 queuesize);
|
SoundSDL(uInt32 fragsize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Destructor
|
Destructor
|
||||||
|
@ -45,53 +46,51 @@ class SoundSDL : public Sound
|
||||||
virtual ~SoundSDL();
|
virtual ~SoundSDL();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
Closes the sound device
|
|
||||||
*/
|
|
||||||
void closeDevice();
|
|
||||||
|
|
||||||
/**
|
|
||||||
Return the playback sample rate for the sound device.
|
|
||||||
|
|
||||||
@return The playback sample rate
|
|
||||||
*/
|
|
||||||
uInt32 getSampleRate() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return true iff the sound device was successfully initialized.
|
Return true iff the sound device was successfully initialized.
|
||||||
|
|
||||||
@return true iff the sound device was successfully initialized
|
@return true iff the sound device was successfully initialized
|
||||||
*/
|
*/
|
||||||
bool isSuccessfullyInitialized() const;
|
virtual bool isSuccessfullyInitialized() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Set the mute state of the sound object.
|
Set the mute state of the sound object. While muted no sound is played.
|
||||||
|
|
||||||
@param state Mutes sound if true, unmute if false
|
@param state Mutes sound if true, unmute if false
|
||||||
*/
|
*/
|
||||||
void mute(bool state);
|
virtual void mute(bool state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sets the volume of the sound device to the specified level. The
|
Resets the sound device.
|
||||||
volume is given as a percentage from 0 to 100. A -1 indicates
|
|
||||||
that the volume shouldn't be changed at all.
|
|
||||||
|
|
||||||
@param percent The new volume percentage level for the sound device
|
|
||||||
*/
|
*/
|
||||||
void setVolume(Int32 percent);
|
virtual void reset();
|
||||||
|
|
||||||
/**
|
|
||||||
Generates audio samples to fill the sample queue.
|
|
||||||
*/
|
|
||||||
void update();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sets the sound register to a given value.
|
Sets the sound register to a given value.
|
||||||
|
|
||||||
@param addr The register address
|
@param addr The register address
|
||||||
@param value The value to save into the register
|
@param value The value to save into the register
|
||||||
|
@param cycle The CPU cycle at which the register is being updated
|
||||||
*/
|
*/
|
||||||
void set(uInt16 addr, uInt8 value);
|
virtual void set(uInt16 addr, uInt8 value, Int32 cycle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the volume of the sound device to the specified level. The
|
||||||
|
volume is given as a percentage from 0 to 100. Values outside
|
||||||
|
this range indicate that the volume shouldn't be changed at all.
|
||||||
|
|
||||||
|
@param percent The new volume percentage level for the sound device
|
||||||
|
*/
|
||||||
|
virtual void setVolume(Int32 percent);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
Loads the current state of this device from the given Deserializer.
|
||||||
|
|
||||||
|
@param in The deserializer device to load from.
|
||||||
|
@return The result of the load. True on success, false on failure.
|
||||||
|
*/
|
||||||
|
bool load(Deserializer& in);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Saves the current state of this device to the given Serializer.
|
Saves the current state of this device to the given Serializer.
|
||||||
|
@ -101,83 +100,91 @@ class SoundSDL : public Sound
|
||||||
*/
|
*/
|
||||||
bool save(Serializer& out);
|
bool save(Serializer& out);
|
||||||
|
|
||||||
|
protected:
|
||||||
/**
|
/**
|
||||||
Loads the current state of this device from the given Deserializer.
|
Invoked by the sound callback to process the next sound fragment.
|
||||||
|
|
||||||
@param in The deserializer device to load from.
|
@param stream Pointer to the start of the fragment
|
||||||
@return The result of the load. True on success, false on failure.
|
@param length Length of the fragment
|
||||||
*/
|
*/
|
||||||
bool load(Deserializer& in);
|
void processFragment(uInt8* stream, Int32 length);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Struct to hold information regarding a TIA sound register write
|
||||||
|
struct RegWrite
|
||||||
|
{
|
||||||
|
uInt16 addr;
|
||||||
|
uInt8 value;
|
||||||
|
double delta;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
/**
|
||||||
A bounded queue class used to hold audio samples after they are
|
A queue class used to hold TIA sound register writes before being
|
||||||
produced by the MediaSource.
|
processed while creating a sound fragment.
|
||||||
*/
|
*/
|
||||||
class SampleQueue
|
class RegWriteQueue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
Create a new SampleQueue instance which can hold the specified
|
Create a new queue instance with the specified initial
|
||||||
number of samples. If the queue ever reaches its capacity then
|
capacity. If the queue ever reaches its capacity then it will
|
||||||
older samples are discarded.
|
automatically increase its size.
|
||||||
*/
|
*/
|
||||||
SampleQueue(uInt32 capacity);
|
RegWriteQueue(uInt32 capacity = 512);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Destroy this SampleQueue instance.
|
Destroy this queue instance.
|
||||||
*/
|
*/
|
||||||
virtual ~SampleQueue();
|
virtual ~RegWriteQueue();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
Clear any samples stored in the queue.
|
Clear any items stored in the queue.
|
||||||
*/
|
*/
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Dequeue the upto the specified number of samples and store them
|
Dequeue the first object in the queue.
|
||||||
in the buffer. Returns the actual number of samples removed from
|
|
||||||
the queue.
|
|
||||||
|
|
||||||
@return the actual number of samples removed from the queue.
|
|
||||||
*/
|
*/
|
||||||
uInt32 dequeue(uInt8* buffer, uInt32 size);
|
void dequeue();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Enqueue the specified number of samples from the buffer.
|
Return the duration of all the items in the queue.
|
||||||
*/
|
*/
|
||||||
void enqueue(uInt8* buffer, uInt32 size);
|
double duration();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Answers the number of samples currently in the queue.
|
Enqueue the specified object.
|
||||||
|
*/
|
||||||
|
void enqueue(const RegWrite& info);
|
||||||
|
|
||||||
@return The number of samples in the queue.
|
/**
|
||||||
|
Return the item at the front on the queue.
|
||||||
|
|
||||||
|
@return The item at the front of the queue.
|
||||||
|
*/
|
||||||
|
RegWrite& front();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Answers the number of items currently in the queue.
|
||||||
|
|
||||||
|
@return The number of items in the queue.
|
||||||
*/
|
*/
|
||||||
uInt32 size() const;
|
uInt32 size() const;
|
||||||
|
|
||||||
/**
|
private:
|
||||||
Answers the maximum number of samples the queue can hold.
|
// Increase the size of the queue
|
||||||
|
void grow();
|
||||||
@return The maximum number of samples in the queue.
|
|
||||||
*/
|
|
||||||
uInt32 capacity() const { return myCapacity; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const uInt32 myCapacity;
|
uInt32 myCapacity;
|
||||||
uInt8* myBuffer;
|
RegWrite* myBuffer;
|
||||||
uInt32 mySize;
|
uInt32 mySize;
|
||||||
uInt32 myHead;
|
uInt32 myHead;
|
||||||
uInt32 myTail;
|
uInt32 myTail;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Current volume
|
|
||||||
uInt32 myCurrentVolume;
|
|
||||||
|
|
||||||
// SDL fragment size
|
|
||||||
uInt32 myFragmentSize;
|
|
||||||
|
|
||||||
// Audio specification structure
|
// Audio specification structure
|
||||||
SDL_AudioSpec myHardwareSpec;
|
SDL_AudioSpec myHardwareSpec;
|
||||||
|
|
||||||
|
@ -187,14 +194,11 @@ class SoundSDL : public Sound
|
||||||
// Indicates if the sound is currently muted
|
// Indicates if the sound is currently muted
|
||||||
bool myIsMuted;
|
bool myIsMuted;
|
||||||
|
|
||||||
// DSP sample rate
|
// Current volume as a percentage (0 - 100)
|
||||||
uInt32 mySampleRate;
|
uInt32 myVolume;
|
||||||
|
|
||||||
// The sample queue size (which is auto-adapting)
|
// Queue of TIA register writes
|
||||||
uInt32 mySampleQueueSize;
|
RegWriteQueue myRegWriteQueue;
|
||||||
|
|
||||||
// Queue which holds samples from the media source before they are played
|
|
||||||
SampleQueue mySampleQueue;
|
|
||||||
|
|
||||||
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
|
||||||
|
|
Loading…
Reference in New Issue