make Audio Interface more clear. Should have no behavioral changes. The comment block in AudioInterface.cpp lays out how real hardware is...
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6427 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
4f81997c14
commit
9dd60c6115
|
@ -15,12 +15,40 @@
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
// This file is ONLY about disc streaming. It's a bit unfortunately named.
|
/*
|
||||||
// For the rest of the audio stuff, including the "real" AI, see DSP.cpp/h.
|
Here is a nice ascii overview of audio flow affected by this file:
|
||||||
|
|
||||||
// AI disc streaming is handled completely separately from the rest of the
|
(RAM)---->[AI FIFO]---->[SRC]---->[Mixer]---->[DAC]---->(Speakers)
|
||||||
// audio processing. In short, it simply streams audio directly from disc
|
^
|
||||||
// out through the speakers.
|
|
|
||||||
|
[L/R Volume]
|
||||||
|
\
|
||||||
|
(DVD)---->[Drive I/F]---->[SRC]---->[Counter]
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
Output at "48KHz" is actually 48043Hz.
|
||||||
|
Sample counter counts streaming stereo samples after upsampling.
|
||||||
|
[DAC] causes [AI I/F] to read from RAM at rate selected by AIDFR.
|
||||||
|
Each [SRC] will upsample a 32KHz source, or pass through the 48KHz
|
||||||
|
source. The [Mixer]/[DAC] only operate at 48KHz.
|
||||||
|
|
||||||
|
AIS == disc streaming == DTK(Disk Track Player) == streaming audio, etc.
|
||||||
|
|
||||||
|
Supposedly, the retail hardware only supports 48KHz streaming from
|
||||||
|
[Drive I/F]. However it's more likely that the hardware supports
|
||||||
|
32KHz streaming, and the upsampling is transparent to the user.
|
||||||
|
TODO check if anything tries to stream at 32KHz.
|
||||||
|
|
||||||
|
The [Drive I/F] actually supports simultaneous requests for audio and
|
||||||
|
normal data. For this reason, we can't really get rid of the crit section.
|
||||||
|
|
||||||
|
IMPORTANT:
|
||||||
|
This file mainly deals with the [Drive I/F], however [AIDFR] controls
|
||||||
|
the rate at which the audio data is DMA'd from RAM into the [AI FIFO]
|
||||||
|
(and the speed at which the FIFO is read by its SRC). Everything else
|
||||||
|
relating to AID happens in DSP.cpp. It's kinda just bad hardware design.
|
||||||
|
TODO maybe the files should be merged?
|
||||||
|
*/
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
|
||||||
|
@ -37,7 +65,7 @@
|
||||||
namespace AudioInterface
|
namespace AudioInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
// internal hardware addresses
|
// Internal hardware addresses
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
AI_CONTROL_REGISTER = 0x6C00,
|
AI_CONTROL_REGISTER = 0x6C00,
|
||||||
|
@ -46,6 +74,15 @@ enum
|
||||||
AI_INTERRUPT_TIMING = 0x6C0C,
|
AI_INTERRUPT_TIMING = 0x6C0C,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
AIS_32KHz = 0,
|
||||||
|
AIS_48KHz = 1,
|
||||||
|
|
||||||
|
AID_32KHz = 1,
|
||||||
|
AID_48KHz = 0
|
||||||
|
};
|
||||||
|
|
||||||
// AI Control Register
|
// AI Control Register
|
||||||
union AICR
|
union AICR
|
||||||
{
|
{
|
||||||
|
@ -54,64 +91,68 @@ union AICR
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
u32 PSTAT : 1; // sample counter/playback enable
|
u32 PSTAT : 1; // sample counter/playback enable
|
||||||
u32 AIFR : 1; // AI Frequency (0=32khz 1=48khz)
|
u32 AISFR : 1; // AIS Frequency (0=32khz 1=48khz)
|
||||||
u32 AIINTMSK : 1; // 0=interrupt masked 1=interrupt enabled
|
u32 AIINTMSK : 1; // 0=interrupt masked 1=interrupt enabled
|
||||||
u32 AIINT : 1; // audio interrupt status
|
u32 AIINT : 1; // audio interrupt status
|
||||||
u32 AIINTVLD : 1; // This bit controls whether AIINT is affected by the AIIT register
|
u32 AIINTVLD : 1; // This bit controls whether AIINT is affected by the Interrupt Timing register
|
||||||
// matching AISLRCNT. Once set, AIINT will hold
|
// matching the sample counter. Once set, AIINT will hold its last value
|
||||||
u32 SCRESET : 1; // write to reset counter
|
u32 SCRESET : 1; // write to reset counter
|
||||||
u32 DACFR : 1; // DAC Frequency (0=48khz 1=32khz)
|
u32 AIDFR : 1; // AID Frequency (0=48khz 1=32khz)
|
||||||
u32 :25;
|
u32 :25;
|
||||||
};
|
};
|
||||||
u32 hex;
|
u32 hex;
|
||||||
};
|
};
|
||||||
|
|
||||||
// AI m_Volume Register
|
// AI Volume Register
|
||||||
union AIVR
|
union AIVR
|
||||||
{
|
{
|
||||||
|
AIVR() { hex = 0;}
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
u32 leftVolume : 8;
|
u32 left : 8;
|
||||||
u32 rightVolume : 8;
|
u32 right : 8;
|
||||||
u32 : 16;
|
u32 :16;
|
||||||
};
|
};
|
||||||
u32 hex;
|
u32 hex;
|
||||||
};
|
};
|
||||||
|
|
||||||
// AudioInterface-Registers
|
|
||||||
struct SAudioRegister
|
|
||||||
{
|
|
||||||
AICR m_Control;
|
|
||||||
AIVR m_Volume;
|
|
||||||
u32 m_SampleCounter;
|
|
||||||
u32 m_InterruptTiming;
|
|
||||||
};
|
|
||||||
|
|
||||||
// STATE_TO_SAVE
|
// STATE_TO_SAVE
|
||||||
static SAudioRegister g_AudioRegister;
|
// Registers
|
||||||
|
static AICR m_Control;
|
||||||
|
static AIVR m_Volume;
|
||||||
|
static u32 m_SampleCounter = 0;
|
||||||
|
static u32 m_InterruptTiming = 0;
|
||||||
|
|
||||||
static u64 g_LastCPUTime = 0;
|
static u64 g_LastCPUTime = 0;
|
||||||
static unsigned int g_AISampleRate = 32000;
|
|
||||||
static unsigned int g_DACSampleRate = 32000;
|
|
||||||
static u64 g_CPUCyclesPerSample = 0xFFFFFFFFFFFULL;
|
static u64 g_CPUCyclesPerSample = 0xFFFFFFFFFFFULL;
|
||||||
|
|
||||||
|
static unsigned int g_AISSampleRate = 48000;
|
||||||
|
static unsigned int g_AIDSampleRate = 32000;
|
||||||
|
|
||||||
void DoState(PointerWrap &p)
|
void DoState(PointerWrap &p)
|
||||||
{
|
{
|
||||||
p.Do(g_AudioRegister);
|
p.Do(m_Control);
|
||||||
|
p.Do(m_Volume);
|
||||||
|
p.Do(m_SampleCounter);
|
||||||
|
p.Do(m_InterruptTiming);
|
||||||
p.Do(g_LastCPUTime);
|
p.Do(g_LastCPUTime);
|
||||||
p.Do(g_AISampleRate);
|
p.Do(g_AISSampleRate);
|
||||||
p.Do(g_DACSampleRate);
|
p.Do(g_AIDSampleRate);
|
||||||
p.Do(g_CPUCyclesPerSample);
|
p.Do(g_CPUCyclesPerSample);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateAudioInterrupt();
|
void GenerateAudioInterrupt();
|
||||||
void UpdateInterrupts();
|
void UpdateInterrupts();
|
||||||
void IncreaseSampleCount(const u32 _uAmount);
|
void IncreaseSampleCount(const u32 _uAmount);
|
||||||
void ReadStreamBlock(short* _pPCM);
|
void ReadStreamBlock(s16* _pPCM);
|
||||||
|
|
||||||
void Init()
|
void Init()
|
||||||
{
|
{
|
||||||
g_AudioRegister.m_SampleCounter = 0;
|
m_Control.hex = 0;
|
||||||
g_AudioRegister.m_Control.AIFR = 1;
|
m_Control.AISFR = AIS_48KHz;
|
||||||
|
m_Volume.hex = 0;
|
||||||
|
m_SampleCounter = 0;
|
||||||
|
m_InterruptTiming = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown()
|
void Shutdown()
|
||||||
|
@ -120,41 +161,35 @@ void Shutdown()
|
||||||
|
|
||||||
void Read32(u32& _rReturnValue, const u32 _Address)
|
void Read32(u32& _rReturnValue, const u32 _Address)
|
||||||
{
|
{
|
||||||
//__AI_SRC_INIT compares CC006C08 to zero, loops if 2
|
|
||||||
switch (_Address & 0xFFFF)
|
switch (_Address & 0xFFFF)
|
||||||
{
|
{
|
||||||
case AI_CONTROL_REGISTER: //0x6C00
|
case AI_CONTROL_REGISTER:
|
||||||
DEBUG_LOG(AUDIO_INTERFACE, "AudioInterface(R) 0x%08x", _Address);
|
_rReturnValue = m_Control.hex;
|
||||||
_rReturnValue = g_AudioRegister.m_Control.hex;
|
break;
|
||||||
|
|
||||||
return;
|
case AI_VOLUME_REGISTER:
|
||||||
|
_rReturnValue = m_Volume.hex;
|
||||||
|
break;
|
||||||
|
|
||||||
// Sample Rate (AIGetDSPSampleRate)
|
case AI_SAMPLE_COUNTER:
|
||||||
// 32bit state (highest bit PlayState) // AIGetStreamPlayState
|
_rReturnValue = m_SampleCounter;
|
||||||
case AI_VOLUME_REGISTER: //0x6C04
|
// HACK - AI SRC init will do while (oldval == sample_counter) {}
|
||||||
DEBUG_LOG(AUDIO_INTERFACE, "AudioInterface(R) 0x%08x", _Address);
|
// in order to pass this, we need to increment the counter whenever read
|
||||||
_rReturnValue = g_AudioRegister.m_Volume.hex;
|
if (m_Control.PSTAT)
|
||||||
return;
|
m_SampleCounter++;
|
||||||
|
break;
|
||||||
case AI_SAMPLE_COUNTER: //0x6C08
|
|
||||||
_rReturnValue = g_AudioRegister.m_SampleCounter;
|
|
||||||
if (g_AudioRegister.m_Control.PSTAT)
|
|
||||||
g_AudioRegister.m_SampleCounter++; // FAKE: but this is a must
|
|
||||||
return;
|
|
||||||
|
|
||||||
case AI_INTERRUPT_TIMING:
|
case AI_INTERRUPT_TIMING:
|
||||||
// When sample counter reaches the value of this register, the interrupt AIINT should
|
_rReturnValue = m_InterruptTiming;
|
||||||
// fire.
|
break;
|
||||||
DEBUG_LOG(AUDIO_INTERFACE, "AudioInterface(R) 0x%08x", _Address);
|
|
||||||
_rReturnValue = g_AudioRegister.m_InterruptTiming;
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
INFO_LOG(AUDIO_INTERFACE, "AudioInterface(R) 0x%08x", _Address);
|
ERROR_LOG(AUDIO_INTERFACE, "unknown read 0x%08x", _Address);
|
||||||
_dbg_assert_msg_(AUDIO_INTERFACE, 0, "AudioInterface - Read from ???");
|
_dbg_assert_msg_(AUDIO_INTERFACE, 0, "AudioInterface - Read from 0x%08x", _Address);
|
||||||
_rReturnValue = 0;
|
_rReturnValue = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
DEBUG_LOG(AUDIO_INTERFACE, "r32 %08x %08x", _Address, _rReturnValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write32(const u32 _Value, const u32 _Address)
|
void Write32(const u32 _Value, const u32 _Address)
|
||||||
|
@ -165,123 +200,111 @@ void Write32(const u32 _Value, const u32 _Address)
|
||||||
{
|
{
|
||||||
AICR tmpAICtrl(_Value);
|
AICR tmpAICtrl(_Value);
|
||||||
|
|
||||||
g_AudioRegister.m_Control.AIINTMSK = tmpAICtrl.AIINTMSK;
|
m_Control.AIINTMSK = tmpAICtrl.AIINTMSK;
|
||||||
g_AudioRegister.m_Control.AIINTVLD = tmpAICtrl.AIINTVLD;
|
m_Control.AIINTVLD = tmpAICtrl.AIINTVLD;
|
||||||
|
|
||||||
// Set frequency
|
// Set frequency of streaming audio
|
||||||
if (tmpAICtrl.AIFR != g_AudioRegister.m_Control.AIFR)
|
if (tmpAICtrl.AISFR != m_Control.AISFR)
|
||||||
{
|
{
|
||||||
INFO_LOG(AUDIO_INTERFACE, "Change Freq to %s", tmpAICtrl.AIFR ? "48khz":"32khz");
|
DEBUG_LOG(AUDIO_INTERFACE, "Change AISFR to %s", tmpAICtrl.AISFR ? "48khz":"32khz");
|
||||||
g_AudioRegister.m_Control.AIFR = tmpAICtrl.AIFR;
|
m_Control.AISFR = tmpAICtrl.AISFR;
|
||||||
}
|
}
|
||||||
// Set DSP frequency
|
// Set frequency of DMA
|
||||||
if (tmpAICtrl.DACFR != g_AudioRegister.m_Control.DACFR)
|
if (tmpAICtrl.AIDFR != m_Control.AIDFR)
|
||||||
{
|
{
|
||||||
INFO_LOG(AUDIO_INTERFACE, "AI_CONTROL_REGISTER: Change DSPFR Freq to %s", tmpAICtrl.DACFR ? "48khz":"32khz");
|
DEBUG_LOG(AUDIO_INTERFACE, "Change AIDFR to %s", tmpAICtrl.AIDFR ? "32khz":"48khz");
|
||||||
g_AudioRegister.m_Control.DACFR = tmpAICtrl.DACFR;
|
m_Control.AIDFR = tmpAICtrl.AIDFR;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_AISampleRate = tmpAICtrl.AIFR ? 48000 : 32000;
|
g_AISSampleRate = tmpAICtrl.AISFR ? 48000 : 32000;
|
||||||
g_DACSampleRate = tmpAICtrl.DACFR ? 32000 : 48000;
|
g_AIDSampleRate = tmpAICtrl.AIDFR ? 32000 : 48000;
|
||||||
|
|
||||||
g_CPUCyclesPerSample = SystemTimers::GetTicksPerSecond() / g_AISampleRate;
|
g_CPUCyclesPerSample = SystemTimers::GetTicksPerSecond() / g_AISSampleRate;
|
||||||
|
|
||||||
// Streaming counter
|
// Streaming counter
|
||||||
if (tmpAICtrl.PSTAT != g_AudioRegister.m_Control.PSTAT)
|
if (tmpAICtrl.PSTAT != m_Control.PSTAT)
|
||||||
{
|
{
|
||||||
INFO_LOG(AUDIO_INTERFACE, "Change StreamingCounter to %s", tmpAICtrl.PSTAT ? "startet":"stopped");
|
DEBUG_LOG(AUDIO_INTERFACE, "%s streaming audio", tmpAICtrl.PSTAT ? "start":"stop");
|
||||||
g_AudioRegister.m_Control.PSTAT = tmpAICtrl.PSTAT;
|
m_Control.PSTAT = tmpAICtrl.PSTAT;
|
||||||
g_LastCPUTime = CoreTiming::GetTicks();
|
g_LastCPUTime = CoreTiming::GetTicks();
|
||||||
|
|
||||||
// This is the only new code in this ~3,326 revision, it seems to avoid hanging Crazy Taxi,
|
// Tell Drive Interface to stop streaming
|
||||||
// while the 1080 and Wave Race music still works
|
|
||||||
if (!tmpAICtrl.PSTAT) DVDInterface::g_bStream = false;
|
if (!tmpAICtrl.PSTAT) DVDInterface::g_bStream = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI Interrupt
|
// AI Interrupt
|
||||||
if (tmpAICtrl.AIINT)
|
if (tmpAICtrl.AIINT)
|
||||||
{
|
{
|
||||||
INFO_LOG(AUDIO_INTERFACE, "Clear AI Interrupt");
|
DEBUG_LOG(AUDIO_INTERFACE, "Clear AIS Interrupt");
|
||||||
g_AudioRegister.m_Control.AIINT = 0;
|
m_Control.AIINT = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sample Count Reset
|
// Sample Count Reset
|
||||||
if (tmpAICtrl.SCRESET)
|
if (tmpAICtrl.SCRESET)
|
||||||
{
|
{
|
||||||
INFO_LOG(AUDIO_INTERFACE, "Reset SampleCounter");
|
DEBUG_LOG(AUDIO_INTERFACE, "Reset AIS sample counter");
|
||||||
g_AudioRegister.m_SampleCounter = 0;
|
m_SampleCounter = 0;
|
||||||
g_AudioRegister.m_Control.SCRESET = 0;
|
|
||||||
|
|
||||||
// set PSTAT = 0 too ? at least the reversed look like this
|
|
||||||
|
|
||||||
g_LastCPUTime = CoreTiming::GetTicks();
|
g_LastCPUTime = CoreTiming::GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
// I don't think we need this
|
|
||||||
//g_AudioRegister.m_Control = tmpAICtrl;
|
|
||||||
|
|
||||||
UpdateInterrupts();
|
UpdateInterrupts();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AI_VOLUME_REGISTER:
|
case AI_VOLUME_REGISTER:
|
||||||
g_AudioRegister.m_Volume.hex = _Value;
|
m_Volume.hex = _Value;
|
||||||
INFO_LOG(AUDIO_INTERFACE, "Set m_Volume: left(%i) right(%i)", g_AudioRegister.m_Volume.leftVolume, g_AudioRegister.m_Volume.rightVolume);
|
DEBUG_LOG(AUDIO_INTERFACE, "Set volume: left(%02x) right(%02x)", m_Volume.left, m_Volume.right);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AI_SAMPLE_COUNTER:
|
case AI_SAMPLE_COUNTER:
|
||||||
// _dbg_assert_msg_(AUDIO_INTERFACE, 0, "AudioInterface - m_SampleCounter is Read only");
|
// Why was this commented out? Does something do this?
|
||||||
g_AudioRegister.m_SampleCounter = _Value;
|
_dbg_assert_msg_(AUDIO_INTERFACE, 0, "AIS - sample counter is read only");
|
||||||
|
m_SampleCounter = _Value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AI_INTERRUPT_TIMING:
|
case AI_INTERRUPT_TIMING:
|
||||||
g_AudioRegister.m_InterruptTiming = _Value;
|
m_InterruptTiming = _Value;
|
||||||
INFO_LOG(AUDIO_INTERFACE, "Set AudioInterrupt: 0x%08x Samples", g_AudioRegister.m_InterruptTiming);
|
DEBUG_LOG(AUDIO_INTERFACE, "Set interrupt: %08x samples", m_InterruptTiming);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
PanicAlert("AudioInterface unknown write");
|
ERROR_LOG(AUDIO_INTERFACE, "unknown write %08x @ %08x", _Value, _Address);
|
||||||
_dbg_assert_msg_(AUDIO_INTERFACE,0,"AudioInterface - Write to ??? %08x", _Address);
|
_dbg_assert_msg_(AUDIO_INTERFACE,0,"AIS - Write %08x to %08x", _Value, _Address);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateInterrupts()
|
static void UpdateInterrupts()
|
||||||
{
|
{
|
||||||
if (g_AudioRegister.m_Control.AIINT & g_AudioRegister.m_Control.AIINTMSK)
|
ProcessorInterface::SetInterrupt(
|
||||||
{
|
ProcessorInterface::INT_CAUSE_AI, m_Control.AIINT & m_Control.AIINTMSK);
|
||||||
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_AI, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_AI, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateAudioInterrupt()
|
static void GenerateAudioInterrupt()
|
||||||
{
|
{
|
||||||
g_AudioRegister.m_Control.AIINT = 1;
|
m_Control.AIINT = 1;
|
||||||
UpdateInterrupts();
|
UpdateInterrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate)
|
void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate)
|
||||||
{
|
{
|
||||||
_AISampleRate = g_AISampleRate;
|
_AISampleRate = g_AISSampleRate;
|
||||||
_DACSampleRate = g_DACSampleRate;
|
_DACSampleRate = g_AIDSampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback for the disc streaming
|
// Callback for the disc streaming
|
||||||
// WARNING - called from audio thread
|
// WARNING - called from audio thread
|
||||||
unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples, unsigned int _sampleRate)
|
unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples, unsigned int _sampleRate)
|
||||||
{
|
{
|
||||||
if (g_AudioRegister.m_Control.PSTAT && !CCPU::IsStepping())
|
if (m_Control.PSTAT && !CCPU::IsStepping())
|
||||||
{
|
{
|
||||||
static int pos = 0;
|
static int pos = 0;
|
||||||
static short pcm[28*2];
|
static short pcm[NGCADPCM::SAMPLES_PER_BLOCK*2];
|
||||||
const int lvolume = g_AudioRegister.m_Volume.leftVolume;
|
const int lvolume = m_Volume.left;
|
||||||
const int rvolume = g_AudioRegister.m_Volume.rightVolume;
|
const int rvolume = m_Volume.right;
|
||||||
|
|
||||||
if (g_AISampleRate == 48000 && _sampleRate == 32000)
|
if (g_AISSampleRate == 48000 && _sampleRate == 32000)
|
||||||
{
|
{
|
||||||
_dbg_assert_msg_(AUDIO_INTERFACE, !(_numSamples & 1), "Number of Samples: %i must be even!", _numSamples);
|
_dbg_assert_msg_(AUDIO_INTERFACE, !(_numSamples & 1), "Number of Samples: %i must be even!", _numSamples);
|
||||||
_numSamples = _numSamples * 3 / 2;
|
_numSamples = _numSamples * 3 / 2;
|
||||||
|
@ -293,7 +316,7 @@ unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples
|
||||||
if (pos == 0)
|
if (pos == 0)
|
||||||
ReadStreamBlock(pcm);
|
ReadStreamBlock(pcm);
|
||||||
|
|
||||||
if (g_AISampleRate == 48000 && _sampleRate == 32000) //downsample 48>32
|
if (g_AISSampleRate == 48000 && _sampleRate == 32000) //downsample 48>32
|
||||||
{
|
{
|
||||||
if (i % 3)
|
if (i % 3)
|
||||||
{
|
{
|
||||||
|
@ -312,7 +335,7 @@ unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples
|
||||||
|
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
else if (g_AISampleRate == 32000 && _sampleRate == 48000) //upsample 32>48
|
else if (g_AISSampleRate == 32000 && _sampleRate == 48000) //upsample 32>48
|
||||||
{
|
{
|
||||||
//starts with one sample of 0
|
//starts with one sample of 0
|
||||||
const u32 ratio = (u32)( 65536.0f * 32000.0f / (float)_sampleRate );
|
const u32 ratio = (u32)( 65536.0f * 32000.0f / (float)_sampleRate );
|
||||||
|
@ -368,7 +391,7 @@ unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos == 28)
|
if (pos == NGCADPCM::SAMPLES_PER_BLOCK)
|
||||||
pos = 0;
|
pos = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -387,52 +410,42 @@ unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING - called from audio thread
|
// WARNING - called from audio thread
|
||||||
void ReadStreamBlock(short *_pPCM)
|
void ReadStreamBlock(s16 *_pPCM)
|
||||||
{
|
{
|
||||||
char tempADPCM[32];
|
u8 tempADPCM[NGCADPCM::ONE_BLOCK_SIZE];
|
||||||
if (DVDInterface::DVDReadADPCM((u8*)tempADPCM, 32))
|
if (DVDInterface::DVDReadADPCM(tempADPCM, NGCADPCM::ONE_BLOCK_SIZE))
|
||||||
{
|
{
|
||||||
NGCADPCM::DecodeBlock(_pPCM, (u8*)tempADPCM);
|
NGCADPCM::DecodeBlock(_pPCM, tempADPCM);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int j=0; j<28; j++)
|
memset(_pPCM, 0, NGCADPCM::SAMPLES_PER_BLOCK*2);
|
||||||
{
|
|
||||||
*_pPCM++ = 0;
|
|
||||||
*_pPCM++ = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// COMMENT:
|
// our whole streaming code is "faked" ... so it shouldn't increase the sample counter
|
||||||
// our whole streaming code is "faked" ... so it shouldn't increase the sample counter
|
// streaming will never work correctly this way, but at least the program will think all is alright.
|
||||||
// streaming will never work correctly this way, but at least the program will think all is alright.
|
|
||||||
|
|
||||||
// This call must not be done wihout going through CoreTiming's threadsafe option.
|
|
||||||
// IncreaseSampleCount(28);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncreaseSampleCount(const u32 _iAmount)
|
static void IncreaseSampleCount(const u32 _iAmount)
|
||||||
{
|
{
|
||||||
if (g_AudioRegister.m_Control.PSTAT)
|
if (m_Control.PSTAT)
|
||||||
{
|
{
|
||||||
g_AudioRegister.m_SampleCounter += _iAmount;
|
m_SampleCounter += _iAmount;
|
||||||
if (g_AudioRegister.m_Control.AIINTVLD &&
|
if (m_Control.AIINTVLD && (m_SampleCounter >= m_InterruptTiming))
|
||||||
(g_AudioRegister.m_SampleCounter >= g_AudioRegister.m_InterruptTiming))
|
|
||||||
{
|
{
|
||||||
GenerateAudioInterrupt();
|
GenerateAudioInterrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int GetDSPSampleRate()
|
unsigned int GetAIDSampleRate()
|
||||||
{
|
{
|
||||||
return g_DACSampleRate;
|
return g_AIDSampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
// update timer
|
if (m_Control.PSTAT)
|
||||||
if (g_AudioRegister.m_Control.PSTAT)
|
|
||||||
{
|
{
|
||||||
const u64 Diff = CoreTiming::GetTicks() - g_LastCPUTime;
|
const u64 Diff = CoreTiming::GetTicks() - g_LastCPUTime;
|
||||||
if (Diff > g_CPUCyclesPerSample)
|
if (Diff > g_CPUCyclesPerSample)
|
||||||
|
@ -445,4 +458,3 @@ void Update()
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end of namespace AudioInterface
|
} // end of namespace AudioInterface
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ void Read32(u32& _uReturnValue, const u32 _iAddress);
|
||||||
void Write32(const u32 _iValue, const u32 _iAddress);
|
void Write32(const u32 _iValue, const u32 _iAddress);
|
||||||
|
|
||||||
// Get the audio rates (48000 or 32000 only)
|
// Get the audio rates (48000 or 32000 only)
|
||||||
unsigned int GetDSPSampleRate();
|
unsigned int GetAIDSampleRate();
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,15 @@
|
||||||
|
|
||||||
#include "StreamADPCM.h"
|
#include "StreamADPCM.h"
|
||||||
|
|
||||||
#define ONE_BLOCK_SIZE 32
|
|
||||||
#define SAMPLES_PER_BLOCK 28
|
|
||||||
|
|
||||||
// STATE_TO_SAVE (not saved yet!)
|
// STATE_TO_SAVE (not saved yet!)
|
||||||
static int histl1;
|
static s32 histl1;
|
||||||
static int histl2;
|
static s32 histl2;
|
||||||
static int histr1;
|
static s32 histr1;
|
||||||
static int histr2;
|
static s32 histr2;
|
||||||
|
|
||||||
short ADPDecodeSample(int bits, int q, int *hist1p, int *hist2p)
|
s16 ADPDecodeSample(s32 bits, s32 q, s32& hist1, s32& hist2)
|
||||||
{
|
{
|
||||||
const int hist1 = *hist1p;
|
s32 hist = 0;
|
||||||
const int hist2 = *hist2p;
|
|
||||||
|
|
||||||
int hist = 0;
|
|
||||||
switch (q >> 4)
|
switch (q >> 4)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -36,17 +30,17 @@ short ADPDecodeSample(int bits, int q, int *hist1p, int *hist2p)
|
||||||
if (hist > 0x1fffff) hist = 0x1fffff;
|
if (hist > 0x1fffff) hist = 0x1fffff;
|
||||||
if (hist < -0x200000) hist = -0x200000;
|
if (hist < -0x200000) hist = -0x200000;
|
||||||
|
|
||||||
int cur = (((short)(bits << 12) >> (q & 0xf)) << 6) + hist;
|
s32 cur = (((s16)(bits << 12) >> (q & 0xf)) << 6) + hist;
|
||||||
|
|
||||||
*hist2p = *hist1p;
|
hist2 = hist1;
|
||||||
*hist1p = cur;
|
hist1 = cur;
|
||||||
|
|
||||||
cur >>= 6;
|
cur >>= 6;
|
||||||
|
|
||||||
if (cur < -0x8000) return -0x8000;
|
if (cur < -0x8000) return -0x8000;
|
||||||
if (cur > 0x7fff) return 0x7fff;
|
if (cur > 0x7fff) return 0x7fff;
|
||||||
|
|
||||||
return (short)cur;
|
return (s16)cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NGCADPCM::InitFilter()
|
void NGCADPCM::InitFilter()
|
||||||
|
@ -57,11 +51,11 @@ void NGCADPCM::InitFilter()
|
||||||
histr2 = 0;
|
histr2 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NGCADPCM::DecodeBlock(short *pcm, const u8 *adpcm)
|
void NGCADPCM::DecodeBlock(s16 *pcm, const u8 *adpcm)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < SAMPLES_PER_BLOCK; i++)
|
for (int i = 0; i < SAMPLES_PER_BLOCK; i++)
|
||||||
{
|
{
|
||||||
pcm[i * 2] = ADPDecodeSample(adpcm[i + (ONE_BLOCK_SIZE - SAMPLES_PER_BLOCK)] & 0xf, adpcm[0], &histl1, &histl2);
|
pcm[i * 2] = ADPDecodeSample(adpcm[i + (ONE_BLOCK_SIZE - SAMPLES_PER_BLOCK)] & 0xf, adpcm[0], histl1, histl2);
|
||||||
pcm[i * 2 + 1] = ADPDecodeSample(adpcm[i + (ONE_BLOCK_SIZE - SAMPLES_PER_BLOCK)] >> 4, adpcm[1], &histr1, &histr2);
|
pcm[i * 2 + 1] = ADPDecodeSample(adpcm[i + (ONE_BLOCK_SIZE - SAMPLES_PER_BLOCK)] >> 4, adpcm[1], histr1, histr2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,14 @@
|
||||||
class NGCADPCM
|
class NGCADPCM
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ONE_BLOCK_SIZE = 32,
|
||||||
|
SAMPLES_PER_BLOCK = 28
|
||||||
|
};
|
||||||
|
|
||||||
static void InitFilter();
|
static void InitFilter();
|
||||||
static void DecodeBlock(short *pcm, const u8 *adpcm);
|
static void DecodeBlock(s16 *pcm, const u8 *adpcm);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -151,8 +151,6 @@ u32 ConvertMillisecondsToTicks(u32 _Milliseconds)
|
||||||
|
|
||||||
void AICallback(u64 userdata, int cyclesLate)
|
void AICallback(u64 userdata, int cyclesLate)
|
||||||
{
|
{
|
||||||
// Update disk streaming. All that code really needs a revamp, including replacing the codec with the one
|
|
||||||
// from in_cube.
|
|
||||||
AudioInterface::Update();
|
AudioInterface::Update();
|
||||||
CoreTiming::ScheduleEvent(AI_PERIOD - cyclesLate, et_AI);
|
CoreTiming::ScheduleEvent(AI_PERIOD - cyclesLate, et_AI);
|
||||||
}
|
}
|
||||||
|
@ -166,7 +164,7 @@ void DSPCallback(u64 userdata, int cyclesLate)
|
||||||
|
|
||||||
void AudioDMACallback(u64 userdata, int cyclesLate)
|
void AudioDMACallback(u64 userdata, int cyclesLate)
|
||||||
{
|
{
|
||||||
int period = CPU_CORE_CLOCK / (AudioInterface::GetDSPSampleRate() * 4 / 32);
|
int period = CPU_CORE_CLOCK / (AudioInterface::GetAIDSampleRate() * 4 / 32);
|
||||||
DSP::UpdateAudioDMA(); // Push audio to speakers.
|
DSP::UpdateAudioDMA(); // Push audio to speakers.
|
||||||
CoreTiming::ScheduleEvent(period - cyclesLate, et_AudioDMA);
|
CoreTiming::ScheduleEvent(period - cyclesLate, et_AudioDMA);
|
||||||
}
|
}
|
||||||
|
@ -280,7 +278,7 @@ void Init()
|
||||||
AI_PERIOD = GetTicksPerSecond() / 80;
|
AI_PERIOD = GetTicksPerSecond() / 80;
|
||||||
|
|
||||||
// System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA
|
// System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA
|
||||||
AUDIO_DMA_PERIOD = CPU_CORE_CLOCK / (AudioInterface::GetDSPSampleRate() * 4 / 32);
|
AUDIO_DMA_PERIOD = CPU_CORE_CLOCK / (AudioInterface::GetAIDSampleRate() * 4 / 32);
|
||||||
|
|
||||||
Common::Timer::IncreaseResolution();
|
Common::Timer::IncreaseResolution();
|
||||||
// store and convert localtime at boot to timebase ticks
|
// store and convert localtime at boot to timebase ticks
|
||||||
|
|
Loading…
Reference in New Issue