mirror of https://github.com/snes9xgit/snes9x.git
audio output thread
This commit is contained in:
parent
a3c0c4621d
commit
e2c1abb7fc
249
unix/unix.cpp
249
unix/unix.cpp
|
@ -12,12 +12,14 @@
|
|||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#ifdef USE_THREADS
|
||||
#include <sched.h>
|
||||
#include <pthread.h>
|
||||
#include <vector>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
|
@ -114,10 +116,6 @@ struct SoundStatus
|
|||
{
|
||||
int sound_fd;
|
||||
uint32 fragment_size;
|
||||
uint32 err_counter;
|
||||
uint32 err_rate;
|
||||
int32 samples_mixed_so_far;
|
||||
int32 play_position;
|
||||
};
|
||||
|
||||
|
||||
|
@ -127,15 +125,6 @@ static SoundStatus so;
|
|||
|
||||
static bool8 rewinding;
|
||||
|
||||
#ifndef NOSOUND
|
||||
static uint8 Buf[SOUND_BUFFER_SIZE];
|
||||
#endif
|
||||
|
||||
#ifdef USE_THREADS
|
||||
static pthread_t thread;
|
||||
static pthread_mutex_t mutex;
|
||||
#endif
|
||||
|
||||
#ifdef JOYSTICK_SUPPORT
|
||||
static uint8 js_mod[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
static int js_fd[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
|
@ -156,14 +145,8 @@ bool S9xDisplayPollButton (uint32, bool *);
|
|||
bool S9xDisplayPollAxis (uint32, int16 *);
|
||||
bool S9xDisplayPollPointer (uint32, int16 *, int16 *);
|
||||
|
||||
static long log2 (long);
|
||||
static void SoundTrigger (void);
|
||||
static void InitTimer (void);
|
||||
static void NSRTControllerSetup (void);
|
||||
static int make_snes9x_dirs (void);
|
||||
#ifndef NOSOUND
|
||||
static void * S9xProcessSound (void *);
|
||||
#endif
|
||||
#ifdef JOYSTICK_SUPPORT
|
||||
static void InitJoysticks (void);
|
||||
static bool8 ReadJoysticks (void);
|
||||
|
@ -180,6 +163,169 @@ static long log2 (long num)
|
|||
return (n);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
#if ! defined(NOSOUND)
|
||||
class S9xAudioOutput
|
||||
{
|
||||
public:
|
||||
S9xAudioOutput(int fd, uint32 sampleRateHz, bool isThreaded)
|
||||
{
|
||||
m_FD = fd;
|
||||
uint32 bufferSizeMS = unixSettings.SoundBufferSize; // milliseconds
|
||||
// 4 = sizeof(uint16) * STEREO
|
||||
m_BufferSize = int(uint64(sampleRateHz) * bufferSizeMS / 1000 * 4);
|
||||
|
||||
#if defined(USE_THREADS)
|
||||
m_WrittenSize = 0;
|
||||
m_Thread = pthread_t();
|
||||
m_isExit = false;
|
||||
if (isThreaded)
|
||||
{
|
||||
m_BufferMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
m_hasBuffer = PTHREAD_COND_INITIALIZER;
|
||||
if (pthread_create(&m_Thread, NULL, AudioOutputThreadEntry, this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
~S9xAudioOutput()
|
||||
{
|
||||
#if defined(USE_THREADS)
|
||||
if (m_Thread)
|
||||
{
|
||||
pthread_mutex_lock(&m_BufferMutex);
|
||||
{
|
||||
m_isExit = true;
|
||||
pthread_cond_signal(&m_hasBuffer);
|
||||
}
|
||||
pthread_mutex_unlock(&m_BufferMutex);
|
||||
|
||||
pthread_join(m_Thread, NULL);
|
||||
pthread_mutex_destroy(&m_BufferMutex);
|
||||
pthread_cond_destroy(&m_hasBuffer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Write(void* data, int size)
|
||||
{
|
||||
#if defined(USE_THREADS)
|
||||
if (m_Thread)
|
||||
{
|
||||
pthread_mutex_lock(&m_BufferMutex);
|
||||
{
|
||||
if (int(m_Buffering.size()) < m_BufferSize)
|
||||
{
|
||||
size_t oldSize = m_Buffering.size();
|
||||
size_t newSize = oldSize + size;
|
||||
m_WrittenSize = newSize;
|
||||
m_Buffering.resize(newSize);
|
||||
memcpy(&m_Buffering[oldSize], data, size);
|
||||
pthread_cond_signal(&m_hasBuffer);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&m_BufferMutex);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
WriteImpl(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
int GetFreeBufferSize()
|
||||
{
|
||||
#if defined(USE_THREADS)
|
||||
if (m_Thread)
|
||||
{
|
||||
int writtenSize;
|
||||
pthread_mutex_lock(&m_BufferMutex);
|
||||
{
|
||||
writtenSize = m_WrittenSize;
|
||||
}
|
||||
pthread_mutex_unlock(&m_BufferMutex);
|
||||
return m_BufferSize - writtenSize;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
audio_buf_info info;
|
||||
ioctl(m_FD, SNDCTL_DSP_GETOSPACE, &info);
|
||||
int writtenSize = info.fragsize * info.fragstotal - info.bytes;
|
||||
return std::max(0, m_BufferSize - writtenSize);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void WriteImpl(const void* data, int size)
|
||||
{
|
||||
const char* p = reinterpret_cast<const char*>(data);
|
||||
while (size > 0)
|
||||
{
|
||||
int result = write(m_FD, p, size);
|
||||
if (result < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
p += result;
|
||||
size -= result;
|
||||
}
|
||||
}
|
||||
|
||||
int m_FD;
|
||||
int m_BufferSize;
|
||||
|
||||
#if defined(USE_THREADS)
|
||||
pthread_t m_Thread;
|
||||
volatile bool m_isExit;
|
||||
pthread_mutex_t m_BufferMutex;
|
||||
pthread_cond_t m_hasBuffer;
|
||||
std::vector<uint8> m_PlayingBuffer;
|
||||
std::vector<uint8> m_Buffering;
|
||||
int m_WrittenSize; // for dynamic rate control
|
||||
|
||||
static void* AudioOutputThreadEntry(void* arg)
|
||||
{
|
||||
S9xAudioOutput* obj = reinterpret_cast<S9xAudioOutput*>(arg);
|
||||
obj->AudioOutputThread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void AudioOutputThread()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
pthread_mutex_lock(&m_BufferMutex);
|
||||
{
|
||||
pthread_cond_wait(&m_hasBuffer, &m_BufferMutex);
|
||||
if (m_isExit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_PlayingBuffer.swap(m_Buffering);
|
||||
m_WrittenSize = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&m_BufferMutex);
|
||||
|
||||
if (! m_PlayingBuffer.empty())
|
||||
{
|
||||
WriteImpl(&m_PlayingBuffer[0], m_PlayingBuffer.size());
|
||||
m_PlayingBuffer.resize(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // USE_THREADS
|
||||
};
|
||||
|
||||
S9xAudioOutput* s_AudioOutput = NULL;
|
||||
#endif // NOSOUND
|
||||
|
||||
}
|
||||
|
||||
void S9xExtraUsage (void)
|
||||
{
|
||||
/* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */
|
||||
|
@ -1181,49 +1327,17 @@ void S9xSamplesAvailable(void *data)
|
|||
{
|
||||
#ifndef NOSOUND
|
||||
|
||||
audio_buf_info info;
|
||||
int samples_to_write;
|
||||
int bytes_to_write;
|
||||
int bytes_written;
|
||||
static uint8 *sound_buffer = NULL;
|
||||
static int sound_buffer_size = 0;
|
||||
|
||||
|
||||
ioctl(so.sound_fd, SNDCTL_DSP_GETOSPACE, &info);
|
||||
|
||||
if (Settings.DynamicRateControl)
|
||||
{
|
||||
S9xUpdateDynamicRate(info.bytes, so.fragment_size * 4);
|
||||
S9xUpdateDynamicRate(s_AudioOutput->GetFreeBufferSize(), so.fragment_size * 4);
|
||||
}
|
||||
|
||||
samples_to_write = S9xGetSampleCount();
|
||||
|
||||
if (Settings.DynamicRateControl && !Settings.SoundSync)
|
||||
{
|
||||
// Using rate control, we should always keep the emulator's sound buffers empty to
|
||||
// maintain an accurate measurement.
|
||||
if (samples_to_write > (info.bytes >> 1))
|
||||
{
|
||||
S9xClearSamples();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
|
||||
{
|
||||
while (info.bytes >> 1 < samples_to_write)
|
||||
{
|
||||
int usec_to_sleep = ((samples_to_write >> 1) - (info.bytes >> 2)) * 10000 /
|
||||
(Settings.SoundPlaybackRate / 100);
|
||||
usleep(usec_to_sleep > 0 ? usec_to_sleep : 0);
|
||||
ioctl(so.sound_fd, SNDCTL_DSP_GETOSPACE, &info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
samples_to_write = MIN(info.bytes >> 1, samples_to_write) & ~1;
|
||||
}
|
||||
|
||||
if (samples_to_write < 0)
|
||||
return;
|
||||
|
||||
|
@ -1235,22 +1349,7 @@ void S9xSamplesAvailable(void *data)
|
|||
|
||||
S9xMixSamples(sound_buffer, samples_to_write);
|
||||
|
||||
bytes_written = 0;
|
||||
bytes_to_write = samples_to_write * 2;
|
||||
|
||||
while (bytes_to_write > bytes_written)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = write(so.sound_fd,
|
||||
((char *)sound_buffer) + bytes_written,
|
||||
bytes_to_write - bytes_written);
|
||||
|
||||
if (result < 0)
|
||||
break;
|
||||
|
||||
bytes_written += result;
|
||||
}
|
||||
s_AudioOutput->Write(sound_buffer, samples_to_write * 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1266,6 +1365,12 @@ bool8 S9xOpenSoundDevice (void)
|
|||
return (FALSE);
|
||||
}
|
||||
|
||||
s_AudioOutput = new S9xAudioOutput(
|
||||
so.sound_fd,
|
||||
Settings.SoundPlaybackRate,
|
||||
bool(unixSettings.ThreadSound)
|
||||
);
|
||||
|
||||
J = log2(unixSettings.SoundFragmentSize) | (4 << 16);
|
||||
if (ioctl(so.sound_fd, SNDCTL_DSP_SETFRAGMENT, &J) == -1)
|
||||
return (FALSE);
|
||||
|
@ -1295,10 +1400,6 @@ bool8 S9xOpenSoundDevice (void)
|
|||
return (TRUE);
|
||||
}
|
||||
|
||||
#ifndef NOSOUND
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
void S9xExit (void)
|
||||
{
|
||||
|
@ -1312,6 +1413,10 @@ void S9xExit (void)
|
|||
S9xNPDisconnect();
|
||||
#endif
|
||||
|
||||
#ifndef NOSOUND
|
||||
delete s_AudioOutput;
|
||||
#endif
|
||||
|
||||
Memory.SaveSRAM(S9xGetFilename(".srm", SRAM_DIR));
|
||||
S9xResetSaveTimer(FALSE);
|
||||
S9xSaveCheatFile(S9xGetFilename(".cht", CHEAT_DIR));
|
||||
|
|
Loading…
Reference in New Issue