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 <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <algorithm>
|
||||||
#ifdef HAVE_STRINGS_H
|
#ifdef HAVE_STRINGS_H
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_THREADS
|
#ifdef USE_THREADS
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <vector>
|
||||||
#endif
|
#endif
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
@ -114,10 +116,6 @@ struct SoundStatus
|
||||||
{
|
{
|
||||||
int sound_fd;
|
int sound_fd;
|
||||||
uint32 fragment_size;
|
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;
|
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
|
#ifdef JOYSTICK_SUPPORT
|
||||||
static uint8 js_mod[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
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 };
|
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 S9xDisplayPollAxis (uint32, int16 *);
|
||||||
bool S9xDisplayPollPointer (uint32, int16 *, int16 *);
|
bool S9xDisplayPollPointer (uint32, int16 *, int16 *);
|
||||||
|
|
||||||
static long log2 (long);
|
|
||||||
static void SoundTrigger (void);
|
|
||||||
static void InitTimer (void);
|
|
||||||
static void NSRTControllerSetup (void);
|
static void NSRTControllerSetup (void);
|
||||||
static int make_snes9x_dirs (void);
|
static int make_snes9x_dirs (void);
|
||||||
#ifndef NOSOUND
|
|
||||||
static void * S9xProcessSound (void *);
|
|
||||||
#endif
|
|
||||||
#ifdef JOYSTICK_SUPPORT
|
#ifdef JOYSTICK_SUPPORT
|
||||||
static void InitJoysticks (void);
|
static void InitJoysticks (void);
|
||||||
static bool8 ReadJoysticks (void);
|
static bool8 ReadJoysticks (void);
|
||||||
|
@ -180,6 +163,169 @@ static long log2 (long num)
|
||||||
return (n);
|
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)
|
void S9xExtraUsage (void)
|
||||||
{
|
{
|
||||||
/* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */
|
/* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */
|
||||||
|
@ -1181,49 +1327,17 @@ void S9xSamplesAvailable(void *data)
|
||||||
{
|
{
|
||||||
#ifndef NOSOUND
|
#ifndef NOSOUND
|
||||||
|
|
||||||
audio_buf_info info;
|
|
||||||
int samples_to_write;
|
int samples_to_write;
|
||||||
int bytes_to_write;
|
|
||||||
int bytes_written;
|
|
||||||
static uint8 *sound_buffer = NULL;
|
static uint8 *sound_buffer = NULL;
|
||||||
static int sound_buffer_size = 0;
|
static int sound_buffer_size = 0;
|
||||||
|
|
||||||
|
|
||||||
ioctl(so.sound_fd, SNDCTL_DSP_GETOSPACE, &info);
|
|
||||||
|
|
||||||
if (Settings.DynamicRateControl)
|
if (Settings.DynamicRateControl)
|
||||||
{
|
{
|
||||||
S9xUpdateDynamicRate(info.bytes, so.fragment_size * 4);
|
S9xUpdateDynamicRate(s_AudioOutput->GetFreeBufferSize(), so.fragment_size * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
samples_to_write = S9xGetSampleCount();
|
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)
|
if (samples_to_write < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1235,22 +1349,7 @@ void S9xSamplesAvailable(void *data)
|
||||||
|
|
||||||
S9xMixSamples(sound_buffer, samples_to_write);
|
S9xMixSamples(sound_buffer, samples_to_write);
|
||||||
|
|
||||||
bytes_written = 0;
|
s_AudioOutput->Write(sound_buffer, samples_to_write * 2);
|
||||||
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;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1266,6 +1365,12 @@ bool8 S9xOpenSoundDevice (void)
|
||||||
return (FALSE);
|
return (FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s_AudioOutput = new S9xAudioOutput(
|
||||||
|
so.sound_fd,
|
||||||
|
Settings.SoundPlaybackRate,
|
||||||
|
bool(unixSettings.ThreadSound)
|
||||||
|
);
|
||||||
|
|
||||||
J = log2(unixSettings.SoundFragmentSize) | (4 << 16);
|
J = log2(unixSettings.SoundFragmentSize) | (4 << 16);
|
||||||
if (ioctl(so.sound_fd, SNDCTL_DSP_SETFRAGMENT, &J) == -1)
|
if (ioctl(so.sound_fd, SNDCTL_DSP_SETFRAGMENT, &J) == -1)
|
||||||
return (FALSE);
|
return (FALSE);
|
||||||
|
@ -1295,10 +1400,6 @@ bool8 S9xOpenSoundDevice (void)
|
||||||
return (TRUE);
|
return (TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NOSOUND
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void S9xExit (void)
|
void S9xExit (void)
|
||||||
{
|
{
|
||||||
|
@ -1312,6 +1413,10 @@ void S9xExit (void)
|
||||||
S9xNPDisconnect();
|
S9xNPDisconnect();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef NOSOUND
|
||||||
|
delete s_AudioOutput;
|
||||||
|
#endif
|
||||||
|
|
||||||
Memory.SaveSRAM(S9xGetFilename(".srm", SRAM_DIR));
|
Memory.SaveSRAM(S9xGetFilename(".srm", SRAM_DIR));
|
||||||
S9xResetSaveTimer(FALSE);
|
S9xResetSaveTimer(FALSE);
|
||||||
S9xSaveCheatFile(S9xGetFilename(".cht", CHEAT_DIR));
|
S9xSaveCheatFile(S9xGetFilename(".cht", CHEAT_DIR));
|
||||||
|
|
Loading…
Reference in New Issue