mirror of https://github.com/stella-emu/stella.git
Implement and connect audio emulation.
This commit is contained in:
parent
f1b5421c17
commit
4ec553785f
|
@ -18,11 +18,97 @@
|
|||
#include "Audio.hxx"
|
||||
#include "AudioQueue.hxx"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
constexpr double R_MAX = 30.;
|
||||
constexpr double R = 1.;
|
||||
|
||||
Int16 mixingTableEntry(uInt8 v, uInt8 vMax)
|
||||
{
|
||||
return floor(0x7fff * double(v) / double(vMax) * (R_MAX + R * double(vMax)) / (R_MAX + R * double(v)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Audio::Audio() {}
|
||||
Audio::Audio()
|
||||
: myAudioQueue(0),
|
||||
myCurrentFragment(0)
|
||||
{
|
||||
for (uInt8 i = 0; i <= 0x1e; i++) myMixingTableSum[i]= mixingTableEntry(i, 0x1e);
|
||||
for (uInt8 i = 0; i <= 0x0f; i++) myMixingTableIndividual[i] = mixingTableEntry(i, 0x0f);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Audio::reset()
|
||||
{
|
||||
myCounter = 0;
|
||||
mySampleIndex = 0;
|
||||
|
||||
myChannel0.reset();
|
||||
myChannel1.reset();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Audio::setAudioQueue(AudioQueue* queue)
|
||||
{
|
||||
myAudioQueue = queue;
|
||||
|
||||
myCurrentFragment = myAudioQueue->enqueue();
|
||||
mySampleIndex = 0;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Audio::tick()
|
||||
{ switch (myCounter) {
|
||||
case 9:
|
||||
case 81:
|
||||
myChannel0.phase0();
|
||||
myChannel1.phase0();
|
||||
|
||||
break;
|
||||
|
||||
case 37:
|
||||
case 149:
|
||||
phase1();
|
||||
break;
|
||||
}
|
||||
|
||||
if (++myCounter == 228) myCounter = 0;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Audio::phase1()
|
||||
{
|
||||
uInt8 sample0 = myChannel0.phase1();
|
||||
uInt8 sample1 = myChannel1.phase1();
|
||||
|
||||
if (!myAudioQueue) return;
|
||||
|
||||
if (myAudioQueue->isStereo()) {
|
||||
myCurrentFragment[2*mySampleIndex] = myMixingTableIndividual[sample0];
|
||||
myCurrentFragment[2*mySampleIndex + 1] = myMixingTableIndividual[sample1];
|
||||
} else {
|
||||
myCurrentFragment[mySampleIndex] = myMixingTableSum[sample0 + sample1];
|
||||
}
|
||||
|
||||
if (++mySampleIndex == myAudioQueue->fragmentSize()) {
|
||||
mySampleIndex = 0;
|
||||
myCurrentFragment = myAudioQueue->enqueue(myCurrentFragment);
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
AudioChannel& Audio::channel0()
|
||||
{
|
||||
return myChannel0;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
AudioChannel& Audio::channel1()
|
||||
{
|
||||
return myChannel1;
|
||||
}
|
||||
|
|
|
@ -18,17 +18,43 @@
|
|||
#ifndef TIA_AUDIO_HXX
|
||||
#define TIA_AUDIO_HXX
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "AudioChannel.hxx"
|
||||
|
||||
class AudioQueue;
|
||||
|
||||
class Audio {
|
||||
class Audio
|
||||
{
|
||||
public:
|
||||
Audio();
|
||||
|
||||
void reset();
|
||||
|
||||
void setAudioQueue(AudioQueue *queue);
|
||||
|
||||
void tick();
|
||||
|
||||
AudioChannel& channel0();
|
||||
|
||||
AudioChannel& channel1();
|
||||
|
||||
private:
|
||||
void phase1();
|
||||
|
||||
private:
|
||||
AudioQueue* myAudioQueue;
|
||||
|
||||
uInt8 myCounter;
|
||||
|
||||
AudioChannel myChannel0;
|
||||
AudioChannel myChannel1;
|
||||
|
||||
Int16 myMixingTableSum[0x1e + 1];
|
||||
Int16 myMixingTableIndividual[0x0f + 1];
|
||||
|
||||
Int16* myCurrentFragment;
|
||||
uInt32 mySampleIndex;
|
||||
|
||||
private:
|
||||
Audio(const Audio&) = delete;
|
||||
Audio(Audio&&) = delete;
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2018 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include "AudioChannel.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
AudioChannel::AudioChannel()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void AudioChannel::reset()
|
||||
{
|
||||
myAudc = myAudv = myAudf = 0;
|
||||
myClockEnable = myNoiseFeedback = myNoiseCounterBit4 = myPulseCounterHold = false;
|
||||
myDivCounter = myPulseCounter = myNoiseCounter = 0;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void AudioChannel::phase0()
|
||||
{
|
||||
if (myClockEnable) {
|
||||
myNoiseCounterBit4 = myNoiseCounter & 0x01;
|
||||
|
||||
switch (myAudc & 0x03) {
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
myPulseCounterHold = false;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
myPulseCounterHold = (myNoiseCounter & 0x1e) != 0x02;
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
myPulseCounterHold = myNoiseCounterBit4;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (myAudc & 0x03) {
|
||||
case 0x00:
|
||||
myNoiseFeedback =
|
||||
((myPulseCounter ^ myNoiseCounter) & 0x01) ||
|
||||
!(myNoiseCounter || (myPulseCounter != 0x0a)) ||
|
||||
!(myAudc & 0x0c);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
myNoiseFeedback =
|
||||
((myNoiseCounter & 0x04 ? 1 : 0) ^ (myNoiseCounter & 0x01)) ||
|
||||
myNoiseCounter == 0;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
myClockEnable = myDivCounter == myAudf;
|
||||
|
||||
if (myDivCounter == myAudf || myDivCounter == 0x1f) {
|
||||
myDivCounter = 0;
|
||||
} else {
|
||||
myDivCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt8 AudioChannel::phase1()
|
||||
{
|
||||
bool pulseFeedback = false;
|
||||
|
||||
if (myClockEnable) {
|
||||
switch (myAudc >> 2) {
|
||||
case 0x00:
|
||||
pulseFeedback =
|
||||
((myPulseCounter & 0x02 ? 1 : 0) ^ (myPulseCounter & 0x01)) &&
|
||||
(myPulseCounter != 0x0a) &&
|
||||
(myAudc & 0x03);
|
||||
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
pulseFeedback = !(myPulseCounter & 0x08);
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
pulseFeedback = !myNoiseCounterBit4;
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
pulseFeedback = !((myPulseCounter & 0x02) || !(myPulseCounter & 0x0e));
|
||||
break;
|
||||
}
|
||||
|
||||
myNoiseCounter >>= 1;
|
||||
if (myNoiseFeedback) {
|
||||
myNoiseCounter |= 0x10;
|
||||
}
|
||||
|
||||
if (!myPulseCounterHold) {
|
||||
myPulseCounter = ~(myPulseCounter >> 1) & 0x07;
|
||||
|
||||
if (pulseFeedback) {
|
||||
myPulseCounter |= 0x08;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (myPulseCounter & 0x01) * myAudv;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void AudioChannel::audc(uInt8 value)
|
||||
{
|
||||
myAudc = value & 0x0f;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void AudioChannel::audv(uInt8 value)
|
||||
{
|
||||
myAudv = value & 0x0f;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void AudioChannel::audf(uInt8 value)
|
||||
{
|
||||
myAudc = value & 0x1f;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2018 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#ifndef TIA_AUDIO_CHANNEL_HXX
|
||||
#define TIA_AUDIO_CHANNEL_HXX
|
||||
|
||||
#include "bspf.hxx"
|
||||
|
||||
class AudioChannel
|
||||
{
|
||||
public:
|
||||
AudioChannel();
|
||||
|
||||
void reset();
|
||||
|
||||
void phase0();
|
||||
|
||||
uInt8 phase1();
|
||||
|
||||
void audc(uInt8 value);
|
||||
|
||||
void audf(uInt8 value);
|
||||
|
||||
void audv(uInt8 value);
|
||||
|
||||
private:
|
||||
uInt8 myAudc;
|
||||
uInt8 myAudv;
|
||||
uInt8 myAudf;
|
||||
|
||||
bool myClockEnable;
|
||||
bool myNoiseFeedback;
|
||||
bool myNoiseCounterBit4;
|
||||
bool myPulseCounterHold;
|
||||
|
||||
uInt8 myDivCounter;
|
||||
uInt8 myPulseCounter;
|
||||
uInt8 myNoiseCounter;
|
||||
|
||||
private:
|
||||
AudioChannel(const AudioChannel&);
|
||||
AudioChannel(AudioChannel&&);
|
||||
AudioChannel& operator=(const AudioChannel&);
|
||||
AudioChannel& operator=(AudioChannel&&);
|
||||
};
|
||||
|
||||
#endif // TIA_AUDIO_CHANNEL_HXX
|
|
@ -521,21 +521,32 @@ bool TIA::poke(uInt16 address, uInt8 value)
|
|||
break;
|
||||
|
||||
case AUDV0:
|
||||
myAudio.channel0().audv(value);
|
||||
myShadowRegisters[address] = value;
|
||||
break;
|
||||
|
||||
case AUDV1:
|
||||
myAudio.channel1().audv(value);
|
||||
myShadowRegisters[address] = value;
|
||||
break;
|
||||
|
||||
case AUDF0:
|
||||
myAudio.channel0().audf(value);
|
||||
myShadowRegisters[address] = value;
|
||||
break;
|
||||
|
||||
case AUDF1:
|
||||
myAudio.channel1().audf(value);
|
||||
myShadowRegisters[address] = value;
|
||||
break;
|
||||
|
||||
case AUDC0:
|
||||
myAudio.channel0().audc(value);
|
||||
myShadowRegisters[address] = value;
|
||||
break;
|
||||
|
||||
case AUDC1:
|
||||
myAudio.channel1().audc(value);
|
||||
myShadowRegisters[address] = value;
|
||||
break;
|
||||
|
||||
|
@ -1179,6 +1190,8 @@ void TIA::cycle(uInt32 colorClocks)
|
|||
if (++myHctr >= 228)
|
||||
nextLine();
|
||||
|
||||
myAudio.tick();
|
||||
|
||||
myTimestamp++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ MODULE_OBJS := \
|
|||
src/emucore/tia/Background.o \
|
||||
src/emucore/tia/LatchedInput.o \
|
||||
src/emucore/tia/PaddleReader.o \
|
||||
src/emucore/tia/Audio.o
|
||||
src/emucore/tia/Audio.o \
|
||||
src/emucore/tia/AudioChannel.o
|
||||
|
||||
MODULE_DIRS += \
|
||||
src/emucore/tia
|
||||
|
|
Loading…
Reference in New Issue