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:
ayuanx 2009-12-23 15:34:14 +00:00
parent 0d0a7c515f
commit 9eea60ca69
27 changed files with 358 additions and 314 deletions

View File

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

View File

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

View File

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

View File

@ -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; {
} // Silence
memset(samples, 0, numSamples * 4);
// first get the DTK Music return numSamples;
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];
x += sample_queue.front();
if (x > 32767) x = 32767;
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;
}
void CMixer::PushSamples(short *samples, int num_stereo_samples, int core_sample_rate)
{
push_sync.Enter();
if (m_queueSize == 0)
{
m_queueSize = queue_minlength;
for (int i = 0; i < queue_minlength; i++)
sample_queue.push((s16)0);
}
push_sync.Leave();
#ifdef _WIN32
if (GetAsyncKeyState(VK_TAB))
return;
#endif
// Write Other Audio
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.
if (g_dspInitialize.pEmulatorState) {
if (*g_dspInitialize.pEmulatorState != 0)
return;
} }
soundStream->Update();
SLEEP(1);
} }
// -----------------------------------------------------------------------
push_sync.Enter(); unsigned int numLeft = Common::AtomicLoad(m_numSamples);
while (num_stereo_samples) numLeft = (numLeft > numSamples) ? numSamples : numLeft;
// Do re-sampling if needed
if (m_sampleRate == m_dspSampleRate)
{ {
sample_queue.push(Common::swap16(*samples)); for (unsigned int i = 0; i < numLeft * 2; i++)
samples++; samples[i] = Common::swap16(m_buffer[(m_indexR + i) & INDEX_MASK]);
sample_queue.push(Common::swap16(*samples)); m_indexR += numLeft * 2;
samples++;
m_queueSize += 2;
num_stereo_samples--;
} }
push_sync.Leave(); else if (m_sampleRate < m_dspSampleRate) // If down-sampling needed
return; {
_dbg_assert_msg_(DSPHLE, !(numSamples % 2), "Number of Samples: %i must be even!", numSamples);
short *pDest = samples;
int last_l, last_r, cur_l, cur_r;
for (unsigned int i = 0; i < numLeft * 3 / 2; i++)
{
cur_l = Common::swap16(m_buffer[(m_indexR + i * 2) & INDEX_MASK]);
cur_r = Common::swap16(m_buffer[(m_indexR + i * 2 + 1) & INDEX_MASK]);
if (i % 3)
{
*pDest++ = (last_l + cur_r) / 2;
*pDest++ = (last_r + cur_r) / 2;
}
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();
*/ */
}
// Padding
if (numSamples > numLeft)
memset(&samples[numLeft * 2], 0, (numSamples - numLeft) * 4);
// Add the HLE sound
if (m_sampleRate < m_dspSampleRate)
{
PanicAlert("Mixer: DSPHLE down-sampling is not implemented yet!\n"
"Usually no game should require this, please report!");
}
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;
} }
int CMixer::GetNumSamples()
void CMixer::PushSamples(short *samples, unsigned int num_samples, unsigned int sample_rate)
{ {
return m_queueSize / 2; // The auto throttle function. This loop will put a ceiling on the CPU MHz.
//int ret = (m_queueSize - queue_minlength) / 2; if (m_throttle)
//ret = (ret > 0) ? ret : 0; {
//return ret; // 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);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,44 +334,26 @@ 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
if (address)
{
for (int i = 0; i < 16; i++)
{
samples[i] = Memory_Read_U16(address + i * 2);
}
// FIXME: Write the audio to a file
//if (log_ai)
// g_wave_writer.AddStereoSamples(samples, 8);
}
*/
// sample_rate is usually 32k here,
// see Core/DSP/DSP.cpp for better information // see Core/DSP/DSP.cpp for better information
pMixer->PushSamples(samples, 32 / 4, sample_rate); pMixer->PushSamples(samples, num_samples, sample_rate);
// FIXME: Write the audio to a file
//if (log_ai)
// g_wave_writer.AddStereoSamples(samples, 8);
} }
// SoundStream is updated only when necessary (there is no 70 ms limit soundStream->Update();
// so each sample now triggers the sound stream)
// TODO: think about this.
static int counter = 0;
counter++;
if ((counter & 31) == 0 && soundStream)
soundStream->Update();
} }
void DSP_ClearAudioBuffer() void DSP_ClearAudioBuffer()

View File

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

View File

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

View File

@ -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,29 +358,21 @@ 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 soundStream->Update();
// so each sample now triggers the sound stream)
// TODO: think about this.
// static int counter = 0;
// counter++;
if (/*(counter & 31) == 0 &&*/ soundStream)
soundStream->Update();
} }
void DSP_ClearAudioBuffer() void DSP_ClearAudioBuffer()