commit
11d304ae29
|
@ -28,10 +28,7 @@ namespace AudioCommon
|
|||
{
|
||||
SoundStream *InitSoundStream(void *hWnd)
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
delete soundStream;
|
||||
CMixer *mixer = new CMixer(AISampleRate, DACSampleRate, 48000);
|
||||
CMixer *mixer = new CMixer(48000);
|
||||
|
||||
// TODO: possible memleak with mixer
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "AudioCommon/Mixer.h"
|
||||
#include "Common/Atomic.h"
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/AudioInterface.h"
|
||||
|
@ -19,20 +20,8 @@
|
|||
#endif
|
||||
|
||||
// Executed from sound stream thread
|
||||
unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_framelimit)
|
||||
unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, bool consider_framelimit)
|
||||
{
|
||||
if (!samples)
|
||||
return 0;
|
||||
|
||||
std::lock_guard<std::mutex> lk(m_csMixing);
|
||||
|
||||
if (PowerPC::GetState() != PowerPC::CPU_RUNNING)
|
||||
{
|
||||
// Silence
|
||||
memset(samples, 0, numSamples * 4);
|
||||
return numSamples;
|
||||
}
|
||||
|
||||
unsigned int currentSample = 0;
|
||||
|
||||
// Cache access in non-volatile variable
|
||||
|
@ -45,7 +34,7 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_
|
|||
u32 indexR = Common::AtomicLoad(m_indexR);
|
||||
u32 indexW = Common::AtomicLoad(m_indexW);
|
||||
|
||||
float numLeft = ((indexW - indexR) & INDEX_MASK) / 2;
|
||||
float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2);
|
||||
m_numLeftI = (numLeft + m_numLeftI*(CONTROL_AVG-1)) / CONTROL_AVG;
|
||||
float offset = (m_numLeftI - LOW_WATERMARK) * CONTROL_FACTOR;
|
||||
if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT;
|
||||
|
@ -56,29 +45,36 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_
|
|||
//remember fractional offset
|
||||
|
||||
u32 framelimit = SConfig::GetInstance().m_Framelimit;
|
||||
float aid_sample_rate = AudioInterface::GetAIDSampleRate() + offset;
|
||||
float aid_sample_rate = m_input_sample_rate + offset;
|
||||
if (consider_framelimit && framelimit > 2)
|
||||
{
|
||||
aid_sample_rate = aid_sample_rate * (framelimit - 1) * 5 / VideoInterface::TargetRefreshRate;
|
||||
}
|
||||
|
||||
static u32 frac = 0;
|
||||
const u32 ratio = (u32)( 65536.0f * aid_sample_rate / (float)m_sampleRate );
|
||||
const u32 ratio = (u32)( 65536.0f * aid_sample_rate / (float)m_mixer->m_sampleRate );
|
||||
|
||||
if (ratio > 0x10000)
|
||||
ERROR_LOG(AUDIO, "ratio out of range");
|
||||
s32 lvolume = m_LVolume;
|
||||
s32 rvolume = m_RVolume;
|
||||
|
||||
// TODO: consider a higher-quality resampling algorithm.
|
||||
for (; currentSample < numSamples*2 && ((indexW-indexR) & INDEX_MASK) > 2; currentSample+=2) {
|
||||
u32 indexR2 = indexR + 2; //next sample
|
||||
|
||||
s16 l1 = Common::swap16(m_buffer[indexR & INDEX_MASK]); //current
|
||||
s16 l2 = Common::swap16(m_buffer[indexR2 & INDEX_MASK]); //next
|
||||
int sampleL = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16;
|
||||
sampleL = (sampleL * lvolume) >> 8;
|
||||
sampleL += samples[currentSample + 1];
|
||||
MathUtil::Clamp(&sampleL, -32767, 32767);
|
||||
samples[currentSample+1] = sampleL;
|
||||
|
||||
s16 r1 = Common::swap16(m_buffer[(indexR + 1) & INDEX_MASK]); //current
|
||||
s16 r2 = Common::swap16(m_buffer[(indexR2 + 1) & INDEX_MASK]); //next
|
||||
int sampleR = ((r1 << 16) + (r2 - r1) * (u16)frac) >> 16;
|
||||
sampleR = (sampleR * rvolume) >> 8;
|
||||
sampleR += samples[currentSample];
|
||||
MathUtil::Clamp(&sampleR, -32767, 32767);
|
||||
samples[currentSample] = sampleR;
|
||||
|
||||
frac += ratio;
|
||||
|
@ -87,36 +83,57 @@ unsigned int CMixer::Mix(short* samples, unsigned int numSamples, bool consider_
|
|||
}
|
||||
|
||||
// Padding
|
||||
unsigned short s[2];
|
||||
short s[2];
|
||||
s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]);
|
||||
s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]);
|
||||
for (; currentSample < numSamples*2; currentSample+=2)
|
||||
s[0] = (s[0] * lvolume) >> 8;
|
||||
s[1] = (s[1] * rvolume) >> 8;
|
||||
for (; currentSample < numSamples * 2; currentSample += 2)
|
||||
{
|
||||
samples[currentSample] = s[0];
|
||||
samples[currentSample+1] = s[1];
|
||||
int sampleR = s[0] + samples[currentSample];
|
||||
MathUtil::Clamp(&sampleR, -32767, 32767);
|
||||
samples[currentSample] = sampleR;
|
||||
int sampleL = s[1] + samples[currentSample + 1];
|
||||
MathUtil::Clamp(&sampleL, -32767, 32767);
|
||||
samples[currentSample + 1] = sampleL;
|
||||
}
|
||||
|
||||
// Flush cached variable
|
||||
Common::AtomicStore(m_indexR, indexR);
|
||||
|
||||
// Add the DTK Music
|
||||
// Re-sampling is done inside
|
||||
AudioInterface::Callback_GetStreaming(samples, numSamples, m_sampleRate);
|
||||
if (m_logAudio)
|
||||
g_wave_writer.AddStereoSamples(samples, numSamples);
|
||||
|
||||
return numSamples;
|
||||
}
|
||||
|
||||
unsigned int CMixer::Mix(short* samples, unsigned int num_samples, bool consider_framelimit)
|
||||
{
|
||||
if (!samples)
|
||||
return 0;
|
||||
|
||||
void CMixer::PushSamples(const short *samples, unsigned int num_samples)
|
||||
std::lock_guard<std::mutex> lk(m_csMixing);
|
||||
|
||||
memset(samples, 0, num_samples * 2 * sizeof(short));
|
||||
|
||||
if (PowerPC::GetState() != PowerPC::CPU_RUNNING)
|
||||
{
|
||||
// Silence
|
||||
return num_samples;
|
||||
}
|
||||
|
||||
m_dma_mixer.Mix(samples, num_samples, consider_framelimit);
|
||||
m_streaming_mixer.Mix(samples, num_samples, consider_framelimit);
|
||||
if (m_logAudio)
|
||||
g_wave_writer.AddStereoSamples(samples, num_samples);
|
||||
return num_samples;
|
||||
}
|
||||
|
||||
void CMixer::MixerFifo::PushSamples(const short *samples, unsigned int num_samples)
|
||||
{
|
||||
// Cache access in non-volatile variable
|
||||
// indexR isn't allowed to cache in the audio throttling loop as it
|
||||
// needs to get updates to not deadlock.
|
||||
u32 indexW = Common::AtomicLoad(m_indexW);
|
||||
|
||||
if (m_throttle)
|
||||
if (m_mixer->m_throttle)
|
||||
{
|
||||
// The auto throttle function. This loop will put a ceiling on the CPU MHz.
|
||||
while (num_samples * 2 + ((indexW - Common::AtomicLoad(m_indexR)) & INDEX_MASK) >= MAX_SAMPLES * 2)
|
||||
|
@ -155,3 +172,23 @@ void CMixer::PushSamples(const short *samples, unsigned int num_samples)
|
|||
return;
|
||||
}
|
||||
|
||||
void CMixer::PushSamples(const short *samples, unsigned int num_samples)
|
||||
{
|
||||
m_dma_mixer.PushSamples(samples, num_samples);
|
||||
}
|
||||
|
||||
void CMixer::PushStreamingSamples(const short *samples, unsigned int num_samples)
|
||||
{
|
||||
m_streaming_mixer.PushSamples(samples, num_samples);
|
||||
}
|
||||
|
||||
void CMixer::SetStreamingVolume(unsigned int lvolume, unsigned int rvolume)
|
||||
{
|
||||
m_streaming_mixer.SetVolume(lvolume, rvolume);
|
||||
}
|
||||
|
||||
void CMixer::MixerFifo::SetVolume(unsigned int lvolume, unsigned int rvolume)
|
||||
{
|
||||
m_LVolume = lvolume + (lvolume >> 7);
|
||||
m_RVolume = rvolume + (rvolume >> 7);
|
||||
}
|
||||
|
|
|
@ -15,29 +15,21 @@
|
|||
|
||||
#define LOW_WATERMARK 1280 // 40 ms
|
||||
#define MAX_FREQ_SHIFT 200 // per 32000 Hz
|
||||
#define CONTROL_FACTOR 0.2 // in freq_shift per fifo size offset
|
||||
#define CONTROL_FACTOR 0.2f // in freq_shift per fifo size offset
|
||||
#define CONTROL_AVG 32
|
||||
|
||||
class CMixer {
|
||||
|
||||
public:
|
||||
CMixer(unsigned int AISampleRate = 48000, unsigned int DACSampleRate = 48000, unsigned int BackendSampleRate = 32000)
|
||||
: m_aiSampleRate(AISampleRate)
|
||||
, m_dacSampleRate(DACSampleRate)
|
||||
, m_bits(16)
|
||||
, m_channels(2)
|
||||
CMixer(unsigned int BackendSampleRate)
|
||||
: m_dma_mixer(this, 32000)
|
||||
, m_streaming_mixer(this, 48000)
|
||||
, m_sampleRate(BackendSampleRate)
|
||||
, m_logAudio(0)
|
||||
, m_indexW(0)
|
||||
, m_indexR(0)
|
||||
, m_numLeftI(0.0f)
|
||||
, m_throttle(false)
|
||||
, m_speed(0)
|
||||
{
|
||||
// AyuanX: The internal (Core & DSP) sample rate is fixed at 32KHz
|
||||
// So when AI/DAC sample rate differs than 32KHz, we have to do re-sampling
|
||||
m_sampleRate = BackendSampleRate;
|
||||
|
||||
memset(m_buffer, 0, sizeof(m_buffer));
|
||||
|
||||
INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized (AISampleRate:%i, DACSampleRate:%i)", AISampleRate, DACSampleRate);
|
||||
INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized");
|
||||
}
|
||||
|
||||
virtual ~CMixer() {}
|
||||
|
@ -47,7 +39,9 @@ public:
|
|||
|
||||
// Called from main thread
|
||||
virtual void PushSamples(const short* samples, unsigned int num_samples);
|
||||
unsigned int GetSampleRate() const {return m_sampleRate;}
|
||||
virtual void PushStreamingSamples(const short* samples, unsigned int num_samples);
|
||||
unsigned int GetSampleRate() const { return m_sampleRate; }
|
||||
void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume);
|
||||
|
||||
void SetThrottle(bool use) { m_throttle = use;}
|
||||
|
||||
|
@ -87,11 +81,36 @@ public:
|
|||
void UpdateSpeed(volatile float val) { m_speed = val; }
|
||||
|
||||
protected:
|
||||
class MixerFifo {
|
||||
public:
|
||||
MixerFifo(CMixer *mixer, unsigned sample_rate)
|
||||
: m_mixer(mixer)
|
||||
, m_input_sample_rate(sample_rate)
|
||||
, m_indexW(0)
|
||||
, m_indexR(0)
|
||||
, m_numLeftI(0.0f)
|
||||
, m_LVolume(256)
|
||||
, m_RVolume(256)
|
||||
{
|
||||
memset(m_buffer, 0, sizeof(m_buffer));
|
||||
}
|
||||
void PushSamples(const short* samples, unsigned int num_samples);
|
||||
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
|
||||
void SetVolume(unsigned int lvolume, unsigned int rvolume);
|
||||
private:
|
||||
CMixer *m_mixer;
|
||||
unsigned m_input_sample_rate;
|
||||
short m_buffer[MAX_SAMPLES * 2];
|
||||
volatile u32 m_indexW;
|
||||
volatile u32 m_indexR;
|
||||
// Volume ranges from 0-256
|
||||
volatile s32 m_LVolume;
|
||||
volatile s32 m_RVolume;
|
||||
float m_numLeftI;
|
||||
};
|
||||
MixerFifo m_dma_mixer;
|
||||
MixerFifo m_streaming_mixer;
|
||||
unsigned int m_sampleRate;
|
||||
unsigned int m_aiSampleRate;
|
||||
unsigned int m_dacSampleRate;
|
||||
int m_bits;
|
||||
int m_channels;
|
||||
|
||||
WaveFileWriter g_wave_writer;
|
||||
|
||||
|
@ -99,14 +118,7 @@ protected:
|
|||
|
||||
bool m_throttle;
|
||||
|
||||
short m_buffer[MAX_SAMPLES * 2];
|
||||
volatile u32 m_indexW;
|
||||
volatile u32 m_indexR;
|
||||
|
||||
std::mutex m_csMixing;
|
||||
float m_numLeftI;
|
||||
|
||||
volatile float m_speed; // Current rate of the emulation (1.0 = 100% speed)
|
||||
private:
|
||||
|
||||
};
|
||||
|
|
|
@ -50,6 +50,8 @@ This file mainly deals with the [Drive I/F], however [AIDFR] controls
|
|||
TODO maybe the files should be merged?
|
||||
*/
|
||||
|
||||
#include "AudioCommon/AudioCommon.h"
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/MathUtil.h"
|
||||
|
||||
|
@ -59,7 +61,6 @@ This file mainly deals with the [Drive I/F], however [AIDFR] controls
|
|||
#include "Core/HW/DVDInterface.h"
|
||||
#include "Core/HW/MMIO.h"
|
||||
#include "Core/HW/ProcessorInterface.h"
|
||||
#include "Core/HW/StreamADPCM.h"
|
||||
#include "Core/HW/SystemTimers.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
||||
|
@ -145,8 +146,7 @@ void DoState(PointerWrap &p)
|
|||
static void GenerateAudioInterrupt();
|
||||
static void UpdateInterrupts();
|
||||
static void IncreaseSampleCount(const u32 _uAmount);
|
||||
void ReadStreamBlock(s16* _pPCM);
|
||||
u64 GetAIPeriod();
|
||||
static u64 GetAIPeriod();
|
||||
int et_AI;
|
||||
|
||||
void Init()
|
||||
|
@ -234,7 +234,10 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
|||
|
||||
mmio->Register(base | AI_VOLUME_REGISTER,
|
||||
MMIO::DirectRead<u32>(&m_Volume.hex),
|
||||
MMIO::DirectWrite<u32>(&m_Volume.hex)
|
||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
||||
m_Volume.hex = val;
|
||||
soundStream->GetMixer()->SetStreamingVolume(m_Volume.left, m_Volume.right);
|
||||
})
|
||||
);
|
||||
|
||||
mmio->Register(base | AI_SAMPLE_COUNTER,
|
||||
|
@ -272,133 +275,6 @@ void GenerateAISInterrupt()
|
|||
GenerateAudioInterrupt();
|
||||
}
|
||||
|
||||
void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate)
|
||||
{
|
||||
_AISampleRate = g_AISSampleRate;
|
||||
_DACSampleRate = g_AIDSampleRate;
|
||||
}
|
||||
|
||||
// Callback for the disc streaming
|
||||
// WARNING - called from audio thread
|
||||
unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples, unsigned int _sampleRate)
|
||||
{
|
||||
if (m_Control.PSTAT && !CCPU::IsStepping())
|
||||
{
|
||||
static int pos = 0;
|
||||
static short pcm[NGCADPCM::SAMPLES_PER_BLOCK*2];
|
||||
const int lvolume = m_Volume.left;
|
||||
const int rvolume = m_Volume.right;
|
||||
|
||||
if (g_AISSampleRate == 48000 && _sampleRate == 32000)
|
||||
{
|
||||
_dbg_assert_msg_(AUDIO_INTERFACE, !(_numSamples & 1), "Number of Samples: %i must be even!", _numSamples);
|
||||
_numSamples = _numSamples * 3 / 2;
|
||||
}
|
||||
|
||||
int pcm_l = 0, pcm_r = 0;
|
||||
for (unsigned int i = 0; i < _numSamples; i++)
|
||||
{
|
||||
if (pos == 0)
|
||||
ReadStreamBlock(pcm);
|
||||
|
||||
if (g_AISSampleRate == 48000 && _sampleRate == 32000) //downsample 48>32
|
||||
{
|
||||
if (i % 3)
|
||||
{
|
||||
pcm_l = (((pcm_l + (int)pcm[pos*2]) / 2 * lvolume) >> 8) + (int)(*_pDestBuffer);
|
||||
MathUtil::Clamp(&pcm_l, -32767, 32767);
|
||||
*_pDestBuffer++ = pcm_l;
|
||||
|
||||
pcm_r = (((pcm_r + (int)pcm[pos*2+1]) / 2 * rvolume) >> 8) + (int)(*_pDestBuffer);
|
||||
MathUtil::Clamp(&pcm_r, -32767, 32767);
|
||||
*_pDestBuffer++ = pcm_r;
|
||||
}
|
||||
pcm_l = pcm[pos*2];
|
||||
pcm_r = pcm[pos*2+1];
|
||||
|
||||
pos++;
|
||||
}
|
||||
else if (g_AISSampleRate == 32000 && _sampleRate == 48000) //upsample 32>48
|
||||
{
|
||||
//starts with one sample of 0
|
||||
const u32 ratio = (u32)( 65536.0f * 32000.0f / (float)_sampleRate );
|
||||
static u32 frac = 0;
|
||||
|
||||
static s16 l1 = 0;
|
||||
static s16 l2 = 0;
|
||||
|
||||
if (frac >= 0x10000 || frac == 0)
|
||||
{
|
||||
frac &= 0xffff;
|
||||
|
||||
l1 = l2; //current
|
||||
l2 = pcm[pos * 2]; //next
|
||||
}
|
||||
|
||||
pcm_l = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16;
|
||||
pcm_r = ((l1 << 16) + (l2 - l1) * (u16)frac) >> 16;
|
||||
|
||||
|
||||
pcm_l = (pcm_l * lvolume >> 8) + (int)(*_pDestBuffer);
|
||||
MathUtil::Clamp(&pcm_l, -32767, 32767);
|
||||
*_pDestBuffer++ = pcm_l;
|
||||
|
||||
pcm_r = (pcm_r * lvolume >> 8) + (int)(*_pDestBuffer);
|
||||
MathUtil::Clamp(&pcm_r, -32767, 32767);
|
||||
*_pDestBuffer++ = pcm_r;
|
||||
|
||||
frac += ratio;
|
||||
pos += frac >> 16;
|
||||
|
||||
}
|
||||
else //1:1 no resampling
|
||||
{
|
||||
pcm_l = (((int)pcm[pos*2] * lvolume) >> 8) + (int)(*_pDestBuffer);
|
||||
MathUtil::Clamp(&pcm_l, -32767, 32767);
|
||||
*_pDestBuffer++ = pcm_l;
|
||||
|
||||
pcm_r = (((int)pcm[pos*2+1] * rvolume) >> 8) + (int)(*_pDestBuffer);
|
||||
MathUtil::Clamp(&pcm_r, -32767, 32767);
|
||||
*_pDestBuffer++ = pcm_r;
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (pos == NGCADPCM::SAMPLES_PER_BLOCK)
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't overwrite existed sample data
|
||||
/*
|
||||
for (unsigned int i = 0; i < _numSamples * 2; i++)
|
||||
{
|
||||
_pDestBuffer[i] = 0; //silence!
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return _numSamples;
|
||||
}
|
||||
|
||||
// WARNING - called from audio thread
|
||||
void ReadStreamBlock(s16 *_pPCM)
|
||||
{
|
||||
u8 tempADPCM[NGCADPCM::ONE_BLOCK_SIZE];
|
||||
if (DVDInterface::DVDReadADPCM(tempADPCM, NGCADPCM::ONE_BLOCK_SIZE))
|
||||
{
|
||||
NGCADPCM::DecodeBlock(_pPCM, tempADPCM);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(_pPCM, 0, NGCADPCM::SAMPLES_PER_BLOCK*2);
|
||||
}
|
||||
|
||||
// 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.
|
||||
}
|
||||
|
||||
static void IncreaseSampleCount(const u32 _iAmount)
|
||||
{
|
||||
if (m_Control.PSTAT)
|
||||
|
|
|
@ -22,10 +22,6 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
|||
|
||||
void Update(u64 userdata, int cyclesLate);
|
||||
|
||||
// Called by DSP emulator
|
||||
void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate);
|
||||
unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples, unsigned int _sampleRate = 48000);
|
||||
|
||||
// Get the audio rates (48000 or 32000 only)
|
||||
unsigned int GetAIDSampleRate();
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "AudioCommon/AudioCommon.h"
|
||||
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Thread.h"
|
||||
|
@ -208,6 +210,7 @@ u32 g_ErrorCode = 0;
|
|||
bool g_bDiscInside = false;
|
||||
bool g_bStream = false;
|
||||
int tc = 0;
|
||||
int dtk = 0;
|
||||
|
||||
static u64 g_last_read_offset;
|
||||
static u64 g_last_read_time;
|
||||
|
@ -215,10 +218,6 @@ static u64 g_last_read_time;
|
|||
// GC-AM only
|
||||
static unsigned char media_buffer[0x40];
|
||||
|
||||
// Needed because data and streaming audio access needs to be managed by the "drive"
|
||||
// (both requests can happen at the same time, audio takes precedence)
|
||||
static std::mutex dvdread_section;
|
||||
|
||||
static int ejectDisc;
|
||||
static int insertDisc;
|
||||
|
||||
|
@ -262,6 +261,63 @@ void TransferComplete(u64 userdata, int cyclesLate)
|
|||
FinishExecuteRead();
|
||||
}
|
||||
|
||||
static u32 ProcessDTKSamples(short *tempPCM, u32 num_samples)
|
||||
{
|
||||
u32 samples_processed = 0;
|
||||
do
|
||||
{
|
||||
if (AudioPos >= CurrentStart + CurrentLength)
|
||||
{
|
||||
AudioPos = LoopStart;
|
||||
CurrentStart = LoopStart;
|
||||
CurrentLength = LoopLength;
|
||||
NGCADPCM::InitFilter();
|
||||
AudioInterface::GenerateAISInterrupt();
|
||||
|
||||
// If there isn't any audio to stream, stop streaming.
|
||||
if (AudioPos >= CurrentStart + CurrentLength)
|
||||
{
|
||||
g_bStream = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
u8 tempADPCM[NGCADPCM::ONE_BLOCK_SIZE];
|
||||
// TODO: What if we can't read from AudioPos?
|
||||
VolumeHandler::ReadToPtr(tempADPCM, AudioPos, sizeof(tempADPCM));
|
||||
AudioPos += sizeof(tempADPCM);
|
||||
NGCADPCM::DecodeBlock(tempPCM + samples_processed * 2, tempADPCM);
|
||||
samples_processed += NGCADPCM::SAMPLES_PER_BLOCK;
|
||||
} while (samples_processed < num_samples);
|
||||
for (unsigned i = 0; i < samples_processed * 2; ++i)
|
||||
{
|
||||
// TODO: Fix the mixer so it can accept non-byte-swapped samples.
|
||||
tempPCM[i] = Common::swap16(tempPCM[i]);
|
||||
}
|
||||
return samples_processed;
|
||||
}
|
||||
|
||||
void DTKStreamingCallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
// Send audio to the mixer.
|
||||
static const int NUM_SAMPLES = 48000 / 2000 * 7; // 3.5ms of 48kHz samples
|
||||
short tempPCM[NUM_SAMPLES * 2];
|
||||
unsigned samples_processed;
|
||||
if (g_bStream)
|
||||
{
|
||||
samples_processed = ProcessDTKSamples(tempPCM, NUM_SAMPLES);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(tempPCM, 0, sizeof(tempPCM));
|
||||
samples_processed = NUM_SAMPLES;
|
||||
}
|
||||
soundStream->GetMixer()->PushStreamingSamples(tempPCM, samples_processed);
|
||||
|
||||
int ticks_to_dtk = int(SystemTimers::GetTicksPerSecond() * u64(samples_processed) / 48000);
|
||||
CoreTiming::ScheduleEvent(ticks_to_dtk - cyclesLate, dtk);
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
m_DISR.Hex = 0;
|
||||
|
@ -288,6 +344,9 @@ void Init()
|
|||
insertDisc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback);
|
||||
|
||||
tc = CoreTiming::RegisterEvent("TransferComplete", TransferComplete);
|
||||
dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback);
|
||||
|
||||
CoreTiming::ScheduleEvent(0, dtk);
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
|
@ -369,57 +428,9 @@ void ClearCoverInterrupt()
|
|||
|
||||
bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength)
|
||||
{
|
||||
// We won't need the crit sec when DTK streaming has been rewritten correctly.
|
||||
std::lock_guard<std::mutex> lk(dvdread_section);
|
||||
return VolumeHandler::ReadToPtr(Memory::GetPointer(_iRamAddress), _iDVDOffset, _iLength);
|
||||
}
|
||||
|
||||
bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples)
|
||||
{
|
||||
_iNumSamples &= ~31;
|
||||
|
||||
if (AudioPos == 0)
|
||||
{
|
||||
memset(_pDestBuffer, 0, _iNumSamples); // probably __AI_SRC_INIT :P
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(dvdread_section);
|
||||
VolumeHandler::ReadToPtr(_pDestBuffer, AudioPos, _iNumSamples);
|
||||
}
|
||||
|
||||
// loop check
|
||||
if (g_bStream)
|
||||
{
|
||||
AudioPos += _iNumSamples;
|
||||
|
||||
if (AudioPos >= CurrentStart + CurrentLength)
|
||||
{
|
||||
if (LoopStart == 0)
|
||||
{
|
||||
AudioPos = 0;
|
||||
CurrentStart = 0;
|
||||
CurrentLength = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioPos = LoopStart;
|
||||
CurrentStart = LoopStart;
|
||||
CurrentLength = LoopLength;
|
||||
}
|
||||
NGCADPCM::InitFilter();
|
||||
AudioInterface::GenerateAISInterrupt();
|
||||
}
|
||||
|
||||
//WARN_LOG(DVDINTERFACE,"ReadADPCM");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
mmio->Register(base | DI_STATUS_REGISTER,
|
||||
|
@ -927,7 +938,6 @@ void ExecuteCommand()
|
|||
CurrentStart = pos;
|
||||
CurrentLength = length;
|
||||
NGCADPCM::InitFilter();
|
||||
g_bStream = true;
|
||||
}
|
||||
|
||||
LoopStart = pos;
|
||||
|
@ -990,11 +1000,13 @@ void ExecuteCommand()
|
|||
case DVDLowAudioBufferConfig:
|
||||
if (m_DICMDBUF[0].CMDBYTE1 == 1)
|
||||
{
|
||||
// TODO: What is this actually supposed to do?
|
||||
g_bStream = true;
|
||||
WARN_LOG(DVDINTERFACE, "(Audio): Audio enabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: What is this actually supposed to do?
|
||||
g_bStream = false;
|
||||
WARN_LOG(DVDINTERFACE, "(Audio): Audio disabled");
|
||||
}
|
||||
|
|
|
@ -33,11 +33,8 @@ void ClearCoverInterrupt();
|
|||
|
||||
// DVD Access Functions
|
||||
bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength);
|
||||
// For AudioInterface
|
||||
bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples);
|
||||
extern bool g_bStream;
|
||||
|
||||
|
||||
// Not sure about endianness here. I'll just name them like this...
|
||||
enum DIErrorLow
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue