A number of files have been updated, added, or removed to implement a

new sound mechanism for Stella. The TIASound library is now part of the
emulation core and each time a frame is created a corresponding set of
audio samples is added to a bounded queue. The GUIs are responsible for
getting the samples from the MediaSource object after each update and
sending them to a sound driver as needed. Currently, only the X11 port
has been updated to use the new API. The new APIs are not backwards
compatible so the other GUIs will not compile until they are updated.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@114 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
bwmott 2002-10-09 04:38:12 +00:00
parent 9d758a41b6
commit 36a5c1575d
18 changed files with 741 additions and 898 deletions

View File

@ -13,7 +13,7 @@
## See the file "license" for information on usage and redistribution of
## this file, and for a DISCLAIMER OF ALL WARRANTIES.
##
## $Id: makefile,v 1.21 2002-08-15 00:29:39 stephena Exp $
## $Id: makefile,v 1.22 2002-10-09 04:38:11 bwmott Exp $
##============================================================================
##============================================================================
@ -150,11 +150,11 @@ dos:
OPTIONS+="$(OPTS.DOS)" \
LDFLAGS="" \
LDLIBS="" \
OBJS="mainDOS.o PCJoys.o SndDOS.o sbdrv.o TIASound.o"
OBJS="mainDOS.o PCJoys.o SndDOS.o sbdrv.o"
unix-x:
make stella.x11 \
INCLUDES="$(INCLUDES) -I$(UI)/x11 -I$(UI)/sound" \
INCLUDES="$(INCLUDES) -I$(UI)/x11" \
SYS_INCLUDES="" \
OPTIONS="-DBSPF_UNIX=1" \
OPTIONS+="$(OPTS.X11)" \
@ -162,11 +162,11 @@ unix-x:
LDFLAGS+="$(CFLAGS.X11)" \
LDLIBS="-lX11 -lXext" \
LDLIBS+="$(LIBS.X11)" \
OBJS="mainX11.o SndUnix.o"
OBJS="mainX11.o SoundX11.o"
linux-x:
make stella.x11 \
INCLUDES="$(INCLUDES) -I$(UI)/x11 -I$(UI)/sound" \
INCLUDES="$(INCLUDES) -I$(UI)/x11" \
SYS_INCLUDES="" \
OPTIONS="-DBSPF_UNIX=1" \
OPTIONS+="$(OPTS.X11)" \
@ -174,7 +174,7 @@ linux-x:
LDFLAGS+="$(CFLAGS.X11)" \
LDLIBS="-lX11 -lXext" \
LDLIBS+="$(LIBS.X11)" \
OBJS="mainX11.o SndUnix.o" \
OBJS="mainX11.o SoundX11.o" \
OBJS+="$(OBJS.X11)"
linux-sdl:
@ -187,11 +187,12 @@ linux-sdl:
LDFLAGS+="$(CFLAGS.SDL)" \
LDLIBS="-lX11 -lXext" \
LDLIBS+="$(LIBS.SDL)" \
OBJS="mainSDL.o SndSDL.o RectList.o TIASound.o" \
OBJS="mainSDL.o SndSDL.o RectList.o" \
OBJS+="$(OBJS.SDL)"
bsdi-x:
make stella.x11 \
INCLUDES="$(INCLUDES) -I$(UI)/x11 -I$(UI)/sound" \
INCLUDES="$(INCLUDES) -I$(UI)/x11" \
SYS_INCLUDES="-I/usr/X11R6/include" \
OPTIONS="-DBSPF_UNIX=1" \
OPTIONS+="$(OPTS.X11)" \
@ -199,11 +200,11 @@ bsdi-x:
LDFLAGS+="$(CFLAGS.X11)" \
LDLIBS="-lX11 -lXext" \
LDLIBS+="$(LIBS.X11)" \
OBJS="mainX11.o SndUnix.o"
OBJS="mainX11.o SoundX11.o"
solaris-x:
make stella.x11 \
INCLUDES="$(INCLUDES) -I$(UI)/x11 -I$(UI)/sound" \
INCLUDES="$(INCLUDES) -I$(UI)/x11" \
SYS_INCLUDES="-I/usr/openwin/include" \
OPTIONS="-DBSPF_UNIX=1" \
OPTIONS+="$(OPTS.X11)" \
@ -211,7 +212,7 @@ solaris-x:
LDFLAGS+="$(CFLAGS.X11)" \
LDLIBS="-lX11 -lXext" \
LDLIBS+="$(LIBS.X11)" \
OBJS="mainX11.o SndUnix.o"
OBJS="mainX11.o SoundX11.o"
###############################################################################
## List of "core" object files
@ -223,8 +224,8 @@ CORE_OBJS = Booster.o Cart.o Cart2K.o Cart3F.o Cart4K.o CartAR.o CartDPC.o \
CartF8.o CartF8SC.o CartFASC.o CartFE.o CartMC.o CartCV.o \
CartMB.o Console.o Control.o Driving.o \
Event.o Joystick.o Keyboard.o M6532.o MD5.o MediaSrc.o Paddles.o \
Props.o PropsSet.o Random.o Sound.o Switches.o Settings.o TIA.o \
Serializer.o Deserializer.o \
Props.o PropsSet.o Random.o Switches.o Settings.o TIA.o \
Serializer.o Deserializer.o TIASound.o \
$(M6502_OBJS)
stella.exe: $(CORE_OBJS) $(OBJS)
@ -347,6 +348,9 @@ M6532.o: $(CORE)/M6532.cxx
TIA.o: $(CORE)/TIA.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/TIA.cxx
TIASound.o: $(CORE)/TIASound.c
$(CXX) -c -DWIN32 $(CXXFLAGS) $(OPTIONS) $(CORE)/TIASound.c
Console.o: $(CORE)/Console.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Console.cxx
@ -368,9 +372,6 @@ Random.o: $(CORE)/Random.cxx
Switches.o: $(CORE)/Switches.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Switches.cxx
Sound.o: $(CORE)/Sound.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Sound.cxx
Serializer.o: $(CORE)/Serializer.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Serializer.cxx
@ -395,15 +396,15 @@ SndDOS.o: $(UI)/dos/SndDOS.cxx
sbdrv.o: $(UI)/dos/sbdrv.c
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/dos/sbdrv.c
TIASound.o: $(UI)/sound/TIASound.c
$(CXX) -c -DWIN32 $(CXXFLAGS) $(OPTIONS) $(UI)/sound/TIASound.c
TermX11.o: $(UI)/x11/TermX11.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/x11/TermX11.cxx
mainX11.o: $(UI)/x11/mainX11.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(LDFLAGS) $(UI)/x11/mainX11.cxx
SoundX11.o: $(UI)/x11/SoundX11.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(LDFLAGS) $(UI)/x11/SoundX11.cxx
mainSDL.o: $(UI)/sdl/mainSDL.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(LDFLAGS) $(UI)/sdl/mainSDL.cxx
@ -416,9 +417,6 @@ RectList.o: $(UI)/sdl/RectList.cxx $(UI)/sdl/RectList.hxx
Snapshot.o: $(UI)/common/Snapshot.cxx $(UI)/common/Snapshot.hxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(LDFLAGS) $(UI)/common/Snapshot.cxx
SndUnix.o: $(UI)/sound/SndUnix.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/sound/SndUnix.cxx
D6502.o: $(CORE)/m6502/src/D6502.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/m6502/src/D6502.cxx
@ -439,3 +437,4 @@ NullDev.o: $(CORE)/m6502/src/NullDev.cxx
System.o: $(CORE)/m6502/src/System.cxx
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/m6502/src/System.cxx

