Implement and connect audio emulation.

This commit is contained in:
Christian Speckner 2018-01-24 22:20:44 +01:00
parent f1b5421c17
commit 4ec553785f
6 changed files with 333 additions and 3 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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++;
}
}

View File

@ -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