mirror of https://github.com/stella-emu/stella.git
Updated the DOS port to use the new sound routines in the core. Also
switched to a new set of low-level Sound Blaster routines which appear to provide better Sound Blaster support and the ability to auto-detect sound card settings. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@142 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
parent
b93d8d186d
commit
f9c3e778c8
|
@ -13,7 +13,7 @@
|
||||||
## 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: makefile,v 1.26 2002-11-11 22:01:28 stephena Exp $
|
## $Id: makefile,v 1.27 2002-11-13 03:47:55 bwmott Exp $
|
||||||
##============================================================================
|
##============================================================================
|
||||||
|
|
||||||
##============================================================================
|
##============================================================================
|
||||||
|
@ -185,7 +185,7 @@ dos:
|
||||||
OPTIONS+="$(OPTS.DOS)" \
|
OPTIONS+="$(OPTS.DOS)" \
|
||||||
LDFLAGS="" \
|
LDFLAGS="" \
|
||||||
LDLIBS="" \
|
LDLIBS="" \
|
||||||
OBJS="mainDOS.o PCJoys.o SndDOS.o sbdrv.o"
|
OBJS="mainDOS.o PCJoys.o SndDOS.o dos_sb.o"
|
||||||
|
|
||||||
unix-x:
|
unix-x:
|
||||||
make stella.x11 \
|
make stella.x11 \
|
||||||
|
@ -428,8 +428,8 @@ PCJoys.o: $(UI)/dos/PCJoys.cxx
|
||||||
SndDOS.o: $(UI)/dos/SndDOS.cxx
|
SndDOS.o: $(UI)/dos/SndDOS.cxx
|
||||||
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/dos/SndDOS.cxx
|
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/dos/SndDOS.cxx
|
||||||
|
|
||||||
sbdrv.o: $(UI)/dos/sbdrv.c
|
dos_sb.o: $(UI)/dos/dos_sb.c
|
||||||
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/dos/sbdrv.c
|
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/dos/dos_sb.c
|
||||||
|
|
||||||
TermX11.o: $(UI)/x11/TermX11.cxx
|
TermX11.o: $(UI)/x11/TermX11.cxx
|
||||||
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/x11/TermX11.cxx
|
$(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/x11/TermX11.cxx
|
||||||
|
|
|
@ -8,122 +8,257 @@
|
||||||
// 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-1998 by Bradford W. Mott
|
// Copyright (c) 1995-2002 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: SndDOS.cxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $
|
// $Id: SndDOS.cxx,v 1.2 2002-11-13 03:47:55 bwmott Exp $
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "SndDOS.hxx"
|
#include "SndDOS.hxx"
|
||||||
#include "TIASound.h"
|
#include "dos.h"
|
||||||
#include "sbdrv.h"
|
#include "dos_sb.h"
|
||||||
|
|
||||||
/**
|
|
||||||
Compute the buffer size to use based on the given sample rate
|
|
||||||
|
|
||||||
@param The sample rate to compute the buffer size for
|
|
||||||
*/
|
|
||||||
static unsigned long computeBufferSize(int sampleRate)
|
|
||||||
{
|
|
||||||
int t;
|
|
||||||
|
|
||||||
for(t = 7; t <= 12; ++t)
|
|
||||||
{
|
|
||||||
if((1 << t) > (sampleRate / 60))
|
|
||||||
{
|
|
||||||
return (1 << (t - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 256;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
SoundDOS::SoundDOS()
|
SoundDOS::SoundDOS(bool activate)
|
||||||
|
: myCurrentVolume(100),
|
||||||
|
myFragmentSize(1024),
|
||||||
|
myIsInitializedFlag(false),
|
||||||
|
myUpdateLock(false),
|
||||||
|
myIsMuted(false),
|
||||||
|
mySampleRate(31400),
|
||||||
|
mySampleQueue(mySampleRate)
|
||||||
{
|
{
|
||||||
int sampleRate = 15700;
|
if(activate)
|
||||||
int DMABufferSize = computeBufferSize(sampleRate);
|
|
||||||
|
|
||||||
if(OpenSB(sampleRate, DMABufferSize))
|
|
||||||
{
|
{
|
||||||
myEnabled = true;
|
int bps = 8;
|
||||||
|
int stereo = 0;
|
||||||
|
int buffersize = myFragmentSize;
|
||||||
|
int playback_freq = mySampleRate;
|
||||||
|
|
||||||
// Initialize TIA Sound Library
|
if(sb_init(&playback_freq, &bps, &buffersize, &stereo) < 0)
|
||||||
Tia_sound_init(31400, sampleRate);
|
{
|
||||||
|
cerr << "WARNING: Couldn't initialize audio system! " << endl;
|
||||||
|
myIsInitializedFlag = false;
|
||||||
|
mySampleRate = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mySampleRate = playback_freq;
|
||||||
|
myFragmentSize = buffersize;
|
||||||
|
|
||||||
|
myIsInitializedFlag = true;
|
||||||
|
myIsMuted = false;
|
||||||
|
|
||||||
// Start playing audio
|
// Start playing audio
|
||||||
Start_audio_output(AUTO_DMA,
|
sb_startoutput((sbmix_t)callback, (void*)this);
|
||||||
(void (*)(unsigned char*, short unsigned int))Tia_process);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Oops, couldn't open SB so we're not enabled :-(
|
myIsInitializedFlag = false;
|
||||||
myEnabled = false;
|
myIsMuted = true;
|
||||||
return;
|
mySampleRate = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
SoundDOS::~SoundDOS()
|
SoundDOS::~SoundDOS()
|
||||||
{
|
{
|
||||||
if(myEnabled)
|
close();
|
||||||
{
|
|
||||||
CloseSB();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundDOS::set(Sound::Register reg, uInt8 value)
|
uInt32 SoundDOS::getSampleRate() const
|
||||||
{
|
{
|
||||||
if(!myEnabled)
|
return myIsInitializedFlag ? mySampleRate : 0;
|
||||||
return;
|
|
||||||
|
|
||||||
switch(reg)
|
|
||||||
{
|
|
||||||
case AUDC0:
|
|
||||||
Update_tia_sound(0x15, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUDC1:
|
|
||||||
Update_tia_sound(0x16, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUDF0:
|
|
||||||
Update_tia_sound(0x17, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUDF1:
|
|
||||||
Update_tia_sound(0x18, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUDV0:
|
|
||||||
Update_tia_sound(0x19, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUDV1:
|
|
||||||
Update_tia_sound(0x1A, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool SoundDOS::isSuccessfullyInitialized() const
|
||||||
|
{
|
||||||
|
return myIsInitializedFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void SoundDOS::mute(bool state)
|
void SoundDOS::mute(bool state)
|
||||||
{
|
{
|
||||||
if(state)
|
if(!myIsInitializedFlag)
|
||||||
{
|
{
|
||||||
Stop_audio_output();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore multiple calls to do the same thing
|
||||||
|
if(myIsMuted == state)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
myIsMuted = state;
|
||||||
|
|
||||||
|
if(myIsMuted)
|
||||||
|
{
|
||||||
|
sb_stopoutput();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Start_audio_output(AUTO_DMA,
|
sb_startoutput((sbmix_t)callback, (void*)this);
|
||||||
(void (*)(unsigned char*, short unsigned int))Tia_process);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundDOS::close()
|
||||||
|
{
|
||||||
|
if(myIsInitializedFlag)
|
||||||
|
{
|
||||||
|
sb_shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
myIsInitializedFlag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundDOS::setSoundVolume(uInt32 percent)
|
||||||
|
{
|
||||||
|
if(myIsInitializedFlag)
|
||||||
|
{
|
||||||
|
if((percent >= 0) && (percent <= 100))
|
||||||
|
{
|
||||||
|
// TODO: At some point we should support setting the volume...
|
||||||
|
myCurrentVolume = percent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundDOS::updateSound(MediaSource& mediaSource)
|
||||||
|
{
|
||||||
|
if(myIsInitializedFlag)
|
||||||
|
{
|
||||||
|
// Move all of the generated samples into our private sample queue
|
||||||
|
uInt8 buffer[4096];
|
||||||
|
while(mediaSource.numberOfAudioSamples() > 0)
|
||||||
|
{
|
||||||
|
uInt32 size = mediaSource.dequeueAudioSamples(buffer, 4096);
|
||||||
|
mySampleQueue.enqueue(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block until the sound interrupt has consumed all but 125 milliseconds
|
||||||
|
// of the available audio samples
|
||||||
|
uInt32 leave = mySampleRate / 8;
|
||||||
|
while(mySampleQueue.size() > leave)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundDOS::callback(void* udata, void* stream, int len)
|
||||||
|
{
|
||||||
|
SoundDOS* sound = (SoundDOS*)udata;
|
||||||
|
|
||||||
|
if(!sound->myIsInitializedFlag)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't use samples unless there's at least 100 milliseconds worth of data
|
||||||
|
if(sound->mySampleQueue.size() < (sound->mySampleRate / 10))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sound->mySampleQueue.dequeue((uInt8*)stream, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
SoundDOS::SampleQueue::SampleQueue(uInt32 capacity)
|
||||||
|
: myCapacity(capacity),
|
||||||
|
myBuffer(0),
|
||||||
|
mySize(0),
|
||||||
|
myHead(0),
|
||||||
|
myTail(0)
|
||||||
|
{
|
||||||
|
myBuffer = new uInt8[myCapacity];
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
SoundDOS::SampleQueue::~SampleQueue()
|
||||||
|
{
|
||||||
|
delete[] myBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void SoundDOS::SampleQueue::clear()
|
||||||
|
{
|
||||||
|
myHead = myTail = mySize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
uInt32 SoundDOS::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 SoundDOS::SampleQueue::enqueue(uInt8* buffer, uInt32 size)
|
||||||
|
{
|
||||||
|
disable();
|
||||||
|
if((mySize + size) > myCapacity)
|
||||||
|
{
|
||||||
|
size = myCapacity - mySize;
|
||||||
|
}
|
||||||
|
enable();
|
||||||
|
|
||||||
|
if(size == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
disable();
|
||||||
|
mySize += size;
|
||||||
|
enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
uInt32 SoundDOS::SampleQueue::size() const
|
||||||
|
{
|
||||||
|
return mySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,34 +8,34 @@
|
||||||
// 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-1998 by Bradford W. Mott
|
// Copyright (c) 1995-2002 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: SndDOS.hxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $
|
// $Id: SndDOS.hxx,v 1.2 2002-11-13 03:47:55 bwmott Exp $
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
#ifndef SOUNDDOS_HXX
|
#ifndef SOUNDDOS_HXX
|
||||||
#define SOUNDDOS_HXX
|
#define SOUNDDOS_HXX
|
||||||
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "Sound.hxx"
|
#include "MediaSrc.hxx"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This class implements the sound API for the DOS operating system
|
This class implements aa sound class for the DOS front-end. It supports
|
||||||
using a sound-blaster card.
|
SoundBlaster compatible sound cards.
|
||||||
|
|
||||||
@author Bradford W. Mott
|
@author Bradford W. Mott
|
||||||
@version $Id: SndDOS.hxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $
|
@version $Id: SndDOS.hxx,v 1.2 2002-11-13 03:47:55 bwmott Exp $
|
||||||
*/
|
*/
|
||||||
class SoundDOS : public Sound
|
class SoundDOS
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
Create a new sound object
|
Create a new sound object
|
||||||
*/
|
*/
|
||||||
SoundDOS();
|
SoundDOS(bool activate = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Destructor
|
Destructor
|
||||||
|
@ -44,23 +44,128 @@ class SoundDOS : public Sound
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
Set the value of the specified sound register
|
Closes the sound device
|
||||||
|
|
||||||
@param reg The sound register to set
|
|
||||||
@param val The new value for the sound registers
|
|
||||||
*/
|
*/
|
||||||
virtual void set(Sound::Register reg, uInt8 val);
|
void close();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Set the mute state of the sound object
|
Return the playback sample rate for the sound device.
|
||||||
|
|
||||||
@param state Mutes sound iff true
|
@return The playback sample rate
|
||||||
*/
|
*/
|
||||||
virtual void mute(bool state);
|
uInt32 getSampleRate() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return true iff the sound device was successfully initialized.
|
||||||
|
|
||||||
|
@return true iff the sound device was successfully initialized
|
||||||
|
*/
|
||||||
|
bool isSuccessfullyInitialized() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Set the mute state of the sound object.
|
||||||
|
|
||||||
|
@param state Mutes sound if true, unmute if false
|
||||||
|
*/
|
||||||
|
void mute(bool state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
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:
|
private:
|
||||||
// Indicates if the sound system was initialized
|
/**
|
||||||
bool myEnabled;
|
A bounded queue class used to hold audio samples after they are
|
||||||
|
produced by the MediaSource.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
volatile uInt32 mySize;
|
||||||
|
volatile uInt32 myHead;
|
||||||
|
volatile uInt32 myTail;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Current volume
|
||||||
|
uInt32 myCurrentVolume;
|
||||||
|
|
||||||
|
// SDL fragment size
|
||||||
|
uInt32 myFragmentSize;
|
||||||
|
|
||||||
|
// Indicates if the sound device was successfully initialized
|
||||||
|
bool myIsInitializedFlag;
|
||||||
|
|
||||||
|
// Mutex
|
||||||
|
bool myUpdateLock;
|
||||||
|
bool myCallbackLock;
|
||||||
|
|
||||||
|
// Indicates if the sound is currently muted
|
||||||
|
bool myIsMuted;
|
||||||
|
|
||||||
|
// DSP sample rate
|
||||||
|
uInt32 mySampleRate;
|
||||||
|
|
||||||
|
// Queue which holds samples from the media source before they are played
|
||||||
|
SampleQueue mySampleQueue;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Callback function invoked by the sound library when it needs data
|
||||||
|
static void callback(void* udata, void* stream, int len);
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
|
||||||
|
**
|
||||||
|
** This program is free software; you can redistribute it and/or
|
||||||
|
** modify it under the terms of version 2 of the GNU Library General
|
||||||
|
** Public License as published by the Free Software Foundation.
|
||||||
|
**
|
||||||
|
** This program is distributed in the hope that it will be useful,
|
||||||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
** Library General Public License for more details. To obtain a
|
||||||
|
** copy of the GNU Library General Public License, write to the Free
|
||||||
|
** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
**
|
||||||
|
** Any permitted reproduction of these routines, in whole or in part,
|
||||||
|
** must bear this legend.
|
||||||
|
**
|
||||||
|
** dos_sb.h
|
||||||
|
**
|
||||||
|
** DOS Sound Blaster header file
|
||||||
|
** $Id: dos_sb.h,v 1.1 2002-11-13 03:47:55 bwmott Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DOS_SB_H
|
||||||
|
#define DOS_SB_H
|
||||||
|
|
||||||
|
/* Thanks, Allegro! */
|
||||||
|
#define BPS_TO_TIMER(x) (1193182L / (long)(x))
|
||||||
|
#define END_OF_FUNCTION(x) void x##_end(void) {}
|
||||||
|
#define END_OF_STATIC_FUNCTION(x) static void x##_end(void) {}
|
||||||
|
#define LOCK_VARIABLE(x) _go32_dpmi_lock_data((void*)&x, sizeof(x))
|
||||||
|
#define LOCK_FUNCTION(x) _go32_dpmi_lock_code(x, (long)x##_end - (long)x)
|
||||||
|
|
||||||
|
#define DISABLE_INTS() __asm__ __volatile__ ("cli")
|
||||||
|
#define ENABLE_INTS() __asm__ __volatile__ ("sti")
|
||||||
|
|
||||||
|
typedef void (*sbmix_t)(void *userdata, void *buffer, int size);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int sb_init(int *sample_rate, int *bps, int *buf_size, int *stereo);
|
||||||
|
extern void sb_shutdown(void);
|
||||||
|
extern int sb_startoutput(sbmix_t fillbuf, void *userdata);
|
||||||
|
extern void sb_stopoutput(void);
|
||||||
|
extern void sb_setrate(int rate);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* DOS_SB_H */
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// 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: mainDOS.cxx,v 1.7 2002-04-28 18:06:56 bwmott Exp $
|
// $Id: mainDOS.cxx,v 1.8 2002-11-13 03:47:55 bwmott Exp $
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
#include <go32.h>
|
#include <go32.h>
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <strstream>
|
#include <sstream>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -848,6 +848,7 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
// Create a sound object for use with the console
|
// Create a sound object for use with the console
|
||||||
SoundDOS sound;
|
SoundDOS sound;
|
||||||
|
// sound.setSoundVolume(settings->theDesiredVolume);
|
||||||
|
|
||||||
// Get just the filename of the file containing the ROM image
|
// Get just the filename of the file containing the ROM image
|
||||||
const char* filename = (!strrchr(file, '\\')) ?
|
const char* filename = (!strrchr(file, '\\')) ?
|
||||||
|
@ -855,7 +856,7 @@ int main(int argc, char* argv[])
|
||||||
|
|
||||||
// Create the 2600 game console
|
// Create the 2600 game console
|
||||||
theConsole = new Console(image, size, filename,
|
theConsole = new Console(image, size, filename,
|
||||||
theEvent, propertiesSet, sound);
|
theEvent, propertiesSet, sound.getSampleRate());
|
||||||
|
|
||||||
// Free the image since we don't need it any longer
|
// Free the image since we don't need it any longer
|
||||||
delete[] image;
|
delete[] image;
|
||||||
|
@ -875,6 +876,7 @@ int main(int argc, char* argv[])
|
||||||
if(!thePauseIndicator)
|
if(!thePauseIndicator)
|
||||||
{
|
{
|
||||||
theConsole->mediaSource().update();
|
theConsole->mediaSource().update();
|
||||||
|
sound.updateSound(theConsole->mediaSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -912,6 +914,9 @@ int main(int argc, char* argv[])
|
||||||
// Get the ending time in case we need to print statistics
|
// Get the ending time in case we need to print statistics
|
||||||
uclock_t endingTime = uclock();
|
uclock_t endingTime = uclock();
|
||||||
|
|
||||||
|
// Close the sound device
|
||||||
|
sound.close();
|
||||||
|
|
||||||
uInt32 scanlines = theConsole->mediaSource().scanlines();
|
uInt32 scanlines = theConsole->mediaSource().scanlines();
|
||||||
string cartName = theConsole->properties().get("Cartridge.Name");
|
string cartName = theConsole->properties().get("Cartridge.Name");
|
||||||
string cartMD5 = theConsole->properties().get("Cartridge.MD5");
|
string cartMD5 = theConsole->properties().get("Cartridge.MD5");
|
||||||
|
|
|
@ -1,988 +0,0 @@
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: SBDRV */
|
|
||||||
/* Purpose: Sound Blaster DAC DMA Driver V1.3 */
|
|
||||||
/* Author(s): Ron Fries, Neil Bradley and Bradford Mott */
|
|
||||||
/* */
|
|
||||||
/* 02/20/97 - Initial Release */
|
|
||||||
/* */
|
|
||||||
/* 08/19/97 - V1.1 - Corrected problem with the auto-detect of older SB */
|
|
||||||
/* cards and problem with DSP shutdown which left the auto-init */
|
|
||||||
/* mode active. Required creating a function to reset the DSP. */
|
|
||||||
/* Also, added checks on the BLASTER settings to verify they */
|
|
||||||
/* are possible values for either SB or SB compatibles. */
|
|
||||||
/* Added several helpful information/error messages. These can */
|
|
||||||
/* be disabled by removing the SBDRV_SHOW_ERR definition. */
|
|
||||||
/* */
|
|
||||||
/* 12/24/97 - V1.2 - Added support for DJGPP (by Bradford Mott). */
|
|
||||||
/* */
|
|
||||||
/* 02/04/99 - V1.3 - Cleaned up DJGPP support, locking code and data. */
|
|
||||||
/* Fixed a bug with the reading of the BLASTER= environment */
|
|
||||||
/* variable, which caused a segfault if one was not set. Alst */
|
|
||||||
/* added a timeout to dsp_out() and some other minor */
|
|
||||||
/* modifications (Matthew Conte) */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* License Information and Copyright Notice */
|
|
||||||
/* ======================================== */
|
|
||||||
/* */
|
|
||||||
/* SBDrv is Copyright(c) 1997-1999 by Ron Fries, Neil Bradley and */
|
|
||||||
/* Bradford Mott */
|
|
||||||
/* */
|
|
||||||
/* This library is free software; you can redistribute it and/or modify it */
|
|
||||||
/* under the terms of version 2 of the GNU Library General Public License */
|
|
||||||
/* as published by the Free Software Foundation. */
|
|
||||||
/* */
|
|
||||||
/* This library is distributed in the hope that it will be useful, but */
|
|
||||||
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
|
|
||||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */
|
|
||||||
/* General Public License for more details. */
|
|
||||||
/* To obtain a copy of the GNU Library General Public License, write to the */
|
|
||||||
/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
||||||
/* */
|
|
||||||
/* Any permitted reproduction of these routines, in whole or in part, must */
|
|
||||||
/* bear this legend. */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef DJGPP
|
|
||||||
#include <go32.h>
|
|
||||||
#include <dpmi.h>
|
|
||||||
#include <sys/movedata.h>
|
|
||||||
|
|
||||||
/* Handy macros (care of Allegro) to lock code / data */
|
|
||||||
#define END_OF_FUNCTION(x) static void x##_end(void) {}
|
|
||||||
#define LOCK_VARIABLE(x) _go32_dpmi_lock_data((void*)&x,sizeof(x))
|
|
||||||
#define LOCK_FUNCTION(x) _go32_dpmi_lock_code(x,(long)x##_end-(long)x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <dos.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <conio.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "sbdrv.h"
|
|
||||||
|
|
||||||
#define DSP_RESET 0x06
|
|
||||||
#define DSP_READ 0x0a
|
|
||||||
#define DSP_WRITE 0x0c
|
|
||||||
#define DSP_ACK 0x0e
|
|
||||||
|
|
||||||
#define DMA_BASE 0x00
|
|
||||||
#define DMA_COUNT 0x01
|
|
||||||
#define DMA_MASK 0x0a
|
|
||||||
#define DMA_MODE 0x0b
|
|
||||||
#define DMA_FF 0x0c
|
|
||||||
|
|
||||||
#define MASTER_VOLUME 0x22
|
|
||||||
#define LINE_VOLUME 0x2e
|
|
||||||
#define FM_VOLUME 0x26
|
|
||||||
|
|
||||||
/* declare local global variables */
|
|
||||||
#ifdef DJGPP
|
|
||||||
static int theDOSBufferSegment;
|
|
||||||
static int theDOSBufferSelector;
|
|
||||||
#endif
|
|
||||||
static uint8 *Sb_buffer;
|
|
||||||
static uint16 Sb_buf_size = 200;
|
|
||||||
static uint16 Sb_offset;
|
|
||||||
static uint16 Playback_freq;
|
|
||||||
static uint8 Sb_init = 0;
|
|
||||||
static uint8 Count_low;
|
|
||||||
static uint8 Count_high;
|
|
||||||
|
|
||||||
static uint16 IOaddr = 0x220;
|
|
||||||
static uint16 Irq = 7;
|
|
||||||
static uint16 Dma = 1;
|
|
||||||
static uint8 DMAmode = AUTO_DMA;
|
|
||||||
/*static*/ uint8 DMAcount;
|
|
||||||
static void (*FillBuffer)(uint8 *buf, uint16 buf_size);
|
|
||||||
|
|
||||||
#ifdef DJGPP
|
|
||||||
static _go32_dpmi_seginfo OldIntVectInfo;
|
|
||||||
static _go32_dpmi_seginfo NewIntVectInfo;
|
|
||||||
#else
|
|
||||||
static void (__interrupt *OldIntVect)(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* function prototypes */
|
|
||||||
static void setNewIntVect (uint16 irq);
|
|
||||||
static void setOldIntVect (uint16 irq);
|
|
||||||
static void dsp_out (uint16 port, uint8 val);
|
|
||||||
static uint8 hextodec (char c);
|
|
||||||
static void logErr (char *st);
|
|
||||||
static uint8 getBlasterEnv (void);
|
|
||||||
static uint8 dsp_in (uint16 port);
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: newIntVect */
|
|
||||||
/* Purpose: The interrupt vector to handle the DAC DMAC completed interrupt */
|
|
||||||
/* Sends the next buffer to the SB and re-fills the current buffer. */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: January 1, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
#ifdef DJGPP
|
|
||||||
static void newIntVect(void)
|
|
||||||
#else
|
|
||||||
static void interrupt newIntVect (void)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
uint16 addr;
|
|
||||||
|
|
||||||
if (DMAmode == STANDARD_DMA)
|
|
||||||
{
|
|
||||||
/* restart standard DMA transfer */
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, 0x14);
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, Count_low);
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, Count_high);
|
|
||||||
}
|
|
||||||
|
|
||||||
DMAcount++;
|
|
||||||
|
|
||||||
/* acknowledge the DSP interrupt */
|
|
||||||
inp (IOaddr + DSP_ACK);
|
|
||||||
|
|
||||||
/* determine the current playback position */
|
|
||||||
addr = inp (DMA_BASE + (Dma << 1)); /* get low byte ptr */
|
|
||||||
addr |= inp (DMA_BASE + (Dma << 1)) << 8; /* and high byte ptr */
|
|
||||||
|
|
||||||
addr -= Sb_offset; /* subtract the offset */
|
|
||||||
|
|
||||||
/* if we're currently playing the first half of the buffer */
|
|
||||||
if (addr < Sb_buf_size)
|
|
||||||
{
|
|
||||||
/* reload the second half of the buffer */
|
|
||||||
FillBuffer(Sb_buffer + Sb_buf_size, Sb_buf_size);
|
|
||||||
|
|
||||||
#ifdef DJGPP
|
|
||||||
/* Copy data to DOS memory buffer */
|
|
||||||
dosmemput(Sb_buffer + Sb_buf_size, Sb_buf_size,
|
|
||||||
(theDOSBufferSegment << 4) + Sb_buf_size);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* else reload the first half of the buffer */
|
|
||||||
FillBuffer(Sb_buffer, Sb_buf_size);
|
|
||||||
|
|
||||||
#ifdef DJGPP
|
|
||||||
/* Copy data to DOS memory buffer */
|
|
||||||
dosmemput(Sb_buffer, Sb_buf_size, theDOSBufferSegment << 4);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* indicate end of interrupt */
|
|
||||||
outp (0x20, 0x20);
|
|
||||||
|
|
||||||
if (Irq > 7)
|
|
||||||
{
|
|
||||||
outp (0xa0, 0x20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef DJGPP
|
|
||||||
END_OF_FUNCTION(newIntVect);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: setNewIntVect */
|
|
||||||
/* Purpose: To set the specified interrupt vector to the sound output */
|
|
||||||
/* processing interrupt. */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: January 1, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
static void setNewIntVect (uint16 irq)
|
|
||||||
{
|
|
||||||
#ifdef DJGPP
|
|
||||||
/* Lock code / data */
|
|
||||||
LOCK_VARIABLE(DMAmode);
|
|
||||||
LOCK_VARIABLE(IOaddr);
|
|
||||||
LOCK_VARIABLE(Count_low);
|
|
||||||
LOCK_VARIABLE(DMAcount);
|
|
||||||
LOCK_VARIABLE(Dma);
|
|
||||||
LOCK_VARIABLE(Sb_offset);
|
|
||||||
LOCK_VARIABLE(Sb_buf_size);
|
|
||||||
LOCK_VARIABLE(theDOSBufferSegment);
|
|
||||||
LOCK_FUNCTION(newIntVect);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (irq > 7)
|
|
||||||
{
|
|
||||||
#ifdef DJGPP
|
|
||||||
_go32_dpmi_get_protected_mode_interrupt_vector(irq + 0x68,
|
|
||||||
&OldIntVectInfo);
|
|
||||||
NewIntVectInfo.pm_selector = _my_cs();
|
|
||||||
NewIntVectInfo.pm_offset = (int)newIntVect;
|
|
||||||
_go32_dpmi_allocate_iret_wrapper(&NewIntVectInfo);
|
|
||||||
_go32_dpmi_set_protected_mode_interrupt_vector(irq + 0x68,
|
|
||||||
&NewIntVectInfo);
|
|
||||||
#else
|
|
||||||
OldIntVect = _dos_getvect (irq + 0x68);
|
|
||||||
_dos_setvect (irq + 0x68, newIntVect);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#ifdef DJGPP
|
|
||||||
_go32_dpmi_get_protected_mode_interrupt_vector(irq + 0x08,
|
|
||||||
&OldIntVectInfo);
|
|
||||||
NewIntVectInfo.pm_selector = _my_cs();
|
|
||||||
NewIntVectInfo.pm_offset = (int)newIntVect;
|
|
||||||
_go32_dpmi_allocate_iret_wrapper(&NewIntVectInfo);
|
|
||||||
_go32_dpmi_set_protected_mode_interrupt_vector(irq + 0x08,
|
|
||||||
&NewIntVectInfo);
|
|
||||||
#else
|
|
||||||
OldIntVect = _dos_getvect (irq + 0x08);
|
|
||||||
_dos_setvect (irq + 0x08, newIntVect);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: setOldIntVect */
|
|
||||||
/* Purpose: To restore the original vector */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: January 1, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
static void setOldIntVect (uint16 irq)
|
|
||||||
{
|
|
||||||
if (irq > 7)
|
|
||||||
{
|
|
||||||
#ifdef DJGPP
|
|
||||||
_go32_dpmi_set_protected_mode_interrupt_vector(irq + 0x68,
|
|
||||||
&OldIntVectInfo);
|
|
||||||
_go32_dpmi_free_iret_wrapper(&NewIntVectInfo);
|
|
||||||
#else
|
|
||||||
_dos_setvect (irq + 0x68, OldIntVect);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#ifdef DJGPP
|
|
||||||
_go32_dpmi_set_protected_mode_interrupt_vector(irq + 0x08,
|
|
||||||
&OldIntVectInfo);
|
|
||||||
_go32_dpmi_free_iret_wrapper(&NewIntVectInfo);
|
|
||||||
#else
|
|
||||||
_dos_setvect (irq + 0x08, OldIntVect);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: dsp_out */
|
|
||||||
/* Purpose: To send a byte to the SB's DSP */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: September 10, 1996 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
static void dsp_out(uint16 port, uint8 val)
|
|
||||||
{
|
|
||||||
uint32 timeout = 60000; /* set timeout */
|
|
||||||
|
|
||||||
/* wait for buffer to be free */
|
|
||||||
while((timeout--) && (inp(IOaddr + DSP_WRITE) & 0x80))
|
|
||||||
{
|
|
||||||
/* do nothing */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* transmit the next byte */
|
|
||||||
outp(port,val);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: dsp_in */
|
|
||||||
/* Purpose: To read a byte from the SB's DSP */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: January 26, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
static uint8 dsp_in(uint16 port)
|
|
||||||
{
|
|
||||||
uint16 x=10000; /* set timeout */
|
|
||||||
|
|
||||||
/* wait for buffer to be free */
|
|
||||||
while(((inp(IOaddr + 0x0E) & 0x80) == 0) && (x>0))
|
|
||||||
{
|
|
||||||
/* decrement the timeout */
|
|
||||||
x--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x>0)
|
|
||||||
{
|
|
||||||
/* read the data byte */
|
|
||||||
return(inp(port));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: hextodec */
|
|
||||||
/* Purpose: Convert the input character to hex */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: September 10, 1996 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
uint8 hextodec (char c)
|
|
||||||
{
|
|
||||||
uint8 retval = 0;
|
|
||||||
|
|
||||||
c = toupper (c);
|
|
||||||
|
|
||||||
if ((c>='0') && (c<='9'))
|
|
||||||
{
|
|
||||||
retval = c - '0';
|
|
||||||
}
|
|
||||||
else if ((c>='A') && (c<='F'))
|
|
||||||
{
|
|
||||||
retval = c - 'A' + 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: logErr */
|
|
||||||
/* Purpose: Displays an error message. */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: September 24, 1996 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
static void logErr (char *st)
|
|
||||||
{
|
|
||||||
#ifdef SBDRV_SHOW_ERR
|
|
||||||
printf ("%s",st);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: getBlasterEnv */
|
|
||||||
/* Purpose: Read the BLASTER environment variable and set the local globals */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: September 10, 1996 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
static uint8 getBlasterEnv (void)
|
|
||||||
{
|
|
||||||
char *env;
|
|
||||||
char *ptr;
|
|
||||||
uint16 count = 0;
|
|
||||||
|
|
||||||
env = getenv("BLASTER");
|
|
||||||
|
|
||||||
/* if the environment variable exists */
|
|
||||||
if (env)
|
|
||||||
{
|
|
||||||
strupr(env);
|
|
||||||
|
|
||||||
/* search for the address setting */
|
|
||||||
ptr = strchr(env, 'A');
|
|
||||||
if (ptr)
|
|
||||||
{
|
|
||||||
/* if valid, read and convert the IO address */
|
|
||||||
IOaddr = (hextodec (ptr[1]) << 8) +
|
|
||||||
(hextodec (ptr[2]) << 4) +
|
|
||||||
(hextodec (ptr[3]));
|
|
||||||
|
|
||||||
/* verify the IO address is one of the possible SB settings */
|
|
||||||
switch (IOaddr)
|
|
||||||
{
|
|
||||||
case 0x210:
|
|
||||||
case 0x220:
|
|
||||||
case 0x230:
|
|
||||||
case 0x240:
|
|
||||||
case 0x250:
|
|
||||||
case 0x260:
|
|
||||||
case 0x280:
|
|
||||||
case 0x2A0:
|
|
||||||
case 0x2C0:
|
|
||||||
case 0x2E0:
|
|
||||||
/* IO address OK so indicate one more valid item found */
|
|
||||||
count++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
logErr ("Invalid Sound Blaster I/O address specified.\n");
|
|
||||||
logErr ("Possible values are: ");
|
|
||||||
logErr ("210, 220, 230, 240, 250, 260, 280, 2A0, 2C0, 2E0.\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logErr ("Unable to read Sound Blaster I/O address.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* search for the IRQ setting */
|
|
||||||
ptr = strchr(env, 'I');
|
|
||||||
if (ptr)
|
|
||||||
{
|
|
||||||
/* if valid, read and convert the IRQ */
|
|
||||||
/* if the IRQ has two digits */
|
|
||||||
if ((ptr[1] == '1') && ((ptr[2] >= '0') && (ptr[2] <='5')))
|
|
||||||
{
|
|
||||||
/* then convert accordingly (using decimal) */
|
|
||||||
Irq = hextodec (ptr[1]) * 10 + hextodec (ptr[2]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* else convert as a single hex digit */
|
|
||||||
Irq = hextodec (ptr[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* verify the IRQ setting is one of the possible SB settings */
|
|
||||||
switch (Irq)
|
|
||||||
{
|
|
||||||
case 2: /* two is actually the interrupt cascade for IRQs > 7 */
|
|
||||||
/* IRQ nine is the cascase for 2 */
|
|
||||||
Irq = 9;
|
|
||||||
|
|
||||||
/* IRQ OK so indicate one more valid item found */
|
|
||||||
count++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
case 5:
|
|
||||||
case 7:
|
|
||||||
case 9:
|
|
||||||
case 10:
|
|
||||||
case 11:
|
|
||||||
case 12:
|
|
||||||
case 15:
|
|
||||||
|
|
||||||
/* IRQ OK so indicate one more valid item found */
|
|
||||||
count++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
logErr ("Invalid Sound Blaster IRQ specified.\n");
|
|
||||||
logErr ("Possible values are: ");
|
|
||||||
logErr ("2, 3, 4, 5, 7, 9, 10, 11, 12, 15.\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logErr ("Unable to read Sound Blaster IRQ.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* search for the DMA setting */
|
|
||||||
ptr = strchr(env, 'D');
|
|
||||||
if (ptr)
|
|
||||||
{
|
|
||||||
/* if valid, read and convert the DMA */
|
|
||||||
Dma = hextodec (ptr[1]);
|
|
||||||
|
|
||||||
/* verify the DMA setting is one of the possible 8-bit SB settings */
|
|
||||||
switch (Dma)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
case 3:
|
|
||||||
/* DMA OK so indicate one more valid item found */
|
|
||||||
count++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
logErr ("Invalid Sound Blaster 8-bit DMA specified.\n");
|
|
||||||
logErr ("Possible values are: ");
|
|
||||||
logErr ("0, 1, 3.\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logErr ("Unable to read Sound Blaster DMA setting.\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logErr ("BLASTER enviroment variable not configured.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (count != 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: low_malloc */
|
|
||||||
/* Purpose: To allocate memory in the first 640K of memory */
|
|
||||||
/* Author: Neil Bradley */
|
|
||||||
/* Date: December 16, 1996 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef __WATCOMC__
|
|
||||||
void dos_memalloc(unsigned short int para, unsigned short int *seg, unsigned short int *sel);
|
|
||||||
#pragma aux dos_memalloc = \
|
|
||||||
"push ecx" \
|
|
||||||
"push edx" \
|
|
||||||
"mov ax, 0100h" \
|
|
||||||
"int 31h" \
|
|
||||||
"pop ebx" \
|
|
||||||
"mov [ebx], dx" \
|
|
||||||
"pop ebx" \
|
|
||||||
"mov [ebx], ax" \
|
|
||||||
parm [bx] [ecx] [edx] \
|
|
||||||
modify [ax ebx ecx edx];
|
|
||||||
|
|
||||||
void dos_memfree(short int sel);
|
|
||||||
#pragma aux dos_memfree = \
|
|
||||||
"mov ax, 0101h" \
|
|
||||||
"int 31h" \
|
|
||||||
parm [dx] \
|
|
||||||
modify [ax dx];
|
|
||||||
|
|
||||||
void *low_malloc(int size)
|
|
||||||
{
|
|
||||||
unsigned short int seg;
|
|
||||||
unsigned short int i=0;
|
|
||||||
|
|
||||||
dos_memalloc((size >> 4) + 1, &seg, &i);
|
|
||||||
return((char *)(seg << 4));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: Set_master_volume */
|
|
||||||
/* Purpose: To set the Sound Blaster's master volume */
|
|
||||||
/* Author: Neil Bradley */
|
|
||||||
/* Date: January 1, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
void Set_master_volume(uint8 left, uint8 right)
|
|
||||||
|
|
||||||
{
|
|
||||||
/* if the SB was initialized */
|
|
||||||
if (Sb_init)
|
|
||||||
{
|
|
||||||
outp(IOaddr + 0x04, MASTER_VOLUME);
|
|
||||||
outp(IOaddr + 0x05, ((left & 0xf) << 4) + (right & 0x0f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: Set_line_volume */
|
|
||||||
/* Purpose: To set the Sound Blaster's line level volume */
|
|
||||||
/* Author: Neil Bradley */
|
|
||||||
/* Date: January 1, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
void Set_line_volume(uint8 left, uint8 right)
|
|
||||||
{
|
|
||||||
/* if the SB was initialized */
|
|
||||||
if (Sb_init)
|
|
||||||
{
|
|
||||||
outp(IOaddr + 0x04, LINE_VOLUME);
|
|
||||||
outp(IOaddr + 0x05, ((left & 0xf) << 4) + (right & 0x0f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: Set_FM_volume */
|
|
||||||
/* Purpose: To set the Sound Blaster's FM volume */
|
|
||||||
/* Author: Neil Bradley */
|
|
||||||
/* Date: January 1, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
void Set_FM_volume(uint8 left, uint8 right)
|
|
||||||
|
|
||||||
{
|
|
||||||
/* if the SB was initialized */
|
|
||||||
if (Sb_init)
|
|
||||||
{
|
|
||||||
outp(IOaddr + 0x04, FM_VOLUME);
|
|
||||||
outp(IOaddr + 0x05, ((left & 0xf) << 4) + (right & 0x0f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: ResetDSP */
|
|
||||||
/* Purpose: To reset the SB DSP. Returns a value of zero if unsuccessful. */
|
|
||||||
/* This function requires as input the SB base port address. */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: August 5, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
uint8 ResetDSP(uint16 ioaddr)
|
|
||||||
{
|
|
||||||
uint8 x;
|
|
||||||
uint16 y;
|
|
||||||
|
|
||||||
/* assume the init was not successful */
|
|
||||||
Sb_init = 0;
|
|
||||||
|
|
||||||
/* send a DSP reset to the SB */
|
|
||||||
outp (ioaddr + DSP_RESET, 1);
|
|
||||||
|
|
||||||
/* wait a few microsec */
|
|
||||||
x = inp(ioaddr + DSP_RESET);
|
|
||||||
x = inp(ioaddr + DSP_RESET);
|
|
||||||
x = inp(ioaddr + DSP_RESET);
|
|
||||||
x = inp(ioaddr + DSP_RESET);
|
|
||||||
|
|
||||||
/* clear the DSP reset */
|
|
||||||
outp (ioaddr + DSP_RESET,0);
|
|
||||||
|
|
||||||
/* wait a bit until the SB indicates good status */
|
|
||||||
y = 0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
x = inp (ioaddr + DSP_READ);
|
|
||||||
y++;
|
|
||||||
} while ((y < 1000) && (x != 0xaa));
|
|
||||||
|
|
||||||
/* if we were able to successfully reset the SB */
|
|
||||||
if (x == 0xaa)
|
|
||||||
{
|
|
||||||
/* turn on speaker */
|
|
||||||
dsp_out (ioaddr + DSP_WRITE, 0xd1);
|
|
||||||
|
|
||||||
/* read to make sure DSP register is clear */
|
|
||||||
dsp_in (ioaddr + DSP_READ);
|
|
||||||
|
|
||||||
/* set time constant */
|
|
||||||
dsp_out (ioaddr + DSP_WRITE, 0x40);
|
|
||||||
dsp_out (ioaddr + DSP_WRITE,
|
|
||||||
(unsigned char)(256 - 1000000L/Playback_freq));
|
|
||||||
|
|
||||||
/* indicate successful initialization */
|
|
||||||
Sb_init = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Sb_init);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: OpenSB */
|
|
||||||
/* Purpose: To reset the SB and prepare all buffers and other global */
|
|
||||||
/* global variables for sound output. Allows the user to select */
|
|
||||||
/* the playback frequency, number of buffers, and size of each */
|
|
||||||
/* buffer. Returns a value of zero if unsuccessful. */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: January 1, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
uint8 OpenSB(uint16 playback_freq, uint16 buffer_size)
|
|
||||||
{
|
|
||||||
/* initialize local globals */
|
|
||||||
if (buffer_size > 0)
|
|
||||||
{
|
|
||||||
Sb_buf_size = buffer_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
Playback_freq = playback_freq;
|
|
||||||
|
|
||||||
/* assume the init was not successful */
|
|
||||||
Sb_init = 0;
|
|
||||||
|
|
||||||
/* attempt to read the Blaster Environment Variable */
|
|
||||||
if (getBlasterEnv() == 0)
|
|
||||||
{
|
|
||||||
/* if the DSP could be successfully reset */
|
|
||||||
if (ResetDSP(IOaddr) != 0 )
|
|
||||||
{
|
|
||||||
/* setup the DSP interrupt service routine */
|
|
||||||
setNewIntVect(Irq);
|
|
||||||
|
|
||||||
/* Enable the interrupt used */
|
|
||||||
if (Irq > 7)
|
|
||||||
{
|
|
||||||
outp (0xa1,inp(0xa1) & (~(1<<(Irq-8))));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
outp (0x21,inp(0x21) & (~(1<<Irq)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* make sure interrupts are enabled */
|
|
||||||
_enable();
|
|
||||||
|
|
||||||
/* create a buffer to hold the data */
|
|
||||||
#ifdef __WATCOMC__
|
|
||||||
Sb_buffer = low_malloc (Sb_buf_size*2);
|
|
||||||
#elif defined(DJGPP)
|
|
||||||
Sb_buffer = (uint8 *)malloc(Sb_buf_size*2);
|
|
||||||
theDOSBufferSegment = __dpmi_allocate_dos_memory((Sb_buf_size*2+15) >> 4,
|
|
||||||
&theDOSBufferSelector);
|
|
||||||
#else
|
|
||||||
Sb_buffer = malloc (Sb_buf_size*2);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* if we were unable to successfully allocate the buffer */
|
|
||||||
#ifdef DJGPP
|
|
||||||
if ((Sb_buffer == 0) || (theDOSBufferSegment == -1))
|
|
||||||
#else
|
|
||||||
if (Sb_buffer == 0)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
logErr ("Unable to allocate buffer for audio output.\n");
|
|
||||||
|
|
||||||
/* close the SB */
|
|
||||||
CloseSB();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logErr ("Unable to initialize the Sound Card.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Sb_init);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: CloseSB */
|
|
||||||
/* Purpose: Closes the SB and disables the interrupts. */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: January 1, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
void CloseSB(void)
|
|
||||||
{
|
|
||||||
#ifdef __WATCOMC__
|
|
||||||
uint32 addr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* if the SB was initialized */
|
|
||||||
if (Sb_init)
|
|
||||||
{
|
|
||||||
/* stop all DMA transfer */
|
|
||||||
Stop_audio_output();
|
|
||||||
ResetDSP(IOaddr);
|
|
||||||
|
|
||||||
/* turn the speaker off */
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, 0xd3);
|
|
||||||
|
|
||||||
/* indicate SB no longer active */
|
|
||||||
Sb_init = 0;
|
|
||||||
|
|
||||||
/* Disable the interrupt used */
|
|
||||||
if (Irq > 7)
|
|
||||||
{
|
|
||||||
outp (0xa1,inp(0xa1) | (1<<(Irq-8)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
outp (0x21,inp(0x21) | (1<<Irq));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* restore the original interrupt routine */
|
|
||||||
setOldIntVect(Irq);
|
|
||||||
|
|
||||||
/* free any memory that had been allocated */
|
|
||||||
if (Sb_buffer != 0)
|
|
||||||
{
|
|
||||||
#ifdef __WATCOMC__
|
|
||||||
addr = (uint32) Sb_buffer;
|
|
||||||
dos_memfree((uint16)(addr >> 4));
|
|
||||||
#elif defined(DJGPP)
|
|
||||||
free(Sb_buffer);
|
|
||||||
__dpmi_free_dos_memory(theDOSBufferSelector);
|
|
||||||
#else
|
|
||||||
free (Sb_buffer);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: Stop_audio_output */
|
|
||||||
/* Purpose: Stops the SB's DMA transfer. */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: January 17, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
void Stop_audio_output (void)
|
|
||||||
{
|
|
||||||
/* stop any transfer that may be in progress */
|
|
||||||
|
|
||||||
/* if the SB was initialized */
|
|
||||||
if (Sb_init)
|
|
||||||
{
|
|
||||||
/* halt DMA */
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, 0xd0);
|
|
||||||
|
|
||||||
/* exit DMA operation*/
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, 0xda);
|
|
||||||
|
|
||||||
/* halt DMA */
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, 0xd0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: Start_audio_output */
|
|
||||||
/* Purpose: Fills all configured buffers and outputs the first. */
|
|
||||||
/* Author: Ron Fries */
|
|
||||||
/* Date: February 20, 1997 */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
uint8 Start_audio_output (uint8 dma_mode,
|
|
||||||
void (*fillBuffer)(uint8 *buf,uint16 n))
|
|
||||||
{
|
|
||||||
uint8 ret_val = 1;
|
|
||||||
static uint8 pagePort[8] = { 0x87, 0x83, 0x81, 0x82 };
|
|
||||||
uint8 offset_low;
|
|
||||||
uint8 offset_high;
|
|
||||||
uint8 page_no;
|
|
||||||
uint8 count_low;
|
|
||||||
uint8 count_high;
|
|
||||||
uint32 addr;
|
|
||||||
clock_t start_time;
|
|
||||||
|
|
||||||
/* if the SB initialized properly */
|
|
||||||
if (Sb_init)
|
|
||||||
{
|
|
||||||
/* set the fill buffer routine */
|
|
||||||
FillBuffer = fillBuffer;
|
|
||||||
|
|
||||||
/* keep track of the DMA selection */
|
|
||||||
DMAmode = dma_mode;
|
|
||||||
|
|
||||||
/* stop any transfer that may be in progress */
|
|
||||||
Stop_audio_output();
|
|
||||||
|
|
||||||
/* fill the buffer */
|
|
||||||
FillBuffer (Sb_buffer, Sb_buf_size*2);
|
|
||||||
|
|
||||||
#ifdef DJGPP
|
|
||||||
/* Copy data to DOS memory buffer */
|
|
||||||
dosmemput(Sb_buffer, Sb_buf_size * 2, theDOSBufferSegment << 4);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* calculate high, low and page addresses of buffer */
|
|
||||||
#ifdef __WATCOMC__
|
|
||||||
addr = (uint32) Sb_buffer;
|
|
||||||
#elif defined(DJGPP)
|
|
||||||
addr = ((uint32) theDOSBufferSegment) << 4;
|
|
||||||
#else
|
|
||||||
addr = ((uint32)FP_SEG(Sb_buffer) << 4) +
|
|
||||||
(uint32)FP_OFF(Sb_buffer);
|
|
||||||
#endif
|
|
||||||
Sb_offset = (uint16)(addr & 0x0ffff);
|
|
||||||
offset_low = (uint8)(addr & 0x0ff);
|
|
||||||
offset_high = (uint8)((addr >> 8) & 0x0ff);
|
|
||||||
page_no = (uint8)(addr >> 16);
|
|
||||||
|
|
||||||
count_low = (uint8) ((Sb_buf_size*2)-1) & 0x0ff;
|
|
||||||
count_high = (uint8) (((Sb_buf_size*2)-1) >> 8) & 0x0ff;
|
|
||||||
|
|
||||||
/* program the DMAC for output transfer */
|
|
||||||
outp (DMA_MASK , 0x04 | Dma );
|
|
||||||
outp (DMA_FF , 0 );
|
|
||||||
|
|
||||||
/* select auto-initialize DMA mode */
|
|
||||||
outp (DMA_MODE , 0x58 | Dma );
|
|
||||||
outp (DMA_BASE + (Dma << 1) , offset_low );
|
|
||||||
outp (DMA_BASE + (Dma << 1) , offset_high );
|
|
||||||
outp (pagePort[Dma] , page_no );
|
|
||||||
outp (DMA_COUNT + (Dma << 1), count_low );
|
|
||||||
outp (DMA_COUNT + (Dma << 1), count_high );
|
|
||||||
outp (DMA_MASK , Dma );
|
|
||||||
|
|
||||||
/* calculate the high/low buffer size counts */
|
|
||||||
Count_low = (uint8) (Sb_buf_size-1) & 0x0ff;
|
|
||||||
Count_high = (uint8) ((Sb_buf_size-1) >> 8) & 0x0ff;
|
|
||||||
|
|
||||||
if (DMAmode == STANDARD_DMA)
|
|
||||||
{
|
|
||||||
/* start the standard DMA transfer */
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, 0x14);
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, Count_low);
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, Count_high);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* reset the DMA counter */
|
|
||||||
DMAcount = 0;
|
|
||||||
|
|
||||||
/* set the auto-initialize buffer size */
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, 0x48);
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, Count_low);
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, Count_high);
|
|
||||||
|
|
||||||
/* and start the auto-initialize DMA transfer */
|
|
||||||
dsp_out (IOaddr + DSP_WRITE, 0x1c);
|
|
||||||
|
|
||||||
start_time = clock();
|
|
||||||
|
|
||||||
/* Delay for a bit and wait for DMAcount to change. */
|
|
||||||
/* Wait for the DMA to be called twice to make sure */
|
|
||||||
/* auto-init mode is working properly. */
|
|
||||||
while ((clock()-start_time < (int)(CLK_TCK/2)) && (DMAcount < 2))
|
|
||||||
{
|
|
||||||
/* This routine will wait for up to 1/2 second for DMAcount */
|
|
||||||
/* to change. The value in CLK_TCK is the number of times */
|
|
||||||
/* the clock will tick in one second. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if the auto-init DMA is not active */
|
|
||||||
if (DMAcount < 2)
|
|
||||||
{
|
|
||||||
/* Reset the SB DSP */
|
|
||||||
ResetDSP(IOaddr);
|
|
||||||
|
|
||||||
/* then try again with STANDARD_DMA */
|
|
||||||
Start_audio_output (STANDARD_DMA, fillBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret_val = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ret_val);
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* Module: SBDRV.H */
|
|
||||||
/* Purpose: Define function prototypes and structures required for the */
|
|
||||||
/* SB DRV routines, V1.3. */
|
|
||||||
/* Author(s): Ron Fries and Neil Bradley */
|
|
||||||
/* */
|
|
||||||
/* 01/30/97 - Initial Release */
|
|
||||||
/* 08/24/97 - V1.1 - Added defintion of SBDRV_SHOW_ERR to cause the SBDRV */
|
|
||||||
/* to display error messages. Comment line to supress */
|
|
||||||
/* 01/12/98 - V1.2 - Added support for DJGPP. */
|
|
||||||
/* 02/04/99 - V1.3 - Cleaned up DJGPP support, fixed a possible segfault */
|
|
||||||
/* in the reading of the BLASTER= env. variable, and */
|
|
||||||
/* added timeout to dsp_out(). */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* */
|
|
||||||
/* License Information and Copyright Notice */
|
|
||||||
/* ======================================== */
|
|
||||||
/* */
|
|
||||||
/* SBDrv is Copyright(c) 1997-1999 by Ron Fries, Neil Bradley and */
|
|
||||||
/* Bradford Mott */
|
|
||||||
/* */
|
|
||||||
/* This library is free software; you can redistribute it and/or modify it */
|
|
||||||
/* under the terms of version 2 of the GNU Library General Public License */
|
|
||||||
/* as published by the Free Software Foundation. */
|
|
||||||
/* */
|
|
||||||
/* This library is distributed in the hope that it will be useful, but */
|
|
||||||
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
|
|
||||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */
|
|
||||||
/* General Public License for more details. */
|
|
||||||
/* To obtain a copy of the GNU Library General Public License, write to the */
|
|
||||||
/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
||||||
/* */
|
|
||||||
/* Any permitted reproduction of these routines, in whole or in part, must */
|
|
||||||
/* bear this legend. */
|
|
||||||
/* */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef _SBDRV_H
|
|
||||||
#define _SBDRV_H
|
|
||||||
|
|
||||||
#ifndef _TYPEDEF_H
|
|
||||||
#define _TYPEDEF_H
|
|
||||||
|
|
||||||
#define SBDRV_SHOW_ERR /* delete line to supress error message printing */
|
|
||||||
|
|
||||||
/* define some data types to keep it platform independent */
|
|
||||||
#ifdef COMP16 /* if 16-bit compiler defined */
|
|
||||||
#define int8 char
|
|
||||||
#define int16 int
|
|
||||||
#define int32 long
|
|
||||||
#else /* else default to 32-bit compiler */
|
|
||||||
#define int8 char
|
|
||||||
#define int16 short
|
|
||||||
#define int32 int
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define uint8 unsigned int8
|
|
||||||
#define uint16 unsigned int16
|
|
||||||
#define uint32 unsigned int32
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* CONSTANT DEFINITIONS */
|
|
||||||
|
|
||||||
#define AUTO_DMA 0 /* selects auto-initialize DMA mode */
|
|
||||||
#define STANDARD_DMA 1 /* selects standard DMA mode */
|
|
||||||
|
|
||||||
/* global function prototypes */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint8 OpenSB(uint16 playback_freq, uint16 buffer_size);
|
|
||||||
void CloseSB(void);
|
|
||||||
uint8 Start_audio_output (uint8 dma_mode,
|
|
||||||
void (*fillBuffer)(uint8 *buf,uint16 n));
|
|
||||||
void Stop_audio_output (void);
|
|
||||||
|
|
||||||
void Set_master_volume(uint8 left, uint8 right);
|
|
||||||
void Set_line_volume(uint8 left, uint8 right);
|
|
||||||
void Set_FM_volume(uint8 left, uint8 right);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,218 +0,0 @@
|
||||||
SBDRV.C - SB DRIVER FUNCTIONS V1.3
|
|
||||||
==================================
|
|
||||||
|
|
||||||
NOTE: There is some important information in the COMPILER REQUIREMENTS
|
|
||||||
section.
|
|
||||||
|
|
||||||
The SBDRV.C file includes a set of simple routines for generating audio
|
|
||||||
output through a basic SB card, which should include all SB compatible cards.
|
|
||||||
The routines are designed to use the low-speed DMA output feature of the SB
|
|
||||||
card. The routines will automatically create the necessary buffers. The
|
|
||||||
audio processing/mixing function will automatically be called from the ISR
|
|
||||||
to provide a continuous stream of output.
|
|
||||||
|
|
||||||
These functions have been designed to work with multiple compilers.
|
|
||||||
They have already been tested with Borland C++ V3.1, MSVC V1.52,
|
|
||||||
WATCOM V10.6 and DJGPP. The Borland and MSVC compilers used were 16-bit,
|
|
||||||
while the WATCOM and DJGPP were 32-bit.
|
|
||||||
|
|
||||||
A header file 'SBDRV.H' has been included for use in other modules, and
|
|
||||||
provides the necessary function prototypes.
|
|
||||||
|
|
||||||
The routines are easy to use. Detailed descriptions on the function calls
|
|
||||||
are listed below.
|
|
||||||
|
|
||||||
|
|
||||||
FEATURES
|
|
||||||
--------
|
|
||||||
|
|
||||||
1) Full support for IRQs 0-15.
|
|
||||||
|
|
||||||
2) Ability to adjust the Master, FM and VOC/WAV volumes (note: not supported
|
|
||||||
by all cards).
|
|
||||||
|
|
||||||
2) Once started, the the sound IRQ automatically calls the audio processing
|
|
||||||
function. No additional calls are required.
|
|
||||||
|
|
||||||
3) The audio processing/mixing function is passed in as a initialization
|
|
||||||
parameter. This should allow the routines to be used in most applications
|
|
||||||
without modification.
|
|
||||||
|
|
||||||
4) The routines support AUTO-INITIALIZE and STANDARD DMA modes of operation.
|
|
||||||
This will allow good sound reproduction on the newer cards but still
|
|
||||||
support the old cards.
|
|
||||||
|
|
||||||
|
|
||||||
LIMITATIONS
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The SB Driver routines have several limitations.
|
|
||||||
|
|
||||||
1) The routines can support any DMA channel from 0 to 3. If any other DMA
|
|
||||||
channel is needed, changes are required. This may need to be changed to
|
|
||||||
support all SB cards. This should not be much of a concern, though,
|
|
||||||
since DMA channels 4-7 are only used for 16-bit DMA operation.
|
|
||||||
|
|
||||||
2) The routines only support the low-speed DMA output function of the SB
|
|
||||||
card, which limits the maximum playback to approximately 22KHz.
|
|
||||||
|
|
||||||
3) The routines only support 8-bit mono audio output.
|
|
||||||
|
|
||||||
4) The routines require that the 'BLASTER' environment variable be configured.
|
|
||||||
This can be accomplished by using "SET BLASTER = A220 I7 D1" from the
|
|
||||||
command line. The 'A' value is the base address of the card, the 'I' value
|
|
||||||
is the configured interrupt line, and the 'D' value is the configured DMA
|
|
||||||
channel.
|
|
||||||
|
|
||||||
|
|
||||||
GENERAL OVERVIEW
|
|
||||||
----------------
|
|
||||||
|
|
||||||
On start-up of the system, a single call should be made to OpenSB.
|
|
||||||
This routine will reset the SB, setup the interrupts and prepare the
|
|
||||||
structures for sound output. This routine should not be called again
|
|
||||||
until after the CloseSB function is called.
|
|
||||||
|
|
||||||
On system shut-down, a single call should be made to CloseSB. This routine
|
|
||||||
will reset the SB, disable the interrupts and free the structures that were
|
|
||||||
created.
|
|
||||||
|
|
||||||
Once the SB has been initialized, calling the Start_audio_output function
|
|
||||||
will start the DMA operation. Once started, the sound output will be
|
|
||||||
continuous; no other calls are required. The sound output will continue
|
|
||||||
until either the Stop_audio_output or CloseSB functions are called.
|
|
||||||
|
|
||||||
The Stop_audio_output function can be used to pause the output temporarily.
|
|
||||||
|
|
||||||
|
|
||||||
COMPILER REQUIREMENTS
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
*** Probably the most important compiler option is the selection of the data
|
|
||||||
segment/stack segment relationship. Because the IRQ will be calling the
|
|
||||||
fillBuffer function from within the IRQ, the data segment and stack segment
|
|
||||||
may not be equal. The compiler should be set to DS != SS. Without this
|
|
||||||
option, a stack overflow, system lockup or other unusual problem will occur.
|
|
||||||
|
|
||||||
|
|
||||||
Borland C++ 3.1 Notes
|
|
||||||
---------------------
|
|
||||||
The memory model must be set to LARGE and the COMP16 logical must be defined.
|
|
||||||
The DS/SS relationship can be left to 'default for memory model' since the
|
|
||||||
LARGE model automatically sets DS != SS, or this option can be set to never.
|
|
||||||
|
|
||||||
|
|
||||||
MSVC++ 1.5x
|
|
||||||
-----------
|
|
||||||
The memory model must be set to LARGE and the segment setup must be set to
|
|
||||||
'DS != SS, DS loaded on function entry'. Extern and unitialized data should
|
|
||||||
be assumed to be 'far'. Also, the COMP16 preprocessor symbol must be defined.
|
|
||||||
|
|
||||||
|
|
||||||
WATCOM 10.6
|
|
||||||
------------
|
|
||||||
The segment relationship must be set to DS != SS and stack probes must be
|
|
||||||
removed. This is accomplished by using the /zu and /s compiler options.
|
|
||||||
|
|
||||||
|
|
||||||
DJGPP
|
|
||||||
-----
|
|
||||||
Actually, I'm not sure what's required. I just compiled it as
|
|
||||||
GCC -o SBDRVTST.EXE SBDRVTST.C SBDRV.C and it worked. If you have problems
|
|
||||||
you may want to contact Bradford Mott as he added DJGPP support.
|
|
||||||
|
|
||||||
|
|
||||||
DETAILED FUNCTION DESCRIPTIONS
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
OpenSB (uint16 playback_freq, uint16 buffer_size)
|
|
||||||
-------------------------------------------------
|
|
||||||
|
|
||||||
This function resets and initializes the SB, initializes and enables the
|
|
||||||
interrupts, and creates and initializes all local global structures.
|
|
||||||
This function takes two parameters: the playback frequency and the size of
|
|
||||||
each playback buffer.
|
|
||||||
|
|
||||||
The playback frequency can be any unsigned integer from 1-65535, though
|
|
||||||
the maximum limit is currently set by the SB low-speed DMA audio routines
|
|
||||||
(approx. 22KHz).
|
|
||||||
|
|
||||||
The system will automatically allocate two playback buffers. The size of
|
|
||||||
each buffer can be any value from 1 to 65535. The actual size needed
|
|
||||||
will depend on the playback frequency and the DMA mode.
|
|
||||||
|
|
||||||
If Auto-Initialize DMA mode will be used, the size of the buffer can be set
|
|
||||||
as low as 35 or 40 with good output, though I don't recommend values lower
|
|
||||||
than 100.
|
|
||||||
|
|
||||||
If Standard DMA mode will be used, the minimum size that will produce clear
|
|
||||||
output varies based on the sample frequency. At maximum freq (22kHz), the
|
|
||||||
buffer size must be between 500-600 to produce clear output. At lower sample
|
|
||||||
frequencies, the size of the buffer can be much smaller.
|
|
||||||
|
|
||||||
If the call to OpenSB was unsuccessful, the function will return a value
|
|
||||||
of 0. Otherwise, the return value will be non-zero.
|
|
||||||
|
|
||||||
|
|
||||||
CloseSB (void)
|
|
||||||
--------------
|
|
||||||
|
|
||||||
This function disables all interrupts, frees all buffers and stops all
|
|
||||||
SB audio output. This function should be called before the program
|
|
||||||
exits. This function has no input or output parameters.
|
|
||||||
|
|
||||||
|
|
||||||
Set_FM_volume (uint8 left, uint8 right)
|
|
||||||
Set_line_volume (uint8 left, uint8 right)
|
|
||||||
Set_master_volume (uint8 left, uint8 right)
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
These functions set the left and right volume levels for the FM, line and
|
|
||||||
master volume controls. These functions have no output parameters (void).
|
|
||||||
|
|
||||||
|
|
||||||
Start_audio_output (uint8 dma_mode, void (*fillBuffer)(uint8 *buf, uint16 n))
|
|
||||||
-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
This function starts the audio output. It takes two parameters, the dma mode
|
|
||||||
to be used and the name of the function that generates the audio.
|
|
||||||
|
|
||||||
The possible selections for the dma_mode are AUTO_DMA and STANDARD_DMA.
|
|
||||||
Normally, the AUTO_DMA option should always be used as the routines will
|
|
||||||
attempt to auto-detect whether the AUTO_DMA option is supported. If AUTO_DMA
|
|
||||||
is not supported, the routine will automatically switch to STANDARD_DMA mode.
|
|
||||||
|
|
||||||
It is possible (though unlikely) that the AUTO_DMA attempt will cause
|
|
||||||
problems. Because of this possibility, the system can be forced to use the
|
|
||||||
STANDARD_DMA mode by passing STANDARD_DMA to Start_audio_output. I recommend
|
|
||||||
providing the user the option to force STANDARD_DMA operation. Note that the
|
|
||||||
buffer size may need to be increased to support STANDARD_DMA (see OpenSB).
|
|
||||||
|
|
||||||
The fillBuffer function passed should be a function that takes two parameters,
|
|
||||||
a pointer to an unsigned 8-bit buffer and an unsigned 16-bit integer. The
|
|
||||||
fillBuffer routine should process the next 'n' bytes (specified by the 16-bit
|
|
||||||
integer) and place them in the buffer pointed to starting with byte 0. Any
|
|
||||||
function that meets these requirements can be used, which makes the SBDRV
|
|
||||||
routines generic.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
License Information and Copyright Notice
|
|
||||||
========================================
|
|
||||||
|
|
||||||
SBDRV is Copyright(c) 1997-1999 by Ron Fries, Neil Bradley and Bradford Mott
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or modify it under
|
|
||||||
the terms of version 2 of the GNU Library General Public License as published
|
|
||||||
by the Free Software Foundation.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more
|
|
||||||
details.
|
|
||||||
|
|
||||||
To obtain a copy of the GNU Library General Public License, write to the Free
|
|
||||||
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
|
|
||||||
Any permitted reproduction of these routines, in whole or in part, must bear
|
|
||||||
this legend.
|
|
Loading…
Reference in New Issue