View File

@ -8,12 +8,12 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1998 by Bradford W. Mott
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Console.cxx,v 1.3 2002-01-16 02:14:25 stephena Exp $
// $Id: Console.cxx,v 1.4 2002-10-09 04:38:11 bwmott Exp $
//============================================================================
#include <assert.h>
@ -41,7 +41,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Console::Console(const uInt8* image, uInt32 size, const char* filename,
const Event& event, PropertiesSet& propertiesSet, Sound& sound)
const Event& event, PropertiesSet& propertiesSet, uInt32 sampleRate)
: myEvent(event)
{
myControllers[0] = 0;
@ -126,7 +126,7 @@ Console::Console(const uInt8* image, uInt32 size, const char* filename,
}
M6532* m6532 = new M6532(*this);
TIA* tia = new TIA(*this, sound);
TIA* tia = new TIA(*this, sampleRate);
Cartridge* cartridge = Cartridge::create(image, size, myProperties);
mySystem->attach(m6502);

View File

@ -8,12 +8,12 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1998 by Bradford W. Mott
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Console.hxx,v 1.2 2002-01-08 17:11:32 stephena Exp $
// $Id: Console.hxx,v 1.3 2002-10-09 04:38:11 bwmott Exp $
//============================================================================
#ifndef CONSOLE_HXX
@ -36,7 +36,7 @@ class System;
This class represents the entire game console.
@author Bradford W. Mott
@version $Id: Console.hxx,v 1.2 2002-01-08 17:11:32 stephena Exp $
@version $Id: Console.hxx,v 1.3 2002-10-09 04:38:11 bwmott Exp $
*/
class Console
{
@ -50,10 +50,10 @@ class Console
@param filename The name of the file that contained the ROM image
@param event The event object to use
@param profiles The game profiles object to use
@param sound The sound object to use
@param sampleRate The rate to create audio samples at
*/
Console(const uInt8* image, uInt32 size, const char* filename,
const Event& event, PropertiesSet& propertiesSet, Sound& sound);
const Event& event, PropertiesSet& propertiesSet, uInt32 sampleRate);
/**
Create a new console object by copying another one

View File

@ -8,12 +8,12 @@
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1998 by Bradford W. Mott
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: MediaSrc.hxx,v 1.3 2002-04-18 17:18:48 stephena Exp $
// $Id: MediaSrc.hxx,v 1.4 2002-10-09 04:38:11 bwmott Exp $
//============================================================================
#ifndef MEDIASOURCE_HXX
@ -26,10 +26,10 @@ class MediaSource;
#include "bspf.hxx"
/**
This class provides an interface for accessing graphics data.
This class provides an interface for accessing graphics and audio data.
@author Bradford W. Mott
@version $Id: MediaSrc.hxx,v 1.3 2002-04-18 17:18:48 stephena Exp $
@version $Id: MediaSrc.hxx,v 1.4 2002-10-09 04:38:11 bwmott Exp $
*/
class MediaSource
{
@ -46,15 +46,18 @@ class MediaSource
public:
/**
This method should be called at an interval corresponding to
the desired frame rate to update the media source.
This method should be called at an interval corresponding to the
desired frame rate to update the media source. Invoking this method
will update the graphics buffer and generate the corresponding audio
samples.
*/
virtual void update() = 0;
/**
This method should be called to cause further calls to 'update'
to be ignored until an unpause is given. Will also send a mute to
the Sound device.
This method should be called to change the pause state of the
media source. Once the media source is paused further calls to
the update method will be ignored until the media source is
unpaused.
@return Status of the pause, success (true) or failure (false)
*/
@ -111,6 +114,47 @@ class MediaSource
*/
virtual uInt32 scanlines() const = 0;
public:
/**
Enumeration of the possible audio sample types.
*/
enum AudioSampleType
{
UNSIGNED_8BIT_MONO_AUDIO
};
/**
Dequeues all of the samples in the audio sample queue.
*/
virtual void clearAudioSamples() = 0;
/**
Dequeues up to the specified number of samples from the audio sample
queue into the buffer. If the requested number of samples are not
available then all of samples are dequeued. The method returns the
actual number of samples removed from the queue.
@return The actual number of samples which were dequeued.
*/
virtual uInt32 dequeueAudioSamples(uInt8* buffer, int size) = 0;
/**
Answers the number of samples currently available in the audio
sample queue.
@return The number of samples in the audio sample queue.
*/
virtual uInt32 numberOfAudioSamples() const = 0;
/**
Returns the type of audio samples which are being stored in the audio
sample queue. Currently, only unsigned 8-bit audio samples are created,
however, in the future this will be extended to support stereo samples.
@return The type of audio sample stored in the sample queue.
*/
virtual AudioSampleType typeOfAudioSamples() const = 0;
private:
// Copy constructor isn't supported by this class so make it private
MediaSource(const MediaSource&);
@ -119,3 +163,4 @@ class MediaSource
MediaSource& operator = (const MediaSource&);
};
#endif

View File

@ -1,52 +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-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Sound.cxx,v 1.2 2002-03-28 02:02:24 bwmott Exp $
//============================================================================
#include "Sound.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Sound::Sound()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Sound::~Sound()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Sound::set(Sound::Register r, uInt8 v, uInt32)
{
// For backwards compatibility we invoke the two argument version of this
// method. This should keep all of the old derived sound classes happy.
set(r, v);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Sound::set(Sound::Register r, uInt8 v)
{
// This sound class doesn't do anything when a register is set
// since we're not handling sound
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Sound::mute(bool)
{
// There's nothing for us to do when the sound is muted since
// we're not handling sound
}

View File

@ -1,82 +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-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Sound.hxx,v 1.2 2002-03-28 02:02:24 bwmott Exp $
//============================================================================
#ifndef SOUND_HXX
#define SOUND_HXX
#include "bspf.hxx"
/**
Base class that defines the standard API for sound classes. You
should derive a new class from this one to create a new sound system
for a specific operating system.
@author Bradford W. Mott
@version $Id: Sound.hxx,v 1.2 2002-03-28 02:02:24 bwmott Exp $
*/
class Sound
{
public:
/**
Enumeration of the TIA sound registers
*/
enum Register
{
AUDF0, AUDF1, AUDC0, AUDC1, AUDV0, AUDV1
};
public:
/**
Create a new sound object
*/
Sound();
/**
Destructor
*/
virtual ~Sound();
public:
/**
Set the value of the specified sound register.
@param reg The sound register to set
@param val The new value for the sound register
@param cycles The number of elapsed CPU cycles since the last set
*/
virtual void set(Sound::Register reg, uInt8 val, uInt32 cycles);
/**
Set the value of the specified sound register. This method is being
kept for backwards compatibility. There's a good chance it will be
removed in the 1.3 release of Stella as the sound system is overhauled.
@param reg The sound register to set
@param val The new value for the sound register
*/
virtual void set(Sound::Register reg, uInt8 val);
/**
Set the mute state of the sound object
@param state Mutes sound iff true
*/
virtual void mute(bool state);
};
#endif

View File

@ -13,29 +13,28 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: TIA.cxx,v 1.15 2002-08-17 15:29:28 stephena Exp $
// $Id: TIA.cxx,v 1.16 2002-10-09 04:38:11 bwmott Exp $
//============================================================================
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include "Console.hxx"
#include "Control.hxx"
#include "M6502.hxx"
#include "Sound.hxx"
#include "System.hxx"
#include "TIA.hxx"
#include "Serializer.hxx"
#include "Deserializer.hxx"
#include <iostream>
#include "TIASound.h"
#define HBLANK 68
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TIA::TIA(const Console& console, Sound& sound)
TIA::TIA(const Console& console, uInt32 sampleRate)
: myConsole(console),
mySound(sound),
myPauseState(false),
myMessageTime(0),
myMessageText(""),
@ -44,8 +43,15 @@ TIA::TIA(const Console& console, Sound& sound)
myCOLUBK(myColor[0]),
myCOLUPF(myColor[1]),
myCOLUP0(myColor[2]),
myCOLUP1(myColor[3])
myCOLUP1(myColor[3]),
mySampleQueue(sampleRate),
mySampleRate(sampleRate)
{
if(mySampleRate != 0)
{
Tia_sound_init(31400, mySampleRate);
}
// Allocate buffers for two frame buffers
myCurrentFrameBuffer = new uInt8[160 * 300];
myPreviousFrameBuffer = new uInt8[160 * 300];
@ -526,6 +532,9 @@ void TIA::update()
// TODO: have code here that handles errors....
// Make sure all of the audio samples have been created
createAudioSamples(0, 0);
// Compute the number of scanlines in the frame
uInt32 totalClocks = (mySystem->cycles() * 3) - myClockWhenFrameStarted;
myScanlineCountForLastFrame = totalClocks / 228;
@ -549,10 +558,6 @@ bool TIA::pause(bool state)
else
{
myPauseState = state;
// Propagate the pause state to the sound object
mySound.mute(myPauseState);
return true;
}
}
@ -697,6 +702,30 @@ uInt32 TIA::scanlines() const
return (uInt32)myScanlineCountForLastFrame;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::clearAudioSamples()
{
mySampleQueue.clear();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 TIA::dequeueAudioSamples(uInt8* buffer, int size)
{
return mySampleQueue.dequeue(buffer, size);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 TIA::numberOfAudioSamples() const
{
return mySampleQueue.size();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MediaSource::AudioSampleType TIA::typeOfAudioSamples() const
{
return MediaSource::UNSIGNED_8BIT_MONO_AUDIO;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::computeBallMaskTable()
{
@ -2474,49 +2503,37 @@ void TIA::poke(uInt16 addr, uInt8 value)
case 0x15: // Audio control 0
{
mySound.set(Sound::AUDC0, value,
mySystem->cycles() - myLastSoundUpdateCycle);
myLastSoundUpdateCycle = mySystem->cycles();
createAudioSamples(addr, value);
break;
}
case 0x16: // Audio control 1
{
mySound.set(Sound::AUDC1, value,
mySystem->cycles() - myLastSoundUpdateCycle);
myLastSoundUpdateCycle = mySystem->cycles();
createAudioSamples(addr, value);
break;
}
case 0x17: // Audio frequency 0
{
mySound.set(Sound::AUDF0, value,
mySystem->cycles() - myLastSoundUpdateCycle);
myLastSoundUpdateCycle = mySystem->cycles();
createAudioSamples(addr, value);
break;
}
case 0x18: // Audio frequency 1
{
mySound.set(Sound::AUDF1, value,
mySystem->cycles() - myLastSoundUpdateCycle);
myLastSoundUpdateCycle = mySystem->cycles();
createAudioSamples(addr, value);
break;
}
case 0x19: // Audio volume 0
{
mySound.set(Sound::AUDV0, value,
mySystem->cycles() - myLastSoundUpdateCycle);
myLastSoundUpdateCycle = mySystem->cycles();
createAudioSamples(addr, value);
break;
}
case 0x1A: // Audio volume 1
{
mySound.set(Sound::AUDV1, value,
mySystem->cycles() - myLastSoundUpdateCycle);
myLastSoundUpdateCycle = mySystem->cycles();
createAudioSamples(addr, value);
break;
}
@ -2845,6 +2862,42 @@ void TIA::poke(uInt16 addr, uInt8 value)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::createAudioSamples(uInt16 addr, uInt8 value)
{
// If the sample rate is zero then we should not create any audio samples
if(mySampleRate == 0)
{
return;
}
// Calculate the number of samples that need to be generated based on the
// number of CPU cycles which have passed since the last sound update
uInt32 samplesToGenerate =
(mySampleRate * (mySystem->cycles() - myLastSoundUpdateCycle)) / 1190000;
// Update counters and create samples if there's one sample to generate
// TODO: This doesn't handle rounding quite right (10/08/2002)
if(samplesToGenerate >= 1)
{
uInt8 buffer[1024];
for(Int32 sg = (Int32)samplesToGenerate; sg > 0; sg -= 1024)
{
Tia_process(buffer, ((sg >= 1024) ? 1024 : sg));
mySampleQueue.enqueue(buffer, ((sg >= 1024) ? 1024 : sg));
}
myLastSoundUpdateCycle = myLastSoundUpdateCycle +
((samplesToGenerate * 1190000) / mySampleRate);
}
if(addr != 0)
{
Update_tia_sound(addr, value);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIA::ourBallMaskTable[4][4][320];
@ -3178,11 +3231,11 @@ const uInt32 TIA::ourFontData[36] = {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TIA::TIA(const TIA& c)
: myConsole(c.myConsole),
mySound(c.mySound),
myCOLUBK(myColor[0]),
myCOLUPF(myColor[1]),
myCOLUP0(myColor[2]),
myCOLUP1(myColor[3])
myCOLUP1(myColor[3]),
mySampleQueue(1024)
{
assert(false);
}
@ -3195,3 +3248,96 @@ TIA& TIA::operator = (const TIA&)
return *this;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TIA::SampleQueue::SampleQueue(uInt32 capacity)
: myCapacity(capacity),
myBuffer(0),
mySize(0),
myHead(0),
myTail(0)
{
myBuffer = new uInt8[myCapacity];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TIA::SampleQueue::~SampleQueue()
{
delete[] myBuffer;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::SampleQueue::clear()
{
myHead = myTail = mySize = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 TIA::SampleQueue::dequeue(uInt8* buffer, uInt32 size)
{
// We can only dequeue up to the number of items in the queue
if(size > mySize)
{
size = 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 TIA::SampleQueue::enqueue(uInt8* buffer, uInt32 size)
{
// If an attempt is made to enqueue more than the queue can hold then
// we'll only enqueue the last myCapacity elements.
if(size > myCapacity)
{
buffer += (size - myCapacity);
size = myCapacity;
}
if((myTail + size) < myCapacity)
{
memcpy((void*)(myBuffer + myTail), (const void*)buffer, size);
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 TIA::SampleQueue::size() const
{
return mySize;
}

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: TIA.hxx,v 1.6 2002-05-13 19:17:32 stephena Exp $
// $Id: TIA.hxx,v 1.7 2002-10-09 04:38:12 bwmott Exp $
//============================================================================
#ifndef TIA_HXX
@ -39,10 +39,11 @@ class Deserializer;
and composite sync required by a video modulator.
This class outputs the serial data into a frame buffer which can then
be displayed on screen.
be displayed on screen it also creates audio samples and places them
in a bounded queue.
@author Bradford W. Mott
@version $Id: TIA.hxx,v 1.6 2002-05-13 19:17:32 stephena Exp $
@version $Id: TIA.hxx,v 1.7 2002-10-09 04:38:12 bwmott Exp $
*/
class TIA : public Device , public MediaSource
{
@ -51,8 +52,9 @@ class TIA : public Device , public MediaSource
Create a new TIA for the specified console
@param console The console the TIA is associated with
@param sampleRate The sample rate to create audio samples at
*/
TIA(const Console& console, Sound& sound);
TIA(const Console& console, uInt32 sampleRate);
/**
Destructor
@ -184,6 +186,38 @@ class TIA : public Device , public MediaSource
*/
uInt32 scanlines() const;
/**
Dequeues all of the samples in the audio sample queue.
*/
void clearAudioSamples();
/**
Dequeues up to the specified number of samples from the audio sample
queue into the buffer. If the requested number of samples are not
available then all of samples are dequeued. The method returns the
actual number of samples removed from the queue.
@return The actual number of samples which were dequeued.
*/
uInt32 dequeueAudioSamples(uInt8* buffer, int size);
/**
Answers the number of samples currently available in the audio
sample queue.
@return The number of samples in the audio sample queue.
*/
uInt32 numberOfAudioSamples() const;
/**
Returns the type of audio samples which are being stored in the audio
sample queue. Currently, only unsigned 8-bit audio samples are created,
however, in the future this will be extended to support stereo samples.
@return The type of audio sample stored in the sample queue.
*/
MediaSource::AudioSampleType typeOfAudioSamples() const;
private:
// Compute the ball mask table
void computeBallMaskTable();
@ -217,22 +251,78 @@ class TIA : public Device , public MediaSource
void waitHorizontalSync();
// Draw message to framebuffer
void drawMessageText();
void drawMessageText();
private:
/**
A bounded queue class used to hold audio samples after they are
produced by the TIA sound emulation.
*/
class SampleQueue
{
public:
/**
Create a new SampleQueue instance which can hold the specified
number of samples. If the queue ever reaches its capacity then
older samples are discarded.
*/
SampleQueue(uInt32 capacity);
/**
Destroy this SampleQueue instance.
*/
virtual ~SampleQueue();
public:
/**
Clear any samples stored in the queue.
*/
void clear();
/**
Dequeue the upto the specified number of samples and store them
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);
/**
Enqueue the specified number of samples from the buffer.
*/
void enqueue(uInt8* buffer, uInt32 size);
/**
Answers the number of samples currently in the queue.
@return The number of samples in the queue.
*/
uInt32 size() const;
private:
const uInt32 myCapacity;
uInt8* myBuffer;
uInt32 mySize;
uInt32 myHead;
uInt32 myTail;
};
// Invoked to create and store audio samples into the sample queue
// whenever a TIA audio register is changed or a frame is finished
void createAudioSamples(uInt16 addr, uInt8 value);
private:
// Console the TIA is associated with
const Console& myConsole;
// Sound object used by the TIA
Sound& mySound;
// Indicates whether the emulation is paused or not
bool myPauseState;
// message timer
Int32 myMessageTime;
// Message timer
Int32 myMessageTime;
// message text
// Message text
string myMessageText;
private:
@ -426,6 +516,13 @@ class TIA : public Device , public MediaSource
// Counter used for TIA M0 "bug"
uInt32 myM0CosmicArkCounter;
private:
// Sample queue instance for storing audio samples
SampleQueue mySampleQueue;
// Sample rate requested for the audio samples
uInt32 mySampleRate;
private:
// Ball mask table (entries are true or false)
static uInt8 ourBallMaskTable[4][4][320];
@ -479,4 +576,6 @@ class TIA : public Device , public MediaSource
// Assignment operator isn't supported by this class so make it private
TIA& operator = (const TIA&);
};
#endif

View File

@ -1,290 +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-1998 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: OSS.c,v 1.2 2002-01-08 17:11:32 stephena Exp $
//==========================================================================*/
/**
This file implements the "stella-sound" process for the
Open Sound System (OSS) API.
@author Bradford W. Mott
@version $Id: OSS.c,v 1.2 2002-01-08 17:11:32 stephena Exp $
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#ifdef __FreeBSD__
#include <machine/soundcard.h>
#else
#include <sys/soundcard.h>
#endif
#include "TIASound.h"
/**
Compute Fragment size to use based on the sample rate
@param sampleRate The sample rate to compute the fragment size for
*/
unsigned long computeFragmentSize(int sampleRate);
/* Mixer function prototypes */
void openMixer(int changeVolume);
void closeMixer();
/* dsp and mixer file descriptors */
int fd, mixer_fd;
int originalVolume;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int main(int argc, char* argv[])
{
int numberAndSizeOfFragments;
int fragmentSize;
unsigned char* fragmentBuffer;
int sampleRate;
int format;
int stereo;
int mute = 0;
int newVolume = 75;
if(argc == 3) /* check to see if volume has been given */
{
if(!strncmp(argv[1], "-volume", 7))
{
if((atoi(argv[2]) >= 0) && (atoi(argv[2]) <= 100))
newVolume = atoi(argv[2]);
}
}
/* Open the sound device for writing */
if((fd = open("/dev/dsp", O_WRONLY, 0)) == -1)
{
printf("stella-sound: Unable to open /dev/dsp device!\n");
return 1;
}
/* Set the AUDIO DATA FORMAT */
format = AFMT_U8;
if(ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1)
{
printf("stella-sound: Unable to set 8-bit sample mode!\n");
return 1;
}
if(format != AFMT_U8)
{
printf("stella-sound: Sound card doesn't support 8-bit sample mode!\n");
return 1;
}
/* Set MONO MODE */
stereo = 0;
if(ioctl(fd, SNDCTL_DSP_STEREO, &stereo) == -1)
{
printf("stella-sound: Sound card doesn't support mono mode!\n");
return 1;
}
if(stereo != 0)
{
printf("stella-sound: Sound card doesn't support mono mode!\n");
return 1;
}
/* Set the SAMPLE RATE */
sampleRate = 31400;
if(ioctl(fd, SNDCTL_DSP_SPEED, &sampleRate) == -1)
{
printf("stella-sound: Unable to set sample rate for /dev/dsp!\n");
return 1;
}
/* Set the NUMBER AND SIZE OF FRAGMENTS */
numberAndSizeOfFragments = 0x00020000 | computeFragmentSize(sampleRate);
if(ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &numberAndSizeOfFragments) == -1)
{
printf("stella-sound: Unable to set fragment size!\n");
return 1;
}
/* Query for the actual fragment size */
ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &fragmentSize);
/* Allocate fragment buffer */
fragmentBuffer = (unsigned char*)malloc(fragmentSize);
/* Now open the mixer for changing the volume */
openMixer(newVolume);
/* Initialize the TIA Sound Library */
Tia_sound_init(31400, sampleRate);
/* Make sure STDIN is in nonblocking mode */
if(fcntl(0, F_SETFL, O_NONBLOCK) == -1)
{
printf("stella-sound: Couldn't set non-blocking mode\n");
return 1;
}
/* Loop reading commands from the emulator and playing sound fragments */
for(;;)
{
int done = 0;
while(!done)
{
int i;
int n;
unsigned char input[1024];
/* Read as many commands as available */
n = read(0, input, 1024);
/* Process all of the commands we read */
for(i = 0; i < n; ++i)
{
unsigned char value = input[i];
switch((value >> 5) & 0x07)
{
case 0: /* Set AUDC0 */
Update_tia_sound(0x15, value);
break;
case 1: /* Set AUDC1 */
Update_tia_sound(0x16, value);
break;
case 2: /* Set AUDF0 */
Update_tia_sound(0x17, value);
break;
case 3: /* Set AUDF1 */
Update_tia_sound(0x18, value);
break;
case 4: /* Set AUDV0 */
Update_tia_sound(0x19, value);
break;
case 5: /* Set AUDV1 */
Update_tia_sound(0x1A, value);
break;
case 6: /* Quit */
close(fd);
free(fragmentBuffer);
closeMixer();
return 1;
break;
case 7: /* Change mute command */
mute = value & 0x01;
break;
default:
break;
}
}
done = (n != 1024);
}
/* If sound isn't muted then play something */
if(!mute)
{
/* Create the next fragment to play */
Tia_process(fragmentBuffer, fragmentSize);
/* Write fragment to sound device */
write(fd, fragmentBuffer, fragmentSize);
}
else
{
/* Sound is muted so let's sleep for a little while */
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
select(0, 0, 0, 0, &timeout);
}
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
unsigned long computeFragmentSize(int sampleRate)
{
int t;
for(t = 7; t < 24; ++t)
{
if((1 << t) > (sampleRate / 60))
return t - 1;
}
/* Default to 256 byte fragment size */
return 8;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void openMixer(int changeVolume)
{
int volume;
if((mixer_fd = open("/dev/mixer", O_RDWR, 0)) == -1)
{
printf("stella-sound: Unable to open /dev/mixer device!\n");
mixer_fd = 0;
return;
}
volume = 0;
if(ioctl(mixer_fd, MIXER_READ(SOUND_MIXER_PCM), &originalVolume) == -1)
{
printf("stella-sound: Unable to read mixer settings!\n");
close(mixer_fd);
mixer_fd = 0;
return;
}
volume = changeVolume | (changeVolume << 8);
if(ioctl(mixer_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)
{
printf("stella-sound: Unable to set new volume!\n");
close(mixer_fd);
mixer_fd = 0;
return;
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void closeMixer()
{
if(mixer_fd)
{
if(ioctl(mixer_fd, MIXER_WRITE(SOUND_MIXER_PCM), &originalVolume) == -1)
printf("stella-sound: Unable to set original volume!\n");
close(mixer_fd);
}
}

View File

@ -1,233 +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-1998 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SndUnix.cxx,v 1.2 2002-01-08 17:11:32 stephena Exp $
//============================================================================
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include "SndUnix.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundUnix::SoundUnix(int volume)
: myDisabled(false),
myMute(false)
{
// Initialize to impossible values so they will be reset
// on the first call to the set method
myAUDC0 = myAUDC1 = myAUDF0 = myAUDF1 = myAUDV0 = myAUDV1 = 255;
int pfd[2];
// Create pipe for interprocess communication
if(pipe(pfd))
{
// Oops. We were not able to create pipe so disable myself and return
myDisabled = true;
return;
}
// Create new process, setup pipe, and start stella-sound
myProcessId = fork();
if(myProcessId == 0)
{
// Close STDIN and put the read end of the pipe in its place
dup2(pfd[0], 0);
// Close unused file descriptors in child process
close(pfd[0]);
close(pfd[1]);
// Execute the stella-sound server
char vol[3];
sprintf(vol, "%d", volume);
if(execlp("stella-sound", "stella-sound", "-volume", vol, (char*)0))
{
exit(1);
}
}
else if(myProcessId > 0)
{
// Close unused file descriptors in parent process
close(pfd[0]);
// Save the pipe's write file descriptor
myFd = pfd[1];
}
else
{
// Couldn't fork so cleanup and disabled myself
close(pfd[0]);
close(pfd[1]);
myDisabled = true;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundUnix::~SoundUnix()
{
if(!myDisabled)
{
// Send quit command to the sound server
unsigned char command = 0xC0;
write(myFd, &command, 1);
// Close descriptors
close(myFd);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundUnix::set(Sound::Register reg, uInt8 value)
{
// Return if I'm currently disabled or if the stella-sound process has died
if(myDisabled || (waitpid(myProcessId, 0, WNOHANG) == myProcessId))
{
myDisabled = true;
return;
}
uInt8 command;
switch(reg)
{
case AUDC0:
{
if(myAUDC0 != (value & 0x0f))
{
myAUDC0 = (value & 0x0f);
command = myAUDC0 | 0x00;
}
else
{
return;
}
break;
}
case AUDC1:
{
if(myAUDC1 != (value & 0x0f))
{
myAUDC1 = (value & 0x0f);
command = myAUDC1 | 0x20;
}
else
{
return;
}
break;
}
case AUDF0:
{
if(myAUDF0 != (value & 0x1f))
{
myAUDF0 = (value & 0x1f);
command = myAUDF0 | 0x40;
}
else
{
return;
}
break;
}
case AUDF1:
{
if(myAUDF1 != (value & 0x1f))
{
myAUDF1 = (value & 0x1f);
command = myAUDF1 | 0x60;
}
else
{
return;
}
break;
}
case AUDV0:
{
if(myAUDV0 != (value & 0x0f))
{
myAUDV0 = (value & 0x0f);
command = myAUDV0 | 0x80;
}
else
{
return;
}
break;
}
case AUDV1:
{
if(myAUDV1 != (value & 0x0f))
{
myAUDV1 = (value & 0x0f);
command = myAUDV1 | 0xA0;
}
else
{
return;
}
break;
}
default:
{
return;
break;
}
}
// Send sound command to the stella-sound process
write(myFd, &command, 1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundUnix::mute(bool state)
{
// Return if I'm currently disabled or if the stella-sound process has died
if(myDisabled || (waitpid(myProcessId, 0, WNOHANG) == myProcessId))
{
myDisabled = true;
return;
}
myMute = state;
uInt8 command;
if(myMute)
{
// Setup the mute enable command
command = 0x01 | 0xE0;
}
else
{
// Setup the mute disable command
command = 0x00 | 0xE0;
}
// Send sound command to the stella-sound process
write(myFd, &command, 1);
}

View File

@ -1,86 +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-1998 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SndUnix.hxx,v 1.2 2002-01-08 17:11:32 stephena Exp $
//============================================================================
#ifndef SOUNDUNIX_HXX
#define SOUNDUNIX_HXX
#include "bspf.hxx"
#include "Sound.hxx"
/**
This class implements the sound API for the Unix operating system.
Under Unix the real work of the sound system is in another process
called "stella-sound". This process is started when an instance
of the SoundUnix class is created. Communicattion with the
"stella-sound" process is done through a pipe.
@author Bradford W. Mott
@version $Id: SndUnix.hxx,v 1.2 2002-01-08 17:11:32 stephena Exp $
*/
class SoundUnix : public Sound
{
public:
/**
Create a new sound object
*/
SoundUnix(int volume);
/**
Destructor
*/
virtual ~SoundUnix();
public:
/**
Set the value of the specified sound register
@param reg The sound register to set
@param val The new value for the sound register
*/
virtual void set(Sound::Register reg, uInt8 val);
/**
Set the mute state of the sound object
@param state Mutes sound iff true
*/
virtual void mute(bool state);
private:
// Indicates if the sound system couldn't be initialized
bool myDisabled;
// Indicates if the sound is muted or not
bool myMute;
// ProcessId of the stella-sound process
int myProcessId;
// Write file descriptor for IPC
int myFd;
// Buffers for the audio registers used so we only
// send "changes" to the stella-sound process
uInt8 myAUDC0;
uInt8 myAUDC1;
uInt8 myAUDF0;
uInt8 myAUDF1;
uInt8 myAUDV0;
uInt8 myAUDV1;
};
#endif

View File

@ -1,36 +0,0 @@
###############################################################################
## C compiler to use
###############################################################################
CC = gcc
CFLAGS = -DWIN32 -O -ansi -Wall
OBJS = TIASound.o
all:
@echo ""
@echo "To build stella-sound, type: 'make <version>'"
@echo ""
@echo "where <version> is one of:"
@echo ""
@echo " linux Linux (same as uss)"
@echo " uss Systems with the Unix Sound System (same as oss)"
@echo " oss systems with the Open Sound System"
@echo " bsdi BSD/OS 4.0"
@echo ""
@echo "Hopefully new versions will be added soon!"
@echo ""
bsdi:
make oss CFLAGS="-DWIN32 -O3 -Wall"
linux: oss
uss: oss
oss: $(OBJS) OSS.o
$(CC) -o stella-sound OSS.o $(OBJS)
clean:
rm -f *.o stella-sound

View File

@ -0,0 +1,229 @@
//============================================================================
//
// 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-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundX11.cxx,v 1.1 2002-10-09 04:38:12 bwmott Exp $
//============================================================================
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#ifdef __FreeBSD__
#include <machine/soundcard.h>
#else
#include <sys/soundcard.h>
#endif
#define DSP_DEVICE "/dev/dsp"
#define MIXER_DEVICE "/dev/mixer"
#include "SoundX11.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundX11::SoundX11()
: myIsInitializedFlag(false),
myDspFd(-1),
myMixerFd(-1),
myOriginalVolume(-1),
mySampleRate(0)
{
// Open the sound device for writing
if((myDspFd = open(DSP_DEVICE, O_WRONLY, 0)) == -1)
{
perror(DSP_DEVICE);
return;
}
// Set the number and size of fragments
int numberAndSizeOfFragments = 0x00020009;
if(ioctl(myDspFd, SNDCTL_DSP_SETFRAGMENT, &numberAndSizeOfFragments) == -1)
{
perror(DSP_DEVICE);
close(myDspFd);
myDspFd = -1;
return;
}
// Set the audio data format
int format = AFMT_U8;
if(ioctl(myDspFd, SNDCTL_DSP_SETFMT, &format) == -1)
{
perror(DSP_DEVICE);
close(myDspFd);
myDspFd = -1;
return;
}
// Make sure the U8 format was selected
if(format != AFMT_U8)
{
cerr << DSP_DEVICE << ": Doesn't support 8-bit sample format!" << endl;
close(myDspFd);
myDspFd = -1;
return;
}
// Set the number of audio channels to 1 (mono mode)
int channels = 1;
if(ioctl(myDspFd, SNDCTL_DSP_CHANNELS, &channels) == -1)
{
perror(DSP_DEVICE);
close(myDspFd);
myDspFd = -1;
return;
}
// Make sure mono mode was selected
if(channels != 1)
{
cerr << DSP_DEVICE << ": Doesn't support mono mode!" << endl;
close(myDspFd);
myDspFd = -1;
return;
}
// Set the audio sample rate
mySampleRate = 31400;
if(ioctl(myDspFd, SNDCTL_DSP_SPEED, &mySampleRate) == -1)
{
perror(DSP_DEVICE);
close(myDspFd);
myDspFd = -1;
mySampleRate = 0;
return;
}
// Now, open the mixer so we'll be able to change the volume
if((myMixerFd = open(MIXER_DEVICE, O_RDWR, 0)) == -1)
{
perror(MIXER_DEVICE);
}
else
{
if(ioctl(myMixerFd, MIXER_READ(SOUND_MIXER_PCM), &myOriginalVolume) == -1)
{
myOriginalVolume = -1;
}
}
// Indicate that the sound system is fully initialized
myIsInitializedFlag = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundX11::~SoundX11()
{
if(myMixerFd != -1)
{
if(myOriginalVolume != -1)
{
if(ioctl(myMixerFd, MIXER_WRITE(SOUND_MIXER_PCM),
&myOriginalVolume) == -1)
{
perror(MIXER_DEVICE);
}
}
close(myMixerFd);
}
if(myDspFd != -1)
{
close(myDspFd);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundX11::getSampleRate() const
{
return myIsInitializedFlag ? mySampleRate : 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundX11::isSuccessfullyInitialized() const
{
return myIsInitializedFlag;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundX11::setSoundVolume(uInt32 volume)
{
if(myIsInitializedFlag && (myMixerFd != -1))
{
if(volume < 0)
{
volume = 0;
}
if(volume > 100)
{
volume = 100;
}
int v = volume | (volume << 8);
if(ioctl(myMixerFd, MIXER_WRITE(SOUND_MIXER_PCM), &v) == -1)
{
perror(MIXER_DEVICE);
close(myMixerFd);
myMixerFd = -1;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundX11::updateSound(MediaSource& mediaSource)
{
if(myIsInitializedFlag)
{
// Get audio buffer information
audio_buf_info info;
if(ioctl(myDspFd, SNDCTL_DSP_GETOSPACE, &info) == -1)
{
return;
}
// Dequeue samples as long as full fragments are available
while(mediaSource.numberOfAudioSamples() >= (uInt32)info.fragsize)
{
uInt8 buffer[info.fragsize];
mediaSource.dequeueAudioSamples(buffer, (uInt32)info.fragsize);
write(myDspFd, buffer, info.fragsize);
}
// Fill any unused fragments with silence so that we have a lower
// risk of having playback underruns
for(;;)
{
// Get audio buffer information
if(ioctl(myDspFd, SNDCTL_DSP_GETOSPACE, &info) == -1)
{
return;
}
if(info.fragments > 0)
{
uInt8 buffer[info.fragsize];
memset((void*)buffer, 0, info.fragsize);
write(myDspFd, buffer, info.fragsize);
}
else
{
break;
}
}
}
}

View File

@ -0,0 +1,93 @@
//============================================================================
//
// 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-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundX11.hxx,v 1.1 2002-10-09 04:38:12 bwmott Exp $
//============================================================================
#ifndef SOUNDX11_HXX
#define SOUNDX11_HXX
#include "bspf.hxx"
#include "MediaSrc.hxx"
/**
This class implements a sound class for the X11 front-end. The
Open Sound System (OSS) API is currently supported.
@author Bradford W. Mott
@version $Id: SoundX11.hxx,v 1.1 2002-10-09 04:38:12 bwmott Exp $
*/
class SoundX11
{
public:
/**
Create a new sound object
*/
SoundX11();
/**
Destructor
*/
virtual ~SoundX11();
public:
/**
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 initlaized.
@return true iff the sound device was successfully initlaized.
*/
bool isSuccessfullyInitialized() const;
/**
Sets the volume of the sound device to the specified level. The
volume is given as a precentage from 0 to 100.
@param volume The new volume for the sound device
*/
void setSoundVolume(uInt32 volume);
/**
Update the sound device using the audio sample from the specified
media source.
@param mediaSource The media source to get audio samples from.
*/
void updateSound(MediaSource& mediaSource);
private:
// Indicates if the sound device was successfully initialized
bool myIsInitializedFlag;
// DSP file descriptor
int myDspFd;
// Mixer file descriptor
int myMixerFd;
// Original mixer volume when the sound device was opened
int myOriginalVolume;
// DSP sample rate
uInt32 mySampleRate;
};
#endif

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: mainX11.cxx,v 1.27 2002-10-05 12:49:49 stephena Exp $
// $Id: mainX11.cxx,v 1.28 2002-10-09 04:38:12 bwmott Exp $
//============================================================================
#include <fstream>
@ -39,9 +39,9 @@
#include "Event.hxx"
#include "MediaSrc.hxx"
#include "PropsSet.hxx"
#include "SndUnix.hxx"
#include "System.hxx"
#include "Settings.hxx"
#include "SoundX11.hxx"
#ifdef HAVE_PNG
#include "Snapshot.hxx"
@ -286,7 +286,8 @@ bool setupDisplay()
char name[512];
sprintf(name, "Stella: \"%s\"",
theConsole->properties().get("Cartridge.Name").c_str());
XmbSetWMProperties(theDisplay, theWindow, name, "stella", 0, 0, &hints, None, None);
XmbSetWMProperties(theDisplay, theWindow, name, "stella", 0, 0, &hints,
None, None);
// Set up the palette for the screen
setupPalette();
@ -314,8 +315,8 @@ bool setupDisplay()
XNextEvent(theDisplay, &event);
} while (event.type != Expose);
eventMask = ExposureMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask |
StructureNotifyMask;
eventMask = ExposureMask | KeyPressMask | KeyReleaseMask |
PropertyChangeMask | StructureNotifyMask;
// If we're using the mouse for paddle emulation then enable mouse events
if(((theConsole->properties().get("Controller.Left") == "Paddles") ||
@ -387,9 +388,9 @@ void setupPalette()
{
XColor color;
color.red = (short unsigned int) (((palette[t] & 0x00ff0000) >> 8) * shade);
color.green = (short unsigned int) ((palette[t] & 0x0000ff00) * shade);
color.blue = (short unsigned int) (((palette[t] & 0x000000ff) << 8) * shade);
color.red = (short unsigned int)(((palette[t] & 0x00ff0000) >> 8) * shade);
color.green = (short unsigned int)((palette[t] & 0x0000ff00) * shade);
color.blue = (short unsigned int)(((palette[t] & 0x000000ff) << 8) * shade);
color.flags = DoRed | DoGreen | DoBlue;
if(settings->theUsePrivateColormapFlag)
@ -556,7 +557,7 @@ void handleEvents()
// Handle the WM_DELETE_WINDOW message outside the event loop
if(XCheckTypedWindowEvent(theDisplay, theWindow, ClientMessage, &event))
{
if(event.xclient.data.l[0] == wm_delete_window)
if((unsigned long)event.xclient.data.l[0] == wm_delete_window)
{
doQuit();
return;
@ -1161,7 +1162,8 @@ void takeSnapshot()
filename = filename + ".png";
// Now save the snapshot file
snapshot->savePNG(filename, theConsole->mediaSource(), settings->theWindowSize);
snapshot->savePNG(filename, theConsole->mediaSource(),
settings->theWindowSize);
if(access(filename.c_str(), F_OK) == 0)
cerr << "Snapshot saved as " << filename << endl;
@ -1214,28 +1216,28 @@ void usage()
"",
"Valid options are:",
"",
" -fps <number> Display the given number of frames per second",
" -owncmap <0|1> Install a private colormap",
" -zoom <size> Makes window be 'size' times normal (1 - 4)",
// " -fullscreen <0|1> Play the game in fullscreen mode",
" -grabmouse <0|1> Keeps the mouse in the game window",
" -hidecursor <0|1> Hides the mouse cursor in the game window",
" -center <0|1> Centers the game window onscreen",
" -volume <number> Set the volume (0 - 100)",
" -fps <number> Display the given number of frames per second",
" -owncmap <0|1> Install a private colormap",
" -zoom <size> Makes window be 'size' times normal (1 - 4)",
// " -fullscreen <0|1> Play the game in fullscreen mode",
" -grabmouse <0|1> Keeps the mouse in the game window",
" -hidecursor <0|1> Hides the mouse cursor in the game window",
" -center <0|1> Centers the game window onscreen",
" -volume <number> Set the volume (0 - 100)",
#ifdef HAVE_JOYSTICK
" -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate",
" or that real Atari 2600 paddles are being used",
" -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate",
" or that real Atari 2600 paddles are being used",
#else
" -paddle <0|1|2|3> Indicates which paddle the mouse should emulate",
" -paddle <0|1|2|3> Indicates which paddle the mouse should emulate",
#endif
" -showinfo <0|1> Shows some game info on exit",
" -showinfo <0|1> Shows some game info on exit",
#ifdef HAVE_PNG
" -ssdir <path> The directory to save snapshot files to",
" -ssname <name> How to name the snapshot (romname or md5sum)",
" -sssingle <0|1> Generate single snapshot instead of many",
" -ssdir <path> The directory to save snapshot files to",
" -ssname <name> How to name the snapshot (romname or md5sum)",
" -sssingle <0|1> Generate single snapshot instead of many",
#endif
" -pro <props file> Use the given properties file instead of stella.pro",
" -accurate <0|1> Accurate game timing (uses more CPU)",
" -pro <props file> Use the given properties file instead of stella.pro",
" -accurate <0|1> Accurate game timing (uses more CPU)",
"",
0
};
@ -1266,7 +1268,8 @@ bool setupProperties(PropertiesSet& set)
{
if(access(settings->theAlternateProFile.c_str(), R_OK) == 0)
{
set.load(settings->theAlternateProFile, &Console::defaultProperties(), false);
set.load(settings->theAlternateProFile,
&Console::defaultProperties(), false);
return true;
}
else
@ -1436,15 +1439,16 @@ int main(int argc, char* argv[])
return 0;
}
// Create a sound object for use with the console
SoundUnix sound(settings->theDesiredVolume);
// Create a sound object for playing audio
SoundX11 sound;
sound.setSoundVolume(settings->theDesiredVolume);
// Get just the filename of the file containing the ROM image
const char* filename = (!strrchr(file, '/')) ? file : strrchr(file, '/') + 1;
// Create the 2600 game console
theConsole = new Console(image, size, filename,
theEvent, propertiesSet, sound);
theEvent, propertiesSet, sound.getSampleRate());
// Free the image since we don't need it any longer
delete[] image;
@ -1471,7 +1475,8 @@ int main(int argc, char* argv[])
{
// Set up timing stuff
uInt32 startTime, delta;
uInt32 timePerFrame = (uInt32) (1000000.0 / (double) settings->theDesiredFrameRate);
uInt32 timePerFrame =
(uInt32)(1000000.0 / (double)settings->theDesiredFrameRate);
// Set the base for the timers
frameTime = 0;
@ -1496,6 +1501,7 @@ int main(int argc, char* argv[])
startTime = getTicks();
theConsole->mediaSource().update();
sound.updateSound(theConsole->mediaSource());
updateDisplay(theConsole->mediaSource());
handleEvents();
@ -1516,7 +1522,8 @@ int main(int argc, char* argv[])
{
// Set up timing stuff
uInt32 startTime, virtualTime, currentTime;
uInt32 timePerFrame = (uInt32) (1000000.0 / (double) settings->theDesiredFrameRate);
uInt32 timePerFrame =
(uInt32)(1000000.0 / (double)settings->theDesiredFrameRate);
// Set the base for the timers
virtualTime = getTicks();
@ -1536,6 +1543,7 @@ int main(int argc, char* argv[])
{
theConsole->mediaSource().update();
}
sound.updateSound(theConsole->mediaSource());
updateDisplay(theConsole->mediaSource());
handleEvents();
@ -1561,9 +1569,11 @@ int main(int argc, char* argv[])
cout << numberOfFrames << " total frames drawn\n";
cout << framesPerSecond << " frames/second\n";
cout << endl;
cout << "Cartridge Name: " << theConsole->properties().get("Cartridge.Name");
cout << "Cartridge Name: "
<< theConsole->properties().get("Cartridge.Name");
cout << endl;
cout << "Cartridge MD5: " << theConsole->properties().get("Cartridge.MD5");
cout << "Cartridge MD5: "
<< theConsole->properties().get("Cartridge.MD5");
cout << endl << endl;
}
@ -1588,3 +1598,4 @@ inline uInt32 getTicks()
#else
#error We need gettimeofday for the X11 version!!!
#endif