summaryrefslogtreecommitdiff
path: root/Source/Core/AudioCommon
diff options
context:
space:
mode:
authorayuanx <ayuanx@gmail.com>2009-12-23 15:34:14 +0000
committerayuanx <ayuanx@gmail.com>2009-12-23 15:34:14 +0000
commit9eea60ca69afac82abb60fc1b1a0c6962f522ced (patch)
tree98d0e1d46c0d337c24218fb6e1b4e8048443c397 /Source/Core/AudioCommon
parent0d0a7c515fe60e4899f4669fb52a115cfd1f7431 (diff)
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
Diffstat (limited to 'Source/Core/AudioCommon')
-rw-r--r--Source/Core/AudioCommon/Src/AudioCommon.h25
-rw-r--r--Source/Core/AudioCommon/Src/DSoundStream.cpp9
-rw-r--r--Source/Core/AudioCommon/Src/DSoundStream.h16
-rw-r--r--Source/Core/AudioCommon/Src/Mixer.cpp214
-rw-r--r--Source/Core/AudioCommon/Src/Mixer.h57
-rw-r--r--Source/Core/AudioCommon/Src/NullSoundStream.h9
-rw-r--r--Source/Core/AudioCommon/Src/OpenALStream.cpp19
-rw-r--r--Source/Core/AudioCommon/Src/OpenALStream.h7
8 files changed, 205 insertions, 151 deletions
diff --git a/Source/Core/AudioCommon/Src/AudioCommon.h b/Source/Core/AudioCommon/Src/AudioCommon.h
index 294fab1f72..6edb792004 100644
--- a/Source/Core/AudioCommon/Src/AudioCommon.h
+++ b/Source/Core/AudioCommon/Src/AudioCommon.h
@@ -30,6 +30,31 @@ extern DSPInitialize g_dspInitialize;
extern SoundStream *soundStream;
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
{
SoundStream *InitSoundStream(CMixer *mixer = NULL);
diff --git a/Source/Core/AudioCommon/Src/DSoundStream.cpp b/Source/Core/AudioCommon/Src/DSoundStream.cpp
index 61613dbd4a..dc451eddb7 100644
--- a/Source/Core/AudioCommon/Src/DSoundStream.cpp
+++ b/Source/Core/AudioCommon/Src/DSoundStream.cpp
@@ -111,13 +111,11 @@ void DSound::SoundLoop()
int numBytesToRender = FIX128(ModBufferSize(currentPos - lastPos));
if (numBytesToRender >= 256)
{
- if (numBytesToRender > sizeof(realtimeBuffer))
+ if (numBytesToRender > sizeof(realtimeBuffer) * sizeof(short))
PanicAlert("soundThread: too big render call");
- m_mixer->Mix(realtimeBuffer, numBytesToRender >> 2);
+ m_mixer->Mix(realtimeBuffer, numBytesToRender / 4);
WriteDataToBuffer(lastPos, (char*)realtimeBuffer, numBytesToRender);
- currentPos = ModBufferSize(lastPos + numBytesToRender);
- totalRenderedBytes += numBytesToRender;
- lastPos = currentPos;
+ lastPos = ModBufferSize(lastPos + numBytesToRender);
}
soundCriticalSection.Leave();
soundSyncEvent.Wait();
@@ -142,7 +140,6 @@ bool DSound::Start()
dsBuffer->Lock(0, bufferSize, (void* *)&p1, &num1, 0, 0, 0);
memset(p1, 0, num1);
dsBuffer->Unlock(p1, num1, 0, 0);
- totalRenderedBytes = -bufferSize;
thread = new Common::Thread(soundThread, (void *)this);
return true;
}
diff --git a/Source/Core/AudioCommon/Src/DSoundStream.h b/Source/Core/AudioCommon/Src/DSoundStream.h
index 9fd2aaf8bb..fcf0413ee7 100644
--- a/Source/Core/AudioCommon/Src/DSoundStream.h
+++ b/Source/Core/AudioCommon/Src/DSoundStream.h
@@ -25,8 +25,7 @@
#include <mmsystem.h>
#include <dsound.h>
-#define BUFSIZE 32768
-#define MAXWAIT 70 // miliseconds
+#define BUFSIZE (1024 * 8 * 4)
#endif
class DSound : public SoundStream
@@ -41,31 +40,30 @@ class DSound : public SoundStream
IDirectSoundBuffer* dsBuffer;
int bufferSize; //i bytes
- int totalRenderedBytes;
int m_volume;
// playback position
int currentPos;
int lastPos;
- short realtimeBuffer[1024 * 1024];
+ short realtimeBuffer[BUFSIZE / sizeof(short)];
- inline int FIX128(int x) {
+ inline int FIX128(int x)
+ {
return x & (~127);
}
- inline int ModBufferSize(int x) {
+ inline int ModBufferSize(int x)
+ {
return (x + bufferSize) % bufferSize;
}
bool CreateBuffer();
- bool WriteDataToBuffer(DWORD dwOffset, char* soundData,
- DWORD dwSoundBytes);
+ bool WriteDataToBuffer(DWORD dwOffset, char* soundData, DWORD dwSoundBytes);
public:
DSound(CMixer *mixer, void *hWnd = NULL)
: SoundStream(mixer)
, bufferSize(0)
- , totalRenderedBytes(0)
, currentPos(0)
, lastPos(0)
, dsBuffer(0)
diff --git a/Source/Core/AudioCommon/Src/Mixer.cpp b/Source/Core/AudioCommon/Src/Mixer.cpp
index 63bc8c7088..00b3a2587b 100644
--- a/Source/Core/AudioCommon/Src/Mixer.cpp
+++ b/Source/Core/AudioCommon/Src/Mixer.cpp
@@ -16,112 +16,65 @@
// http://code.google.com/p/dolphin-emu/
-// This queue solution is temporary. I'll implement something more efficient later.
-#include <queue> // System
-
-#include "Thread.h" // Common
+#include "Atomic.h"
#include "Mixer.h"
-#include "FixedSizeQueue.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) {
- Premix(NULL, 0);
+ if (!samples)
return 0;
- }
- // silence
- memset(samples, 0, numSamples * 2 * sizeof(short));
- if (g_dspInitialize.pEmulatorState) {
+ if (g_dspInitialize.pEmulatorState)
+ {
if (*g_dspInitialize.pEmulatorState != 0)
- return 0;
- }
-
- // first get the DTK Music
- if (m_EnableDTKMusic) {
- g_dspInitialize.pGetAudioStreaming(samples, numSamples);
+ {
+ // Silence
+ memset(samples, 0, numSamples * 4);
+ return numSamples;
+ }
}
- Premix(samples, numSamples);
-
- int count = 0;
+ unsigned int numLeft = Common::AtomicLoad(m_numSamples);
+ numLeft = (numLeft > numSamples) ? numSamples : numLeft;
- push_sync.Enter();
- while (m_queueSize > queue_minlength && count < numSamples * 2)
+ // Do re-sampling if needed
+ if (m_sampleRate == m_dspSampleRate)
{
- 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;
+ for (unsigned int i = 0; i < numLeft * 2; i++)
+ samples[i] = Common::swap16(m_buffer[(m_indexR + i) & INDEX_MASK]);
+ m_indexR += numLeft * 2;
}
- push_sync.Leave();
+ else if (m_sampleRate < m_dspSampleRate) // If down-sampling needed
+ {
+ _dbg_assert_msg_(DSPHLE, !(numSamples % 2), "Number of Samples: %i must be even!", numSamples);
- return count;
-}
+ 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]);
-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
+ if (i % 3)
+ {
+ *pDest++ = (last_l + cur_r) / 2;
+ *pDest++ = (last_r + cur_r) / 2;
+ }
- // 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;
+ last_l = cur_l;
+ last_r = cur_r;
}
- soundStream->Update();
- SLEEP(1);
- }
- // -----------------------------------------------------------------------
- push_sync.Enter();
- while (num_stereo_samples)
- {
- sample_queue.push(Common::swap16(*samples));
- samples++;
- sample_queue.push(Common::swap16(*samples));
- samples++;
- m_queueSize += 2;
- num_stereo_samples--;
+ m_indexR += numLeft * 2 * 3 / 2;
}
- push_sync.Leave();
- return;
-
+ 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 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);
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;
+}
+
+
+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;
}
-int CMixer::GetNumSamples()
+unsigned int CMixer::GetNumSamples()
{
- return m_queueSize / 2;
- //int ret = (m_queueSize - queue_minlength) / 2;
- //ret = (ret > 0) ? ret : 0;
- //return ret;
+ return Common::AtomicLoad(m_numSamples);
}
diff --git a/Source/Core/AudioCommon/Src/Mixer.h b/Source/Core/AudioCommon/Src/Mixer.h
index 4fcf1c7d16..c6e50c4839 100644
--- a/Source/Core/AudioCommon/Src/Mixer.h
+++ b/Source/Core/AudioCommon/Src/Mixer.h
@@ -18,39 +18,38 @@
#ifndef _MIXER_H_
#define _MIXER_H_
-#include "FixedSizeQueue.h"
-#include "Thread.h"
-
-// On real hardware, this fifo is much, much smaller. But timing is also
-// tighter than under Windows, so...
-#define queue_minlength 1024 * 4
-#define queue_maxlength 1024 * 28
+// 16 bit Stereo
+#define MAX_SAMPLES (1024 * 4)
+#define INDEX_MASK (MAX_SAMPLES * 2 - 1)
+#define RESERVED_SAMPLES (MAX_SAMPLES / 8)
class CMixer {
public:
- // AyuanX: Mixer sample rate is fixed to 32khz for now
- // if any game sets DSP sample rate to 48khz, we are doomed
- // TODO: Fix this somehow!
- CMixer(unsigned int SampleRate = 32000)
- : m_sampleRate(SampleRate)
+ CMixer(unsigned int AISampleRate = 48000, unsigned int DSPSampleRate = 48000)
+ : m_aiSampleRate(AISampleRate)
+ , m_dspSampleRate(DSPSampleRate)
, m_bits(16)
, m_channels(2)
- , m_mode(2)
, 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
- virtual int Mix(short *sample, int numSamples);
- virtual int GetNumSamples();
+ virtual unsigned int Mix(short* samples, unsigned int numSamples);
+ virtual void Premix(short *samples, unsigned int numSamples, unsigned int sampleRate) {}
+ unsigned int GetNumSamples();
// Called from main thread
- virtual void PushSamples(short* samples, int num_stereo_samples, int core_sample_rate);
-
- virtual void Premix(short *samples, int numSamples) {}
-
- int GetSampleRate() {return m_sampleRate;}
+ virtual void PushSamples(short* samples, unsigned int num_samples, unsigned int sample_rate);
+ unsigned int GetSampleRate() {return m_sampleRate;}
void SetThrottle(bool use) { m_throttle = use;}
void SetDTKMusic(bool use) { m_EnableDTKMusic = use;}
@@ -61,19 +60,23 @@ public:
// ---------------------
protected:
- int m_sampleRate;
+ unsigned int m_sampleRate;
+ unsigned int m_aiSampleRate;
+ unsigned int m_dspSampleRate;
int m_bits;
int m_channels;
- int m_mode;
bool m_HLEready;
- int m_queueSize;
bool m_EnableDTKMusic;
bool m_throttle;
+
+ short m_buffer[MAX_SAMPLES * 2];
+ u32 m_indexW;
+ u32 m_indexR;
+ volatile u32 m_numSamples;
+
private:
- Common::CriticalSection push_sync;
- FixedSizeQueue<s16, queue_maxlength> sample_queue;
};
diff --git a/Source/Core/AudioCommon/Src/NullSoundStream.h b/Source/Core/AudioCommon/Src/NullSoundStream.h
index d73e6ccc01..7fc6aecd88 100644
--- a/Source/Core/AudioCommon/Src/NullSoundStream.h
+++ b/Source/Core/AudioCommon/Src/NullSoundStream.h
@@ -22,10 +22,10 @@
#include "Mixer.h"
class NullMixer : public CMixer {
+
public:
- virtual int Mix(short *sample, int numSamples) {return 0;}
- virtual void PushSamples(short* samples, int num_stereo_samples,
- int core_sample_rate) {}
+ virtual unsigned int Mix(short *samples, unsigned int numSamples) { return 0; }
+ virtual void PushSamples(short* samples, unsigned int num_samples, unsigned int sample_rate) {}
};
class NullSound : public SoundStream
@@ -35,7 +35,6 @@ public:
{
delete m_mixer;
m_mixer = new NullMixer();
-
}
virtual ~NullSound() {}
@@ -47,7 +46,7 @@ public:
virtual bool Start() { return true; }
virtual void Update() {
- m_mixer->Mix(NULL, 256 >> 2);
+ //m_mixer->Mix(NULL, 256 >> 2);
//(*callback)(NULL, 256 >> 2, 16, sampleRate, 2);
}
};
diff --git a/Source/Core/AudioCommon/Src/OpenALStream.cpp b/Source/Core/AudioCommon/Src/OpenALStream.cpp
index 1b2363888d..7945f44d86 100644
--- a/Source/Core/AudioCommon/Src/OpenALStream.cpp
+++ b/Source/Core/AudioCommon/Src/OpenALStream.cpp
@@ -138,12 +138,13 @@ void OpenALStream::SoundLoop()
// Generate a Source to playback the Buffers
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++)
- 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);
alSourcePlay(uiSource);
+
err = alGetError();
// TODO: Error handling
@@ -158,12 +159,12 @@ void OpenALStream::SoundLoop()
alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &iBuffersProcessed);
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)
if (iBuffersFilled == 0)
alSourceUnqueueBuffers(uiSource, iBuffersProcessed, uiBufferTemp);
@@ -176,11 +177,11 @@ void OpenALStream::SoundLoop()
if (iBuffersFilled == OAL_NUM_BUFFERS)
alSourcePlay(uiSource);
}
- else
+ else if (numSamples >= OAL_THRESHOLD)
{
ALint state = 0;
alGetSourcei(uiSource, AL_SOURCE_STATE, &state);
- if (state != AL_PLAYING)
+ if (state == AL_STOPPED)
alSourcePlay(uiSource);
}
soundSyncEvent.Wait();
diff --git a/Source/Core/AudioCommon/Src/OpenALStream.h b/Source/Core/AudioCommon/Src/OpenALStream.h
index fbeaadba60..9a469511b8 100644
--- a/Source/Core/AudioCommon/Src/OpenALStream.h
+++ b/Source/Core/AudioCommon/Src/OpenALStream.h
@@ -33,10 +33,11 @@
#include "AL/al.h"
#include "AL/alc.h"
#endif // WIN32
-// public use
+// 16 bit Stereo
#define SFX_MAX_SOURCE 1
#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
class OpenALStream: public SoundStream
@@ -66,7 +67,7 @@ private:
Common::CriticalSection soundCriticalSection;
Common::Event soundSyncEvent;
- short realtimeBuffer[OAL_BUFFER_SIZE/sizeof(short)];
+ short realtimeBuffer[OAL_MAX_SAMPLES * 2];
ALuint uiBuffers[OAL_NUM_BUFFERS];
ALuint uiSource;
ALfloat fVolume;