Sound System Rework: Phase 2
. Performance boost (Completely non-blocking between Sound thread and CPU thread, in the meantime keeping them thread safe) . Both 32KHz & 48KHz sound can be handled properly now (But up-sampling is still not implemented, and I don't think any game requires it.) . Strategy adjustment When your PC is *NOT* capable to run the game at 100%: >> DSound Could yield more fluent sound than OpenAL sometimes, but you will lose the sync between video & audio (since audio is played before video to guarantee fluency) >> OpenAL Ensures video & audio are always sync'ed, but sound could be intermittent(to let slow video catch up) . Changed default frame limit to: Auto (Somehow this can dramatically decrease the chance of wiimote desync in game NSMB) git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4724 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
0d0a7c515f
commit
9eea60ca69
|
@ -30,6 +30,31 @@ extern DSPInitialize g_dspInitialize;
|
||||||
extern SoundStream *soundStream;
|
extern SoundStream *soundStream;
|
||||||
extern AudioCommonConfig ac_Config;
|
extern AudioCommonConfig ac_Config;
|
||||||
|
|
||||||
|
// UDSPControl
|
||||||
|
union UDSPControl
|
||||||
|
{
|
||||||
|
u16 Hex;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned DSPReset : 1; // Write 1 to reset and waits for 0
|
||||||
|
unsigned DSPAssertInt : 1;
|
||||||
|
unsigned DSPHalt : 1;
|
||||||
|
|
||||||
|
unsigned AI : 1;
|
||||||
|
unsigned AI_mask : 1;
|
||||||
|
unsigned ARAM : 1;
|
||||||
|
unsigned ARAM_mask : 1;
|
||||||
|
unsigned DSP : 1;
|
||||||
|
unsigned DSP_mask : 1;
|
||||||
|
|
||||||
|
unsigned ARAM_DMAState : 1; // DSPGetDMAStatus() uses this flag
|
||||||
|
unsigned DSPInitCode : 1;
|
||||||
|
unsigned DSPInit : 1; // DSPInit() writes to this flag
|
||||||
|
unsigned pad : 4;
|
||||||
|
};
|
||||||
|
UDSPControl(u16 _Hex = 0) : Hex(_Hex) {}
|
||||||
|
};
|
||||||
|
|
||||||
namespace AudioCommon
|
namespace AudioCommon
|
||||||
{
|
{
|
||||||
SoundStream *InitSoundStream(CMixer *mixer = NULL);
|
SoundStream *InitSoundStream(CMixer *mixer = NULL);
|
||||||
|
|
|
@ -111,13 +111,11 @@ void DSound::SoundLoop()
|
||||||
int numBytesToRender = FIX128(ModBufferSize(currentPos - lastPos));
|
int numBytesToRender = FIX128(ModBufferSize(currentPos - lastPos));
|
||||||
if (numBytesToRender >= 256)
|
if (numBytesToRender >= 256)
|
||||||
{
|
{
|
||||||
if (numBytesToRender > sizeof(realtimeBuffer))
|
if (numBytesToRender > sizeof(realtimeBuffer) * sizeof(short))
|
||||||
PanicAlert("soundThread: too big render call");
|
PanicAlert("soundThread: too big render call");
|
||||||
m_mixer->Mix(realtimeBuffer, numBytesToRender >> 2);
|
m_mixer->Mix(realtimeBuffer, numBytesToRender / 4);
|
||||||
WriteDataToBuffer(lastPos, (char*)realtimeBuffer, numBytesToRender);
|
WriteDataToBuffer(lastPos, (char*)realtimeBuffer, numBytesToRender);
|
||||||
currentPos = ModBufferSize(lastPos + numBytesToRender);
|
lastPos = ModBufferSize(lastPos + numBytesToRender);
|
||||||
totalRenderedBytes += numBytesToRender;
|
|
||||||
lastPos = currentPos;
|
|
||||||
}
|
}
|
||||||
soundCriticalSection.Leave();
|
soundCriticalSection.Leave();
|
||||||
soundSyncEvent.Wait();
|
soundSyncEvent.Wait();
|
||||||
|
@ -142,7 +140,6 @@ bool DSound::Start()
|
||||||
dsBuffer->Lock(0, bufferSize, (void* *)&p1, &num1, 0, 0, 0);
|
dsBuffer->Lock(0, bufferSize, (void* *)&p1, &num1, 0, 0, 0);
|
||||||
memset(p1, 0, num1);
|
memset(p1, 0, num1);
|
||||||
dsBuffer->Unlock(p1, num1, 0, 0);
|
dsBuffer->Unlock(p1, num1, 0, 0);
|
||||||
totalRenderedBytes = -bufferSize;
|
|
||||||
thread = new Common::Thread(soundThread, (void *)this);
|
thread = new Common::Thread(soundThread, (void *)this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
#include <dsound.h>
|
#include <dsound.h>
|
||||||
|
|
||||||
#define BUFSIZE 32768
|
#define BUFSIZE (1024 * 8 * 4)
|
||||||
#define MAXWAIT 70 // miliseconds
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class DSound : public SoundStream
|
class DSound : public SoundStream
|
||||||
|
@ -41,31 +40,30 @@ class DSound : public SoundStream
|
||||||
IDirectSoundBuffer* dsBuffer;
|
IDirectSoundBuffer* dsBuffer;
|
||||||
|
|
||||||
int bufferSize; //i bytes
|
int bufferSize; //i bytes
|
||||||
int totalRenderedBytes;
|
|
||||||
int m_volume;
|
int m_volume;
|
||||||
|
|
||||||
// playback position
|
// playback position
|
||||||
int currentPos;
|
int currentPos;
|
||||||
int lastPos;
|
int lastPos;
|
||||||
short realtimeBuffer[1024 * 1024];
|
short realtimeBuffer[BUFSIZE / sizeof(short)];
|
||||||
|
|
||||||
inline int FIX128(int x) {
|
inline int FIX128(int x)
|
||||||
|
{
|
||||||
return x & (~127);
|
return x & (~127);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int ModBufferSize(int x) {
|
inline int ModBufferSize(int x)
|
||||||
|
{
|
||||||
return (x + bufferSize) % bufferSize;
|
return (x + bufferSize) % bufferSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CreateBuffer();
|
bool CreateBuffer();
|
||||||
bool WriteDataToBuffer(DWORD dwOffset, char* soundData,
|
bool WriteDataToBuffer(DWORD dwOffset, char* soundData, DWORD dwSoundBytes);
|
||||||
DWORD dwSoundBytes);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DSound(CMixer *mixer, void *hWnd = NULL)
|
DSound(CMixer *mixer, void *hWnd = NULL)
|
||||||
: SoundStream(mixer)
|
: SoundStream(mixer)
|
||||||
, bufferSize(0)
|
, bufferSize(0)
|
||||||
, totalRenderedBytes(0)
|
|
||||||
, currentPos(0)
|
, currentPos(0)
|
||||||
, lastPos(0)
|
, lastPos(0)
|
||||||
, dsBuffer(0)
|
, dsBuffer(0)
|
||||||
|
|
|
@ -16,112 +16,65 @@
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
|
|
||||||
// This queue solution is temporary. I'll implement something more efficient later.
|
#include "Atomic.h"
|
||||||
#include <queue> // System
|
|
||||||
|
|
||||||
#include "Thread.h" // Common
|
|
||||||
|
|
||||||
#include "Mixer.h"
|
#include "Mixer.h"
|
||||||
#include "FixedSizeQueue.h"
|
|
||||||
#include "AudioCommon.h"
|
#include "AudioCommon.h"
|
||||||
|
|
||||||
int CMixer::Mix(short *samples, int numSamples)
|
// Executed from sound stream thread
|
||||||
|
unsigned int CMixer::Mix(short* samples, unsigned int numSamples)
|
||||||
{
|
{
|
||||||
if (! samples) {
|
if (!samples)
|
||||||
Premix(NULL, 0);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
// silence
|
|
||||||
memset(samples, 0, numSamples * 2 * sizeof(short));
|
|
||||||
|
|
||||||
if (g_dspInitialize.pEmulatorState) {
|
if (g_dspInitialize.pEmulatorState)
|
||||||
|
{
|
||||||
if (*g_dspInitialize.pEmulatorState != 0)
|
if (*g_dspInitialize.pEmulatorState != 0)
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// first get the DTK Music
|
|
||||||
if (m_EnableDTKMusic) {
|
|
||||||
g_dspInitialize.pGetAudioStreaming(samples, numSamples);
|
|
||||||
}
|
|
||||||
|
|
||||||
Premix(samples, numSamples);
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
push_sync.Enter();
|
|
||||||
while (m_queueSize > queue_minlength && count < numSamples * 2)
|
|
||||||
{
|
{
|
||||||
int x = samples[count];
|
// Silence
|
||||||
x += sample_queue.front();
|
memset(samples, 0, numSamples * 4);
|
||||||
if (x > 32767) x = 32767;
|
return numSamples;
|
||||||
if (x < -32767) x = -32767;
|
|
||||||
samples[count++] = x;
|
|
||||||
sample_queue.pop();
|
|
||||||
x = samples[count];
|
|
||||||
x += sample_queue.front();
|
|
||||||
if (x > 32767) x = 32767;
|
|
||||||
if (x < -32767) x = -32767;
|
|
||||||
samples[count++] = x;
|
|
||||||
sample_queue.pop();
|
|
||||||
m_queueSize-=2;
|
|
||||||
}
|
}
|
||||||
push_sync.Leave();
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int numLeft = Common::AtomicLoad(m_numSamples);
|
||||||
|
numLeft = (numLeft > numSamples) ? numSamples : numLeft;
|
||||||
|
|
||||||
void CMixer::PushSamples(short *samples, int num_stereo_samples, int core_sample_rate)
|
// Do re-sampling if needed
|
||||||
|
if (m_sampleRate == m_dspSampleRate)
|
||||||
{
|
{
|
||||||
push_sync.Enter();
|
for (unsigned int i = 0; i < numLeft * 2; i++)
|
||||||
if (m_queueSize == 0)
|
samples[i] = Common::swap16(m_buffer[(m_indexR + i) & INDEX_MASK]);
|
||||||
|
m_indexR += numLeft * 2;
|
||||||
|
}
|
||||||
|
else if (m_sampleRate < m_dspSampleRate) // If down-sampling needed
|
||||||
{
|
{
|
||||||
m_queueSize = queue_minlength;
|
_dbg_assert_msg_(DSPHLE, !(numSamples % 2), "Number of Samples: %i must be even!", numSamples);
|
||||||
for (int i = 0; i < queue_minlength; i++)
|
|
||||||
sample_queue.push((s16)0);
|
|
||||||
}
|
|
||||||
push_sync.Leave();
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
short *pDest = samples;
|
||||||
if (GetAsyncKeyState(VK_TAB))
|
int last_l, last_r, cur_l, cur_r;
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Write Other Audio
|
for (unsigned int i = 0; i < numLeft * 3 / 2; i++)
|
||||||
if (!m_throttle)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// The auto throttle function. This loop will put a ceiling on the CPU MHz.
|
|
||||||
// ----------------------------
|
|
||||||
/* This is only needed for non-AX sound, currently directly
|
|
||||||
streamed and DTK sound. For AX we call SoundStream::Update in
|
|
||||||
AXTask() for example. */
|
|
||||||
while (m_queueSize > queue_maxlength / 2)
|
|
||||||
{
|
{
|
||||||
// Urgh.
|
cur_l = Common::swap16(m_buffer[(m_indexR + i * 2) & INDEX_MASK]);
|
||||||
if (g_dspInitialize.pEmulatorState) {
|
cur_r = Common::swap16(m_buffer[(m_indexR + i * 2 + 1) & INDEX_MASK]);
|
||||||
if (*g_dspInitialize.pEmulatorState != 0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
soundStream->Update();
|
|
||||||
SLEEP(1);
|
|
||||||
}
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
|
|
||||||
push_sync.Enter();
|
if (i % 3)
|
||||||
while (num_stereo_samples)
|
|
||||||
{
|
{
|
||||||
sample_queue.push(Common::swap16(*samples));
|
*pDest++ = (last_l + cur_r) / 2;
|
||||||
samples++;
|
*pDest++ = (last_r + cur_r) / 2;
|
||||||
sample_queue.push(Common::swap16(*samples));
|
|
||||||
samples++;
|
|
||||||
m_queueSize += 2;
|
|
||||||
num_stereo_samples--;
|
|
||||||
}
|
}
|
||||||
push_sync.Leave();
|
|
||||||
return;
|
|
||||||
|
|
||||||
|
last_l = cur_l;
|
||||||
|
last_r = cur_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_indexR += numLeft * 2 * 3 / 2;
|
||||||
|
}
|
||||||
|
else if (m_sampleRate > m_dspSampleRate)
|
||||||
|
{
|
||||||
|
// AyuanX: Up-sampling is not implemented yet
|
||||||
|
PanicAlert("Mixer: Up-sampling is not implemented yet!");
|
||||||
/*
|
/*
|
||||||
static int PV1l=0,PV2l=0,PV3l=0,PV4l=0;
|
static int PV1l=0,PV2l=0,PV3l=0,PV4l=0;
|
||||||
static int PV1r=0,PV2r=0,PV3r=0,PV4r=0;
|
static int PV1r=0,PV2r=0,PV3r=0,PV4r=0;
|
||||||
|
@ -183,16 +136,93 @@ void CMixer::PushSamples(short *samples, int num_stereo_samples, int core_sample
|
||||||
sample_queue.push(r);
|
sample_queue.push(r);
|
||||||
m_queueSize += 2;
|
m_queueSize += 2;
|
||||||
}
|
}
|
||||||
push_sync.Leave();
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int CMixer::GetNumSamples()
|
// Padding
|
||||||
|
if (numSamples > numLeft)
|
||||||
|
memset(&samples[numLeft * 2], 0, (numSamples - numLeft) * 4);
|
||||||
|
|
||||||
|
// Add the HLE sound
|
||||||
|
if (m_sampleRate < m_dspSampleRate)
|
||||||
{
|
{
|
||||||
return m_queueSize / 2;
|
PanicAlert("Mixer: DSPHLE down-sampling is not implemented yet!\n"
|
||||||
//int ret = (m_queueSize - queue_minlength) / 2;
|
"Usually no game should require this, please report!");
|
||||||
//ret = (ret > 0) ? ret : 0;
|
}
|
||||||
//return ret;
|
else
|
||||||
|
{
|
||||||
|
Premix(samples, numSamples, m_sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the DTK Music
|
||||||
|
if (m_EnableDTKMusic)
|
||||||
|
{
|
||||||
|
// Re-sampling is done inside
|
||||||
|
g_dspInitialize.pGetAudioStreaming(samples, numSamples, m_sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::AtomicAdd(m_numSamples, -(int)numLeft);
|
||||||
|
|
||||||
|
return numSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CMixer::PushSamples(short *samples, unsigned int num_samples, unsigned int sample_rate)
|
||||||
|
{
|
||||||
|
// The auto throttle function. This loop will put a ceiling on the CPU MHz.
|
||||||
|
if (m_throttle)
|
||||||
|
{
|
||||||
|
// AyuanX: Remember to reserve "num_samples * 1.5" free sample space at least!
|
||||||
|
// Becuse we may do re-sampling later
|
||||||
|
while (Common::AtomicLoad(m_numSamples) >= MAX_SAMPLES - RESERVED_SAMPLES)
|
||||||
|
{
|
||||||
|
if (g_dspInitialize.pEmulatorState)
|
||||||
|
{
|
||||||
|
if (*g_dspInitialize.pEmulatorState != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
soundStream->Update();
|
||||||
|
SLEEP(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough free space
|
||||||
|
if (num_samples > MAX_SAMPLES - Common::AtomicLoad(m_numSamples))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// AyuanX: Actual re-sampling work has been moved to sound thread
|
||||||
|
// to alleviates the workload on main thread
|
||||||
|
// and we simply store raw data here to make fast mem copy
|
||||||
|
int over_bytes = num_samples * 4 - (MAX_SAMPLES * 2 - (m_indexW & INDEX_MASK)) * sizeof(short);
|
||||||
|
if (over_bytes > 0)
|
||||||
|
{
|
||||||
|
memcpy(&m_buffer[m_indexW & INDEX_MASK], samples, num_samples * 4 - over_bytes);
|
||||||
|
memcpy(&m_buffer[0], samples + (num_samples * 4 - over_bytes) / sizeof(short), over_bytes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(&m_buffer[m_indexW & INDEX_MASK], samples, num_samples * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_indexW += num_samples * 2;
|
||||||
|
|
||||||
|
if (m_sampleRate < m_dspSampleRate)
|
||||||
|
{
|
||||||
|
// This is kind of tricky :P
|
||||||
|
num_samples = num_samples * 2 / 3;
|
||||||
|
}
|
||||||
|
else if (m_sampleRate > m_dspSampleRate)
|
||||||
|
{
|
||||||
|
PanicAlert("Mixer: Up-sampling is not implemented yet!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::AtomicAdd(m_numSamples, num_samples);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int CMixer::GetNumSamples()
|
||||||
|
{
|
||||||
|
return Common::AtomicLoad(m_numSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,39 +18,38 @@
|
||||||
#ifndef _MIXER_H_
|
#ifndef _MIXER_H_
|
||||||
#define _MIXER_H_
|
#define _MIXER_H_
|
||||||
|
|
||||||
#include "FixedSizeQueue.h"
|
// 16 bit Stereo
|
||||||
#include "Thread.h"
|
#define MAX_SAMPLES (1024 * 4)
|
||||||
|
#define INDEX_MASK (MAX_SAMPLES * 2 - 1)
|
||||||
// On real hardware, this fifo is much, much smaller. But timing is also
|
#define RESERVED_SAMPLES (MAX_SAMPLES / 8)
|
||||||
// tighter than under Windows, so...
|
|
||||||
#define queue_minlength 1024 * 4
|
|
||||||
#define queue_maxlength 1024 * 28
|
|
||||||
|
|
||||||
class CMixer {
|
class CMixer {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// AyuanX: Mixer sample rate is fixed to 32khz for now
|
CMixer(unsigned int AISampleRate = 48000, unsigned int DSPSampleRate = 48000)
|
||||||
// if any game sets DSP sample rate to 48khz, we are doomed
|
: m_aiSampleRate(AISampleRate)
|
||||||
// TODO: Fix this somehow!
|
, m_dspSampleRate(DSPSampleRate)
|
||||||
CMixer(unsigned int SampleRate = 32000)
|
|
||||||
: m_sampleRate(SampleRate)
|
|
||||||
, m_bits(16)
|
, m_bits(16)
|
||||||
, m_channels(2)
|
, m_channels(2)
|
||||||
, m_mode(2)
|
|
||||||
, m_HLEready(false)
|
, m_HLEready(false)
|
||||||
, m_queueSize(0)
|
, m_numSamples(0)
|
||||||
{}
|
, m_indexW(0)
|
||||||
|
, m_indexR(0)
|
||||||
|
{
|
||||||
|
// AyuanX: When sample rate differs, we have to do re-sampling
|
||||||
|
// I perfer speed so let's do down-sampling instead of up-sampling
|
||||||
|
// If you like better sound than speed, feel free to implement the up-sampling code
|
||||||
|
m_sampleRate = (m_aiSampleRate < m_dspSampleRate) ? m_aiSampleRate : m_dspSampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
// Called from audio threads
|
// Called from audio threads
|
||||||
virtual int Mix(short *sample, int numSamples);
|
virtual unsigned int Mix(short* samples, unsigned int numSamples);
|
||||||
virtual int GetNumSamples();
|
virtual void Premix(short *samples, unsigned int numSamples, unsigned int sampleRate) {}
|
||||||
|
unsigned int GetNumSamples();
|
||||||
|
|
||||||
// Called from main thread
|
// Called from main thread
|
||||||
virtual void PushSamples(short* samples, int num_stereo_samples, int core_sample_rate);
|
virtual void PushSamples(short* samples, unsigned int num_samples, unsigned int sample_rate);
|
||||||
|
unsigned int GetSampleRate() {return m_sampleRate;}
|
||||||
virtual void Premix(short *samples, int numSamples) {}
|
|
||||||
|
|
||||||
int GetSampleRate() {return m_sampleRate;}
|
|
||||||
|
|
||||||
void SetThrottle(bool use) { m_throttle = use;}
|
void SetThrottle(bool use) { m_throttle = use;}
|
||||||
void SetDTKMusic(bool use) { m_EnableDTKMusic = use;}
|
void SetDTKMusic(bool use) { m_EnableDTKMusic = use;}
|
||||||
|
@ -61,19 +60,23 @@ public:
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int m_sampleRate;
|
unsigned int m_sampleRate;
|
||||||
|
unsigned int m_aiSampleRate;
|
||||||
|
unsigned int m_dspSampleRate;
|
||||||
int m_bits;
|
int m_bits;
|
||||||
int m_channels;
|
int m_channels;
|
||||||
|
|
||||||
int m_mode;
|
|
||||||
bool m_HLEready;
|
bool m_HLEready;
|
||||||
int m_queueSize;
|
|
||||||
|
|
||||||
bool m_EnableDTKMusic;
|
bool m_EnableDTKMusic;
|
||||||
bool m_throttle;
|
bool m_throttle;
|
||||||
|
|
||||||
|
short m_buffer[MAX_SAMPLES * 2];
|
||||||
|
u32 m_indexW;
|
||||||
|
u32 m_indexR;
|
||||||
|
volatile u32 m_numSamples;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Common::CriticalSection push_sync;
|
|
||||||
FixedSizeQueue<s16, queue_maxlength> sample_queue;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,10 @@
|
||||||
#include "Mixer.h"
|
#include "Mixer.h"
|
||||||
|
|
||||||
class NullMixer : public CMixer {
|
class NullMixer : public CMixer {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual int Mix(short *sample, int numSamples) {return 0;}
|
virtual unsigned int Mix(short *samples, unsigned int numSamples) { return 0; }
|
||||||
virtual void PushSamples(short* samples, int num_stereo_samples,
|
virtual void PushSamples(short* samples, unsigned int num_samples, unsigned int sample_rate) {}
|
||||||
int core_sample_rate) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class NullSound : public SoundStream
|
class NullSound : public SoundStream
|
||||||
|
@ -35,7 +35,6 @@ public:
|
||||||
{
|
{
|
||||||
delete m_mixer;
|
delete m_mixer;
|
||||||
m_mixer = new NullMixer();
|
m_mixer = new NullMixer();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~NullSound() {}
|
virtual ~NullSound() {}
|
||||||
|
@ -47,7 +46,7 @@ public:
|
||||||
virtual bool Start() { return true; }
|
virtual bool Start() { return true; }
|
||||||
|
|
||||||
virtual void Update() {
|
virtual void Update() {
|
||||||
m_mixer->Mix(NULL, 256 >> 2);
|
//m_mixer->Mix(NULL, 256 >> 2);
|
||||||
//(*callback)(NULL, 256 >> 2, 16, sampleRate, 2);
|
//(*callback)(NULL, 256 >> 2, 16, sampleRate, 2);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -138,12 +138,13 @@ void OpenALStream::SoundLoop()
|
||||||
// Generate a Source to playback the Buffers
|
// Generate a Source to playback the Buffers
|
||||||
alGenSources(1, &uiSource);
|
alGenSources(1, &uiSource);
|
||||||
|
|
||||||
memset(realtimeBuffer, 0, OAL_BUFFER_SIZE);
|
// Short Silence
|
||||||
|
memset(realtimeBuffer, 0, OAL_MAX_SAMPLES * 4);
|
||||||
for (int i = 0; i < OAL_NUM_BUFFERS; i++)
|
for (int i = 0; i < OAL_NUM_BUFFERS; i++)
|
||||||
alBufferData(uiBuffers[i], AL_FORMAT_STEREO16, realtimeBuffer, OAL_BUFFER_SIZE, ulFrequency);
|
alBufferData(uiBuffers[i], AL_FORMAT_STEREO16, realtimeBuffer, OAL_MAX_SAMPLES, ulFrequency);
|
||||||
|
|
||||||
alSourceQueueBuffers(uiSource, OAL_NUM_BUFFERS, uiBuffers);
|
alSourceQueueBuffers(uiSource, OAL_NUM_BUFFERS, uiBuffers);
|
||||||
alSourcePlay(uiSource);
|
alSourcePlay(uiSource);
|
||||||
|
|
||||||
err = alGetError();
|
err = alGetError();
|
||||||
// TODO: Error handling
|
// TODO: Error handling
|
||||||
|
|
||||||
|
@ -158,12 +159,12 @@ void OpenALStream::SoundLoop()
|
||||||
alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &iBuffersProcessed);
|
alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &iBuffersProcessed);
|
||||||
iBuffersFilled = 0;
|
iBuffersFilled = 0;
|
||||||
}
|
}
|
||||||
int numSamples = m_mixer->GetNumSamples();
|
|
||||||
numSamples &= ~0x100;
|
|
||||||
|
|
||||||
if (iBuffersProcessed && numSamples)
|
unsigned int numSamples = m_mixer->GetNumSamples();
|
||||||
|
|
||||||
|
if (iBuffersProcessed && (numSamples >= OAL_THRESHOLD))
|
||||||
{
|
{
|
||||||
numSamples = (numSamples > OAL_BUFFER_SIZE / 4) ? OAL_BUFFER_SIZE / 4 : numSamples;
|
numSamples = (numSamples > OAL_MAX_SAMPLES) ? OAL_MAX_SAMPLES : numSamples;
|
||||||
// Remove the Buffer from the Queue. (uiBuffer contains the Buffer ID for the unqueued Buffer)
|
// Remove the Buffer from the Queue. (uiBuffer contains the Buffer ID for the unqueued Buffer)
|
||||||
if (iBuffersFilled == 0)
|
if (iBuffersFilled == 0)
|
||||||
alSourceUnqueueBuffers(uiSource, iBuffersProcessed, uiBufferTemp);
|
alSourceUnqueueBuffers(uiSource, iBuffersProcessed, uiBufferTemp);
|
||||||
|
@ -176,11 +177,11 @@ void OpenALStream::SoundLoop()
|
||||||
if (iBuffersFilled == OAL_NUM_BUFFERS)
|
if (iBuffersFilled == OAL_NUM_BUFFERS)
|
||||||
alSourcePlay(uiSource);
|
alSourcePlay(uiSource);
|
||||||
}
|
}
|
||||||
else
|
else if (numSamples >= OAL_THRESHOLD)
|
||||||
{
|
{
|
||||||
ALint state = 0;
|
ALint state = 0;
|
||||||
alGetSourcei(uiSource, AL_SOURCE_STATE, &state);
|
alGetSourcei(uiSource, AL_SOURCE_STATE, &state);
|
||||||
if (state != AL_PLAYING)
|
if (state == AL_STOPPED)
|
||||||
alSourcePlay(uiSource);
|
alSourcePlay(uiSource);
|
||||||
}
|
}
|
||||||
soundSyncEvent.Wait();
|
soundSyncEvent.Wait();
|
||||||
|
|
|
@ -33,10 +33,11 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
// public use
|
// 16 bit Stereo
|
||||||
#define SFX_MAX_SOURCE 1
|
#define SFX_MAX_SOURCE 1
|
||||||
#define OAL_NUM_BUFFERS 8
|
#define OAL_NUM_BUFFERS 8
|
||||||
#define OAL_BUFFER_SIZE (512 * 4)
|
#define OAL_MAX_SAMPLES 512 // AyuanX: Don't make it too large, as larger buffer means longer delay
|
||||||
|
#define OAL_THRESHOLD 128
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class OpenALStream: public SoundStream
|
class OpenALStream: public SoundStream
|
||||||
|
@ -66,7 +67,7 @@ private:
|
||||||
Common::CriticalSection soundCriticalSection;
|
Common::CriticalSection soundCriticalSection;
|
||||||
Common::Event soundSyncEvent;
|
Common::Event soundSyncEvent;
|
||||||
|
|
||||||
short realtimeBuffer[OAL_BUFFER_SIZE/sizeof(short)];
|
short realtimeBuffer[OAL_MAX_SAMPLES * 2];
|
||||||
ALuint uiBuffers[OAL_NUM_BUFFERS];
|
ALuint uiBuffers[OAL_NUM_BUFFERS];
|
||||||
ALuint uiSource;
|
ALuint uiSource;
|
||||||
ALfloat fVolume;
|
ALfloat fVolume;
|
||||||
|
|
|
@ -27,7 +27,7 @@ typedef void (__cdecl* TDSP_WriteMailBox)(bool _CPUMailbox, unsigned short);
|
||||||
typedef unsigned short (__cdecl* TDSP_ReadMailBox)(bool _CPUMailbox);
|
typedef unsigned short (__cdecl* TDSP_ReadMailBox)(bool _CPUMailbox);
|
||||||
typedef unsigned short (__cdecl* TDSP_ReadControlRegister)();
|
typedef unsigned short (__cdecl* TDSP_ReadControlRegister)();
|
||||||
typedef unsigned short (__cdecl* TDSP_WriteControlRegister)(unsigned short);
|
typedef unsigned short (__cdecl* TDSP_WriteControlRegister)(unsigned short);
|
||||||
typedef void (__cdecl *TDSP_SendAIBuffer)(unsigned int address, int sample_rate);
|
typedef void (__cdecl *TDSP_SendAIBuffer)(unsigned int address, unsigned int num_samples, unsigned int sample_rate);
|
||||||
typedef void (__cdecl *TDSP_Update)(int cycles);
|
typedef void (__cdecl *TDSP_Update)(int cycles);
|
||||||
typedef void (__cdecl *TDSP_StopSoundStream)();
|
typedef void (__cdecl *TDSP_StopSoundStream)();
|
||||||
typedef void (__cdecl *TDSP_ClearAudioBuffer)();
|
typedef void (__cdecl *TDSP_ClearAudioBuffer)();
|
||||||
|
|
|
@ -234,7 +234,7 @@ void SConfig::LoadSettings()
|
||||||
ini.Get("Core", "RunCompareServer", &m_LocalCoreStartupParameter.bRunCompareServer, false);
|
ini.Get("Core", "RunCompareServer", &m_LocalCoreStartupParameter.bRunCompareServer, false);
|
||||||
ini.Get("Core", "RunCompareClient", &m_LocalCoreStartupParameter.bRunCompareClient, false);
|
ini.Get("Core", "RunCompareClient", &m_LocalCoreStartupParameter.bRunCompareClient, false);
|
||||||
ini.Get("Core", "TLBHack", &m_LocalCoreStartupParameter.iTLBHack, 0);
|
ini.Get("Core", "TLBHack", &m_LocalCoreStartupParameter.iTLBHack, 0);
|
||||||
ini.Get("Core", "FrameLimit", &m_Framelimit, 1);
|
ini.Get("Core", "FrameLimit", &m_Framelimit, 0); // auto frame limit by default
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
ini.Get("Core", "GFXPlugin", &m_LocalCoreStartupParameter.m_strVideoPlugin, m_DefaultGFXPlugin.c_str());
|
ini.Get("Core", "GFXPlugin", &m_LocalCoreStartupParameter.m_strVideoPlugin, m_DefaultGFXPlugin.c_str());
|
||||||
|
|
|
@ -386,6 +386,7 @@ THREAD_RETURN EmuThread(void *pArg)
|
||||||
dspInit.pDebuggerBreak = Callback_DebuggerBreak;
|
dspInit.pDebuggerBreak = Callback_DebuggerBreak;
|
||||||
dspInit.pGenerateDSPInterrupt = Callback_DSPInterrupt;
|
dspInit.pGenerateDSPInterrupt = Callback_DSPInterrupt;
|
||||||
dspInit.pGetAudioStreaming = AudioInterface::Callback_GetStreaming;
|
dspInit.pGetAudioStreaming = AudioInterface::Callback_GetStreaming;
|
||||||
|
dspInit.pGetSampleRate = AudioInterface::Callback_GetSampleRate;
|
||||||
dspInit.pEmulatorState = (int *)PowerPC::GetStatePtr();
|
dspInit.pEmulatorState = (int *)PowerPC::GetStatePtr();
|
||||||
dspInit.bWii = _CoreParameter.bWii;
|
dspInit.bWii = _CoreParameter.bWii;
|
||||||
dspInit.bOnThread = _CoreParameter.bDSPThread;
|
dspInit.bOnThread = _CoreParameter.bDSPThread;
|
||||||
|
|
|
@ -54,13 +54,13 @@ union AICR
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
unsigned PSTAT : 1; // sample counter/playback enable
|
unsigned PSTAT : 1; // sample counter/playback enable
|
||||||
unsigned AFR : 1; // 0=32khz 1=48khz
|
unsigned AFR : 1; // AI Frequency (0=32khz 1=48khz)
|
||||||
unsigned AIINTMSK : 1; // 0=interrupt masked 1=interrupt enabled
|
unsigned AIINTMSK : 1; // 0=interrupt masked 1=interrupt enabled
|
||||||
unsigned AIINT : 1; // audio interrupt status
|
unsigned AIINT : 1; // audio interrupt status
|
||||||
unsigned AIINTVLD : 1; // This bit controls whether AIINT is affected by the AIIT register
|
unsigned AIINTVLD : 1; // This bit controls whether AIINT is affected by the AIIT register
|
||||||
// matching AISLRCNT. Once set, AIINT will hold
|
// matching AISLRCNT. Once set, AIINT will hold
|
||||||
unsigned SCRESET : 1; // write to reset counter
|
unsigned SCRESET : 1; // write to reset counter
|
||||||
unsigned DSPFR : 1; // DSP Frequency (0=48khz 1=32khz) WTF, who designed this?
|
unsigned DSPFR : 1; // DSP Frequency (0=48khz 1=32khz)
|
||||||
unsigned :25;
|
unsigned :25;
|
||||||
};
|
};
|
||||||
u32 hex;
|
u32 hex;
|
||||||
|
@ -90,8 +90,8 @@ struct SAudioRegister
|
||||||
// STATE_TO_SAVE
|
// STATE_TO_SAVE
|
||||||
static SAudioRegister g_AudioRegister;
|
static SAudioRegister g_AudioRegister;
|
||||||
static u64 g_LastCPUTime = 0;
|
static u64 g_LastCPUTime = 0;
|
||||||
static int g_SampleRate = 32000;
|
static unsigned int g_SampleRate = 32000;
|
||||||
static int g_DSPSampleRate = 32000;
|
static unsigned int g_DSPSampleRate = 32000;
|
||||||
static u64 g_CPUCyclesPerSample = 0xFFFFFFFFFFFULL;
|
static u64 g_CPUCyclesPerSample = 0xFFFFFFFFFFFULL;
|
||||||
|
|
||||||
void DoState(PointerWrap &p)
|
void DoState(PointerWrap &p)
|
||||||
|
@ -264,9 +264,15 @@ void GenerateAudioInterrupt()
|
||||||
UpdateInterrupts();
|
UpdateInterrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DSPSampleRate)
|
||||||
|
{
|
||||||
|
_AISampleRate = g_SampleRate;
|
||||||
|
_DSPSampleRate = g_DSPSampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
// Callback for the disc streaming
|
// Callback for the disc streaming
|
||||||
// WARNING - called from audio thread
|
// WARNING - called from audio thread
|
||||||
u32 Callback_GetStreaming(short* _pDestBuffer, u32 _numSamples)
|
unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples, unsigned int _sampleRate)
|
||||||
{
|
{
|
||||||
if (g_AudioRegister.m_Control.PSTAT && !CCPU::IsStepping())
|
if (g_AudioRegister.m_Control.PSTAT && !CCPU::IsStepping())
|
||||||
{
|
{
|
||||||
|
@ -275,34 +281,60 @@ u32 Callback_GetStreaming(short* _pDestBuffer, u32 _numSamples)
|
||||||
const int lvolume = g_AudioRegister.m_Volume.leftVolume;
|
const int lvolume = g_AudioRegister.m_Volume.leftVolume;
|
||||||
const int rvolume = g_AudioRegister.m_Volume.rightVolume;
|
const int rvolume = g_AudioRegister.m_Volume.rightVolume;
|
||||||
|
|
||||||
// AyuanX: I hate this, but for now we have to do down-sampling to support 48khz
|
|
||||||
if (g_SampleRate == 48000)
|
if (g_SampleRate == 48000 && _sampleRate == 32000)
|
||||||
{
|
{
|
||||||
_dbg_assert_msg_(AUDIO_INTERFACE, !(_numSamples % 2), "Number of Samples: %i must be even!", _numSamples);
|
_dbg_assert_msg_(AUDIO_INTERFACE, !(_numSamples % 2), "Number of Samples: %i must be even!", _numSamples);
|
||||||
_numSamples = _numSamples * 3 / 2;
|
_numSamples = _numSamples * 3 / 2;
|
||||||
}
|
}
|
||||||
|
else if (g_SampleRate == 32000 && _sampleRate == 48000)
|
||||||
|
{
|
||||||
|
// AyuanX: Up-sampling is not implemented yet
|
||||||
|
PanicAlert("AUDIO_INTERFACE: Up-sampling is not implemented yet!");
|
||||||
|
}
|
||||||
|
|
||||||
short pcm_l = 0;
|
int pcm_l, pcm_r;
|
||||||
short pcm_r = 0;
|
|
||||||
for (unsigned int i = 0; i < _numSamples; i++)
|
for (unsigned int i = 0; i < _numSamples; i++)
|
||||||
{
|
{
|
||||||
if (pos == 0)
|
if (pos == 0)
|
||||||
ReadStreamBlock(pcm);
|
ReadStreamBlock(pcm);
|
||||||
|
|
||||||
if (g_SampleRate == 48000)
|
if (g_SampleRate == 48000 && _sampleRate == 32000)
|
||||||
{
|
{
|
||||||
if (i % 3)
|
if (i % 3)
|
||||||
{
|
{
|
||||||
*_pDestBuffer++ = ((pcm_l / 2 + pcm[pos*2] / 2) * lvolume) >> 8;
|
pcm_l = (((pcm_l + (int)pcm[pos*2]) / 2 * lvolume) >> 8) + (int)(*_pDestBuffer);
|
||||||
*_pDestBuffer++ = ((pcm_r / 2 + pcm[pos*2+1] / 2) * rvolume) >> 8;
|
if (pcm_l > 32767)
|
||||||
|
pcm_l = 32767;
|
||||||
|
else if (pcm_l < -32767)
|
||||||
|
pcm_l = -32767;
|
||||||
|
*_pDestBuffer++ = pcm_l;
|
||||||
|
|
||||||
|
pcm_r = (((pcm_r + (int)pcm[pos*2+1]) / 2 * rvolume) >> 8) + (int)(*_pDestBuffer);
|
||||||
|
if (pcm_r > 32767)
|
||||||
|
pcm_r = 32767;
|
||||||
|
else if (pcm_r < -32767)
|
||||||
|
pcm_r = -32767;
|
||||||
|
*_pDestBuffer++ = pcm_r;
|
||||||
}
|
}
|
||||||
pcm_l = pcm[pos*2];
|
pcm_l = pcm[pos*2];
|
||||||
pcm_r = pcm[pos*2+1];
|
pcm_r = pcm[pos*2+1];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*_pDestBuffer++ = (pcm[pos*2] * lvolume) >> 8;
|
pcm_l = (((int)pcm[pos*2] * lvolume) >> 8) + (int)(*_pDestBuffer);
|
||||||
*_pDestBuffer++ = (pcm[pos*2+1] * rvolume) >> 8;
|
if (pcm_l > 32767)
|
||||||
|
pcm_l = 32767;
|
||||||
|
else if (pcm_l < -32767)
|
||||||
|
pcm_l = -32767;
|
||||||
|
*_pDestBuffer++ = pcm_l;
|
||||||
|
|
||||||
|
pcm_r = (((int)pcm[pos*2+1] * rvolume) >> 8) + (int)(*_pDestBuffer);
|
||||||
|
if (pcm_r > 32767)
|
||||||
|
pcm_r = 32767;
|
||||||
|
else if (pcm_r < -32767)
|
||||||
|
pcm_r = -32767;
|
||||||
|
*_pDestBuffer++ = pcm_r;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++pos == 28)
|
if (++pos == 28)
|
||||||
|
@ -311,7 +343,7 @@ u32 Callback_GetStreaming(short* _pDestBuffer, u32 _numSamples)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// AyuanX: We have already preset those bytes, no need to do this again
|
// Don't overwrite existed sample data
|
||||||
/*
|
/*
|
||||||
for (unsigned int i = 0; i < _numSamples * 2; i++)
|
for (unsigned int i = 0; i < _numSamples * 2; i++)
|
||||||
{
|
{
|
||||||
|
@ -361,12 +393,12 @@ void IncreaseSampleCount(const u32 _iAmount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetAISampleRate()
|
unsigned int GetAISampleRate()
|
||||||
{
|
{
|
||||||
return g_SampleRate;
|
return g_SampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetDSPSampleRate()
|
unsigned int GetDSPSampleRate()
|
||||||
{
|
{
|
||||||
return g_DSPSampleRate;
|
return g_DSPSampleRate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,14 +34,15 @@ void DoState(PointerWrap &p);
|
||||||
void Update();
|
void Update();
|
||||||
|
|
||||||
// Called by DSP plugin
|
// Called by DSP plugin
|
||||||
u32 Callback_GetStreaming(short* _pDestBuffer, u32 _numSamples);
|
void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DSPSampleRate);
|
||||||
|
unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples, unsigned int _sampleRate = 48000);
|
||||||
|
|
||||||
void Read32(u32& _uReturnValue, const u32 _iAddress);
|
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)
|
||||||
u32 GetAISampleRate();
|
unsigned int GetAISampleRate();
|
||||||
u32 GetDSPSampleRate();
|
unsigned int GetDSPSampleRate();
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
|
@ -197,7 +197,6 @@ static u16 g_AR_SIZE;
|
||||||
static u16 g_AR_MODE;
|
static u16 g_AR_MODE;
|
||||||
static u16 g_AR_REFRESH;
|
static u16 g_AR_REFRESH;
|
||||||
|
|
||||||
|
|
||||||
Common::PluginDSP *dsp_plugin;
|
Common::PluginDSP *dsp_plugin;
|
||||||
|
|
||||||
|
|
||||||
|
@ -379,27 +378,6 @@ void Write16(const u16 _Value, const u32 _Address)
|
||||||
g_dspState.DSPControl.DSPHalt = tmpControl.DSPHalt;
|
g_dspState.DSPControl.DSPHalt = tmpControl.DSPHalt;
|
||||||
g_dspState.DSPControl.DSPInit = tmpControl.DSPInit;
|
g_dspState.DSPControl.DSPInit = tmpControl.DSPInit;
|
||||||
|
|
||||||
// AyuanX: WTF, sample rate between AI & DSP can be different?
|
|
||||||
// This is a big problem especially when our mixer is fixed to 32000
|
|
||||||
// TODO: Try to support these!
|
|
||||||
// More info: AudioCommon/Mixer.h, HW/AudioInterface.cpp
|
|
||||||
static bool FirstTimeWarning = false;
|
|
||||||
if (!FirstTimeWarning)
|
|
||||||
{
|
|
||||||
if (!g_dspState.DSPControl.DSPHalt && g_dspState.DSPControl.DSPInit)
|
|
||||||
{
|
|
||||||
// It's time to check now, and we do this only once
|
|
||||||
FirstTimeWarning = true;
|
|
||||||
if (AudioInterface::GetAISampleRate() != 32000 || AudioInterface::GetDSPSampleRate() != 32000)
|
|
||||||
{
|
|
||||||
WARN_LOG(DSPINTERFACE, "Unsupported Sample Rate, AI:%i, DSP:%i", AudioInterface::GetAISampleRate(), AudioInterface::GetDSPSampleRate());
|
|
||||||
if (AudioInterface::GetDSPSampleRate() != 32000)
|
|
||||||
PanicAlert("DSPINTERFACE: Unsupported Sample Rate, AI:%i, DSP:%i\n"
|
|
||||||
"You may get incorrect sound output, please report!", AudioInterface::GetAISampleRate(), AudioInterface::GetDSPSampleRate());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interrupt (mask)
|
// Interrupt (mask)
|
||||||
g_dspState.DSPControl.AID_mask = tmpControl.AID_mask;
|
g_dspState.DSPControl.AID_mask = tmpControl.AID_mask;
|
||||||
g_dspState.DSPControl.ARAM_mask = tmpControl.ARAM_mask;
|
g_dspState.DSPControl.ARAM_mask = tmpControl.ARAM_mask;
|
||||||
|
@ -503,12 +481,17 @@ void UpdateAudioDMA()
|
||||||
// external audio fifo in the emulator, to be mixed with the disc
|
// external audio fifo in the emulator, to be mixed with the disc
|
||||||
// streaming output. If that audio queue fills up, we delay the
|
// streaming output. If that audio queue fills up, we delay the
|
||||||
// emulator.
|
// emulator.
|
||||||
dsp_plugin->DSP_SendAIBuffer(g_audioDMA.ReadAddress, AudioInterface::GetDSPSampleRate());
|
|
||||||
g_audioDMA.ReadAddress += 32;
|
// AyuanX: let's do it in a bundle to speed up
|
||||||
|
if (g_audioDMA.BlocksLeft == g_audioDMA.AudioDMAControl.NumBlocks)
|
||||||
|
dsp_plugin->DSP_SendAIBuffer(g_audioDMA.SourceAddress, g_audioDMA.AudioDMAControl.NumBlocks * 8, AudioInterface::GetDSPSampleRate());
|
||||||
|
|
||||||
|
// g_audioDMA.ReadAddress += 32;
|
||||||
g_audioDMA.BlocksLeft--;
|
g_audioDMA.BlocksLeft--;
|
||||||
|
|
||||||
if (g_audioDMA.BlocksLeft == 0)
|
if (g_audioDMA.BlocksLeft == 0)
|
||||||
{
|
{
|
||||||
g_audioDMA.ReadAddress = g_audioDMA.SourceAddress;
|
// g_audioDMA.ReadAddress = g_audioDMA.SourceAddress;
|
||||||
g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks;
|
g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks;
|
||||||
// DEBUG_LOG(DSPLLE, "ADMA read addresses: %08x", g_audioDMA.ReadAddress);
|
// DEBUG_LOG(DSPLLE, "ADMA read addresses: %08x", g_audioDMA.ReadAddress);
|
||||||
GenerateDSPInterrupt(DSP::INT_AID);
|
GenerateDSPInterrupt(DSP::INT_AID);
|
||||||
|
|
|
@ -590,7 +590,7 @@ void GenerateISIException()
|
||||||
// segment (N bit set in segment descriptor), or to guarded memory
|
// segment (N bit set in segment descriptor), or to guarded memory
|
||||||
// when MSR[IR] = 1. Otherwise, cleared.
|
// when MSR[IR] = 1. Otherwise, cleared.
|
||||||
// Bit 4: Set if a memory access is not permitted by the page or IBAT protection
|
// Bit 4: Set if a memory access is not permitted by the page or IBAT protection
|
||||||
// mechanism, described in Chapter 7, “Memory Management”; otherwise cleared.
|
// mechanism, described in Chapter 7, "Memory Management" otherwise cleared.
|
||||||
// Only one of 1,3, or 4 may be set at a time
|
// Only one of 1,3, or 4 may be set at a time
|
||||||
|
|
||||||
// For now let's just say that hash lookup failed
|
// For now let's just say that hash lookup failed
|
||||||
|
|
|
@ -16,7 +16,8 @@ typedef void (*TLogv)(const char* _szMessage, int _v);
|
||||||
typedef const char* (*TName)(void);
|
typedef const char* (*TName)(void);
|
||||||
typedef void (*TDebuggerBreak)(void);
|
typedef void (*TDebuggerBreak)(void);
|
||||||
typedef void (*TGenerateDSPInt)(void);
|
typedef void (*TGenerateDSPInt)(void);
|
||||||
typedef unsigned int (*TAudioGetStreaming)(short* _pDestBuffer, unsigned int _numSamples);
|
typedef unsigned int (*TAudioGetStreaming)(short* _pDestBuffer, unsigned int _numSamples, unsigned int _sampleRate);
|
||||||
|
typedef void (*TGetSampleRate)(unsigned int &AISampleRate, unsigned int &DSPSampleRate);
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
@ -30,6 +31,7 @@ typedef struct
|
||||||
TDebuggerBreak pDebuggerBreak;
|
TDebuggerBreak pDebuggerBreak;
|
||||||
TGenerateDSPInt pGenerateDSPInterrupt;
|
TGenerateDSPInt pGenerateDSPInterrupt;
|
||||||
TAudioGetStreaming pGetAudioStreaming;
|
TAudioGetStreaming pGetAudioStreaming;
|
||||||
|
TGetSampleRate pGetSampleRate;
|
||||||
int *pEmulatorState;
|
int *pEmulatorState;
|
||||||
bool bWii;
|
bool bWii;
|
||||||
bool bOnThread;
|
bool bOnThread;
|
||||||
|
@ -95,9 +97,10 @@ EXPORT void CALL DSP_Update(int cycles);
|
||||||
// Function: DSP_SendAIBuffer
|
// Function: DSP_SendAIBuffer
|
||||||
// Purpose: This function sends the current AI Buffer to the DSP plugin
|
// Purpose: This function sends the current AI Buffer to the DSP plugin
|
||||||
// input: _Address : Memory-Address
|
// input: _Address : Memory-Address
|
||||||
// input: _Size : Size of the Buffer (always 32)
|
// input: _Number : Number of the Samples
|
||||||
|
// input: _Rate : Sample Rate 32000/48000
|
||||||
//
|
//
|
||||||
EXPORT void CALL DSP_SendAIBuffer(unsigned int address, int sample_rate);
|
EXPORT void CALL DSP_SendAIBuffer(unsigned int address, unsigned int num_samples, unsigned int sample_rate);
|
||||||
|
|
||||||
// __________________________________________________________________________________________________
|
// __________________________________________________________________________________________________
|
||||||
// Function: DSP_StopSoundStream
|
// Function: DSP_StopSoundStream
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#define _DSPHANDLER_H
|
#define _DSPHANDLER_H
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
#include "AudioCommon.h"
|
||||||
#include "MailHandler.h"
|
#include "MailHandler.h"
|
||||||
#include "UCodes/UCodes.h"
|
#include "UCodes/UCodes.h"
|
||||||
|
|
||||||
|
@ -57,34 +58,6 @@ private:
|
||||||
CDSPHandler();
|
CDSPHandler();
|
||||||
~CDSPHandler();
|
~CDSPHandler();
|
||||||
|
|
||||||
// UDSPControl
|
|
||||||
union UDSPControl
|
|
||||||
{
|
|
||||||
u16 Hex;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
unsigned DSPReset : 1; // Write 1 to reset and waits for 0
|
|
||||||
unsigned DSPAssertInt : 1;
|
|
||||||
unsigned DSPHalt : 1;
|
|
||||||
|
|
||||||
unsigned AI : 1;
|
|
||||||
unsigned AI_mask : 1;
|
|
||||||
unsigned ARAM : 1;
|
|
||||||
unsigned ARAM_mask : 1;
|
|
||||||
unsigned DSP : 1;
|
|
||||||
unsigned DSP_mask : 1;
|
|
||||||
|
|
||||||
unsigned ARAM_DMAState : 1; // DSPGetDMAStatus() uses this flag
|
|
||||||
unsigned DSPInitCode : 1;
|
|
||||||
unsigned DSPInit : 1; // DSPInit() writes to this flag
|
|
||||||
unsigned pad : 4;
|
|
||||||
};
|
|
||||||
|
|
||||||
UDSPControl(u16 _Hex = 0)
|
|
||||||
: Hex(_Hex)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
// singleton instance
|
// singleton instance
|
||||||
static CDSPHandler* m_pInstance;
|
static CDSPHandler* m_pInstance;
|
||||||
|
|
||||||
|
|
|
@ -20,23 +20,14 @@
|
||||||
#include "DSPHandler.h"
|
#include "DSPHandler.h"
|
||||||
#include "HLEMixer.h"
|
#include "HLEMixer.h"
|
||||||
|
|
||||||
void HLEMixer::MixUCode(short *samples, int numSamples) {
|
void HLEMixer::Premix(short *samples, unsigned int numSamples, unsigned int sampleRate)
|
||||||
// if this was called directly from the HLE, and not by timeout
|
{
|
||||||
if (g_Config.m_EnableHLEAudio && IsHLEReady()) {
|
// if this was called directly from the HLE
|
||||||
|
if (g_Config.m_EnableHLEAudio && IsHLEReady())
|
||||||
|
{
|
||||||
IUCode *pUCode = CDSPHandler::GetInstance().GetUCode();
|
IUCode *pUCode = CDSPHandler::GetInstance().GetUCode();
|
||||||
if (pUCode && samples)
|
if (pUCode && samples)
|
||||||
pUCode->MixAdd(samples, numSamples);
|
pUCode->MixAdd(samples, numSamples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HLEMixer::Premix(short *samples, int numSamples) {
|
|
||||||
|
|
||||||
// first get the DTK Music
|
|
||||||
// if (g_Config.m_EnableDTKMusic) {
|
|
||||||
// g_dspInitialize.pGetAudioStreaming(samples, numSamples);
|
|
||||||
// }
|
|
||||||
|
|
||||||
MixUCode(samples, numSamples);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
class HLEMixer : public CMixer
|
class HLEMixer : public CMixer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void MixUCode(short *samples, int numSamples);
|
HLEMixer(unsigned int AISampleRate = 48000, unsigned int DSPSampleRate = 48000)
|
||||||
|
: CMixer(AISampleRate, DSPSampleRate) {};
|
||||||
|
|
||||||
virtual void Premix(short *samples, int numSamples);
|
virtual void Premix(short *samples, unsigned int numSamples, unsigned int sampleRate);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HLEMIXER_H
|
#endif // HLEMIXER_H
|
||||||
|
|
|
@ -498,7 +498,7 @@ bool CUCode_AX::AXTask(u32& _uMail)
|
||||||
SaveLog("%08x : AXLIST PB address: %08x", uAddress, m_addressPBs);
|
SaveLog("%08x : AXLIST PB address: %08x", uAddress, m_addressPBs);
|
||||||
|
|
||||||
SaveLog("Update the SoundThread to be in sync");
|
SaveLog("Update the SoundThread to be in sync");
|
||||||
soundStream->Update(); //do it in this thread to avoid sync problems
|
// soundStream->Update(); //do it in this thread to avoid sync problems
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -156,7 +156,7 @@ struct AXParamBlock
|
||||||
/* 63 */ PBADPCMInfo adpcm;
|
/* 63 */ PBADPCMInfo adpcm;
|
||||||
/* 83 */ PBSampleRateConverter src;
|
/* 83 */ PBSampleRateConverter src;
|
||||||
/* 90 */ PBADPCMLoopInfo adpcm_loop_info;
|
/* 90 */ PBADPCMLoopInfo adpcm_loop_info;
|
||||||
/* 93 */ u16 unknown_maybe_padding[3];
|
/* 93 */ //u16 unknown_maybe_padding[3]; // Comment this out to get some speedup
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PBLpf
|
struct PBLpf
|
||||||
|
@ -201,7 +201,7 @@ struct AXParamBlockWii
|
||||||
/* 95 */ PBADPCMLoopInfo adpcm_loop_info;
|
/* 95 */ PBADPCMLoopInfo adpcm_loop_info;
|
||||||
/* 98 */ PBLpf lpf;
|
/* 98 */ PBLpf lpf;
|
||||||
/* 102 */ PBHpf hpf;
|
/* 102 */ PBHpf hpf;
|
||||||
/* 106 */ u16 pad[22];
|
/* 106 */ //u16 pad[22]; // Comment this out to get some speedup
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AXParamBlockWii_ // new CRC version
|
struct AXParamBlockWii_ // new CRC version
|
||||||
|
@ -226,7 +226,7 @@ struct AXParamBlockWii_ // new CRC version
|
||||||
/* 0x0B4 */ PBADPCMLoopInfo adpcm_loop_info;
|
/* 0x0B4 */ PBADPCMLoopInfo adpcm_loop_info;
|
||||||
/* 0x0BA */ PBLpf lpf;
|
/* 0x0BA */ PBLpf lpf;
|
||||||
/* 0x0C2 */ PBHpf hpf;
|
/* 0x0C2 */ PBHpf hpf;
|
||||||
/* 0x0CA */ u16 pad[27];
|
/* 0x0CA */ //u16 pad[27]; // Comment this out to get some speedup
|
||||||
/* 0x100 */
|
/* 0x100 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -289,7 +289,7 @@ bool CUCode_AXWii::AXTask(u32& _uMail)
|
||||||
m_addressPBs = Memory_Read_U32(uAddress);
|
m_addressPBs = Memory_Read_U32(uAddress);
|
||||||
lCUCode_AX->m_addressPBs = m_addressPBs; // for the sake of logging
|
lCUCode_AX->m_addressPBs = m_addressPBs; // for the sake of logging
|
||||||
soundStream->GetMixer()->SetHLEReady(true);
|
soundStream->GetMixer()->SetHLEReady(true);
|
||||||
soundStream->Update();
|
// soundStream->Update();
|
||||||
uAddress += 4;
|
uAddress += 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ void CUCode_Zelda::HandleMail_LightVersion(u32 _uMail)
|
||||||
|
|
||||||
soundStream->GetMixer()->SetHLEReady(true);
|
soundStream->GetMixer()->SetHLEReady(true);
|
||||||
DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync");
|
DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync");
|
||||||
soundStream->Update(); //do it in this thread to avoid sync problems
|
// soundStream->Update(); //do it in this thread to avoid sync problems
|
||||||
|
|
||||||
m_bSyncCmdPending = false;
|
m_bSyncCmdPending = false;
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ void CUCode_Zelda::HandleMail_SMSVersion(u32 _uMail)
|
||||||
|
|
||||||
soundStream->GetMixer()->SetHLEReady(true);
|
soundStream->GetMixer()->SetHLEReady(true);
|
||||||
DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync");
|
DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync");
|
||||||
soundStream->Update(); //do it in this thread to avoid sync problems
|
// soundStream->Update(); //do it in this thread to avoid sync problems
|
||||||
|
|
||||||
m_bSyncCmdPending = false;
|
m_bSyncCmdPending = false;
|
||||||
}
|
}
|
||||||
|
@ -339,7 +339,7 @@ void CUCode_Zelda::HandleMail_NormalVersion(u32 _uMail)
|
||||||
|
|
||||||
soundStream->GetMixer()->SetHLEReady(true);
|
soundStream->GetMixer()->SetHLEReady(true);
|
||||||
DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync");
|
DEBUG_LOG(DSPHLE, "Update the SoundThread to be in sync");
|
||||||
soundStream->Update(); //do it in this thread to avoid sync problems
|
// soundStream->Update(); //do it in this thread to avoid sync problems
|
||||||
|
|
||||||
m_bSyncCmdPending = false;
|
m_bSyncCmdPending = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ DSPDebuggerHLE* m_DebuggerFrame = NULL;
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Setup.h"
|
#include "Setup.h"
|
||||||
#include "StringUtil.h"
|
#include "StringUtil.h"
|
||||||
#include "AudioCommon.h"
|
|
||||||
#include "LogManager.h"
|
#include "LogManager.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,8 +41,8 @@ PLUGIN_GLOBALS* globals = NULL;
|
||||||
DSPInitialize g_dspInitialize;
|
DSPInitialize g_dspInitialize;
|
||||||
u8* g_pMemory;
|
u8* g_pMemory;
|
||||||
extern std::vector<std::string> sMailLog, sMailTime;
|
extern std::vector<std::string> sMailLog, sMailTime;
|
||||||
bool g_bMuted = false;
|
|
||||||
|
|
||||||
|
bool g_InitMixer = false;
|
||||||
SoundStream *soundStream = NULL;
|
SoundStream *soundStream = NULL;
|
||||||
|
|
||||||
// Mailbox utility
|
// Mailbox utility
|
||||||
|
@ -203,6 +202,7 @@ void DllConfig(HWND _hParent)
|
||||||
|
|
||||||
void Initialize(void *init)
|
void Initialize(void *init)
|
||||||
{
|
{
|
||||||
|
g_InitMixer = false;
|
||||||
g_dspInitialize = *(DSPInitialize*)init;
|
g_dspInitialize = *(DSPInitialize*)init;
|
||||||
|
|
||||||
g_Config.Load();
|
g_Config.Load();
|
||||||
|
@ -211,10 +211,6 @@ void Initialize(void *init)
|
||||||
g_dspState.Reset();
|
g_dspState.Reset();
|
||||||
|
|
||||||
CDSPHandler::CreateInstance();
|
CDSPHandler::CreateInstance();
|
||||||
|
|
||||||
soundStream = AudioCommon::InitSoundStream(new HLEMixer());
|
|
||||||
if(!soundStream)
|
|
||||||
PanicAlert("Error starting up sound stream");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSP_StopSoundStream()
|
void DSP_StopSoundStream()
|
||||||
|
@ -245,6 +241,7 @@ void Shutdown()
|
||||||
void DoState(unsigned char **ptr, int mode)
|
void DoState(unsigned char **ptr, int mode)
|
||||||
{
|
{
|
||||||
PointerWrap p(ptr, mode);
|
PointerWrap p(ptr, mode);
|
||||||
|
p.Do(g_InitMixer);
|
||||||
CDSPHandler::GetInstance().GetUCode()->DoState(p);
|
CDSPHandler::GetInstance().GetUCode()->DoState(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,6 +302,19 @@ void DSP_WriteMailboxLow(bool _CPUMailbox, unsigned short _Value)
|
||||||
// Other DSP fuctions
|
// Other DSP fuctions
|
||||||
unsigned short DSP_WriteControlRegister(unsigned short _Value)
|
unsigned short DSP_WriteControlRegister(unsigned short _Value)
|
||||||
{
|
{
|
||||||
|
UDSPControl Temp(_Value);
|
||||||
|
if (!g_InitMixer)
|
||||||
|
{
|
||||||
|
if (!Temp.DSPHalt && Temp.DSPInit)
|
||||||
|
{
|
||||||
|
unsigned int AISampleRate, DSPSampleRate;
|
||||||
|
g_dspInitialize.pGetSampleRate(AISampleRate, DSPSampleRate);
|
||||||
|
soundStream = AudioCommon::InitSoundStream(new HLEMixer(AISampleRate, DSPSampleRate));
|
||||||
|
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||||
|
// Mixer is initialized
|
||||||
|
g_InitMixer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return CDSPHandler::GetInstance().WriteControlRegister(_Value);
|
return CDSPHandler::GetInstance().WriteControlRegister(_Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,43 +334,25 @@ void DSP_Update(int cycles)
|
||||||
inside Mixer_PushSamples(), the reason that we don't disable this entire
|
inside Mixer_PushSamples(), the reason that we don't disable this entire
|
||||||
function when Other Audio is disabled is that then we can't turn it back on
|
function when Other Audio is disabled is that then we can't turn it back on
|
||||||
again once the game has started. */
|
again once the game has started. */
|
||||||
void DSP_SendAIBuffer(unsigned int address, int sample_rate)
|
void DSP_SendAIBuffer(unsigned int address, unsigned int num_samples, unsigned int sample_rate)
|
||||||
{
|
{
|
||||||
// TODO: This is not yet fully threadsafe.
|
if (!soundStream)
|
||||||
if (!soundStream) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
CMixer* pMixer = soundStream->GetMixer();
|
CMixer* pMixer = soundStream->GetMixer();
|
||||||
|
|
||||||
if (pMixer && address)
|
if (pMixer && address)
|
||||||
{
|
{
|
||||||
short* samples = (short*)Memory_Get_Pointer(address);
|
short* samples = (short*)Memory_Get_Pointer(address);
|
||||||
/*
|
// sample_rate could be 32khz/48khz here,
|
||||||
short samples[16] = {0}; // interleaved stereo
|
// see Core/DSP/DSP.cpp for better information
|
||||||
if (address)
|
pMixer->PushSamples(samples, num_samples, sample_rate);
|
||||||
{
|
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
samples[i] = Memory_Read_U16(address + i * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Write the audio to a file
|
// FIXME: Write the audio to a file
|
||||||
//if (log_ai)
|
//if (log_ai)
|
||||||
// g_wave_writer.AddStereoSamples(samples, 8);
|
// g_wave_writer.AddStereoSamples(samples, 8);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
// sample_rate is usually 32k here,
|
|
||||||
// see Core/DSP/DSP.cpp for better information
|
|
||||||
pMixer->PushSamples(samples, 32 / 4, sample_rate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SoundStream is updated only when necessary (there is no 70 ms limit
|
|
||||||
// so each sample now triggers the sound stream)
|
|
||||||
|
|
||||||
// TODO: think about this.
|
|
||||||
static int counter = 0;
|
|
||||||
counter++;
|
|
||||||
if ((counter & 31) == 0 && soundStream)
|
|
||||||
soundStream->Update();
|
soundStream->Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,11 @@ u32 Memory_Read_U32(u32 _uAddress)
|
||||||
return Common::swap32(*(u32*)&g_dsp.cpu_ram[_uAddress & RAM_MASK]);
|
return Common::swap32(*(u32*)&g_dsp.cpu_ram[_uAddress & RAM_MASK]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* Memory_Get_Pointer(u32 _uAddress)
|
||||||
|
{
|
||||||
|
return &g_dsp.cpu_ram[_uAddress & RAM_MASK];
|
||||||
|
}
|
||||||
|
|
||||||
#if PROFILE
|
#if PROFILE
|
||||||
|
|
||||||
#define PROFILE_MAP_SIZE 0x10000
|
#define PROFILE_MAP_SIZE 0x10000
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
u16 Memory_Read_U16(u32 _uAddress); // For PB address detection
|
u16 Memory_Read_U16(u32 _uAddress); // For PB address detection
|
||||||
u32 Memory_Read_U32(u32 _uAddress);
|
u32 Memory_Read_U32(u32 _uAddress);
|
||||||
|
void* Memory_Get_Pointer(u32 _uAddress);
|
||||||
|
|
||||||
#if PROFILE
|
#if PROFILE
|
||||||
void ProfilerDump(u64 _count);
|
void ProfilerDump(u64 _count);
|
||||||
|
|
|
@ -47,6 +47,7 @@ PLUGIN_GLOBALS* globals = NULL;
|
||||||
DSPInitialize g_dspInitialize;
|
DSPInitialize g_dspInitialize;
|
||||||
Common::Thread *g_hDSPThread = NULL;
|
Common::Thread *g_hDSPThread = NULL;
|
||||||
SoundStream *soundStream = NULL;
|
SoundStream *soundStream = NULL;
|
||||||
|
bool g_InitMixer = false;
|
||||||
|
|
||||||
bool bIsRunning = false;
|
bool bIsRunning = false;
|
||||||
|
|
||||||
|
@ -166,6 +167,7 @@ void DllConfig(HWND _hParent)
|
||||||
void DoState(unsigned char **ptr, int mode)
|
void DoState(unsigned char **ptr, int mode)
|
||||||
{
|
{
|
||||||
PointerWrap p(ptr, mode);
|
PointerWrap p(ptr, mode);
|
||||||
|
p.Do(g_InitMixer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DllDebugger(HWND _hParent, bool Show)
|
void DllDebugger(HWND _hParent, bool Show)
|
||||||
|
@ -202,6 +204,7 @@ void DSP_DebugBreak()
|
||||||
|
|
||||||
void Initialize(void *init)
|
void Initialize(void *init)
|
||||||
{
|
{
|
||||||
|
g_InitMixer = false;
|
||||||
bool bCanWork = true;
|
bool bCanWork = true;
|
||||||
g_dspInitialize = *(DSPInitialize*)init;
|
g_dspInitialize = *(DSPInitialize*)init;
|
||||||
|
|
||||||
|
@ -229,7 +232,6 @@ void Initialize(void *init)
|
||||||
{
|
{
|
||||||
g_hDSPThread = new Common::Thread(dsp_thread, NULL);
|
g_hDSPThread = new Common::Thread(dsp_thread, NULL);
|
||||||
}
|
}
|
||||||
soundStream = AudioCommon::InitSoundStream();
|
|
||||||
|
|
||||||
#if defined(HAVE_WX) && HAVE_WX
|
#if defined(HAVE_WX) && HAVE_WX
|
||||||
if (m_DebuggerFrame)
|
if (m_DebuggerFrame)
|
||||||
|
@ -256,6 +258,19 @@ void Shutdown()
|
||||||
|
|
||||||
u16 DSP_WriteControlRegister(u16 _uFlag)
|
u16 DSP_WriteControlRegister(u16 _uFlag)
|
||||||
{
|
{
|
||||||
|
UDSPControl Temp(_uFlag);
|
||||||
|
if (!g_InitMixer)
|
||||||
|
{
|
||||||
|
if (!Temp.DSPHalt && Temp.DSPInit)
|
||||||
|
{
|
||||||
|
unsigned int AISampleRate, DSPSampleRate;
|
||||||
|
g_dspInitialize.pGetSampleRate(AISampleRate, DSPSampleRate);
|
||||||
|
soundStream = AudioCommon::InitSoundStream(new CMixer(AISampleRate, DSPSampleRate));
|
||||||
|
if(!soundStream) PanicAlert("Error starting up sound stream");
|
||||||
|
// Mixer is initialized
|
||||||
|
g_InitMixer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
DSPInterpreter::WriteCR(_uFlag);
|
DSPInterpreter::WriteCR(_uFlag);
|
||||||
return DSPInterpreter::ReadCR();
|
return DSPInterpreter::ReadCR();
|
||||||
}
|
}
|
||||||
|
@ -343,28 +358,20 @@ void DSP_Update(int cycles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSP_SendAIBuffer(unsigned int address, int sample_rate)
|
void DSP_SendAIBuffer(unsigned int address, unsigned int num_samples, unsigned int sample_rate)
|
||||||
{
|
{
|
||||||
if (soundStream->GetMixer())
|
if (!soundStream)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CMixer* pMixer = soundStream->GetMixer();
|
||||||
|
|
||||||
|
if (pMixer && address)
|
||||||
{
|
{
|
||||||
short samples[16] = {0}; // interleaved stereo
|
short* samples = (short*)Memory_Get_Pointer(address);
|
||||||
if (address)
|
|
||||||
{
|
pMixer->PushSamples(samples, num_samples, sample_rate);
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
samples[i] = Memory_Read_U16(address + i * 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
soundStream->GetMixer()->PushSamples(samples, 32 / 4, sample_rate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SoundStream is updated only when necessary (there is no 70 ms limit
|
|
||||||
// so each sample now triggers the sound stream)
|
|
||||||
|
|
||||||
// TODO: think about this.
|
|
||||||
// static int counter = 0;
|
|
||||||
// counter++;
|
|
||||||
if (/*(counter & 31) == 0 &&*/ soundStream)
|
|
||||||
soundStream->Update();
|
soundStream->Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue