mirror of https://github.com/snes9xgit/snes9x.git
Gtk: Simplify drivers by moving port code to gtk_sound.cpp.
This commit is contained in:
parent
9a0712b258
commit
8c5b6d012e
|
@ -150,7 +150,7 @@ void S9xPortSoundDeinit()
|
|||
S9xSoundStop();
|
||||
|
||||
if (driver)
|
||||
driver->terminate();
|
||||
driver->deinit();
|
||||
|
||||
delete driver;
|
||||
}
|
||||
|
@ -167,6 +167,55 @@ void S9xSoundStop()
|
|||
driver->stop();
|
||||
}
|
||||
|
||||
static std::vector<int16_t> temp_buffer;
|
||||
void S9xSamplesAvailable(void *userdata)
|
||||
{
|
||||
bool clear_leftover_samples = false;
|
||||
int samples = S9xGetSampleCount();
|
||||
int space_free = driver->space_free();
|
||||
|
||||
if (space_free < samples)
|
||||
{
|
||||
if (!Settings.SoundSync)
|
||||
clear_leftover_samples = true;
|
||||
|
||||
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
|
||||
{
|
||||
for (int i = 0; i < 200; i++) // Wait for a max of 5ms
|
||||
{
|
||||
space_free = driver->space_free();
|
||||
if (space_free < samples)
|
||||
usleep(50);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (space_free < samples)
|
||||
samples = space_free & ~1;
|
||||
|
||||
if (samples == 0)
|
||||
{
|
||||
S9xClearSamples();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((int)temp_buffer.size() < samples)
|
||||
temp_buffer.resize(samples);
|
||||
S9xMixSamples((uint8_t *)temp_buffer.data(), samples);
|
||||
driver->write_samples(temp_buffer.data(), samples);
|
||||
|
||||
if (clear_leftover_samples)
|
||||
S9xClearSamples();
|
||||
|
||||
if (Settings.DynamicRateControl)
|
||||
{
|
||||
auto level = driver->buffer_level();
|
||||
S9xUpdateDynamicRate(level.first, level.second);
|
||||
}
|
||||
}
|
||||
|
||||
bool8 S9xOpenSoundDevice()
|
||||
{
|
||||
if (gui_config->mute_sound)
|
||||
|
@ -174,7 +223,8 @@ bool8 S9xOpenSoundDevice()
|
|||
|
||||
gui_config->sound_buffer_size = CLAMP(gui_config->sound_buffer_size, 2, 256);
|
||||
|
||||
return driver->open_device();
|
||||
S9xSetSamplesAvailableCallback(S9xSamplesAvailable, nullptr);
|
||||
return driver->open_device(Settings.SoundPlaybackRate, gui_config->sound_buffer_size);
|
||||
}
|
||||
|
||||
/* This really shouldn't be in the port layer */
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
For further information, consult the LICENSE file in the root directory.
|
||||
\*****************************************************************************/
|
||||
|
||||
#ifndef __GTK_SOUND_DRIVER_H
|
||||
#define __GTK_SOUND_DRIVER_H
|
||||
#ifndef __S9X_SOUND_DRIVER_H
|
||||
#define __S9X_SOUND_DRIVER_H
|
||||
|
||||
#include "gtk_s9x.h"
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
|
||||
class S9xSoundDriver
|
||||
{
|
||||
|
@ -15,9 +16,12 @@ class S9xSoundDriver
|
|||
virtual ~S9xSoundDriver()
|
||||
{
|
||||
}
|
||||
virtual void write_samples(int16_t *data, int samples) = 0;
|
||||
virtual int space_free() = 0;
|
||||
virtual std::pair<int, int> buffer_level() = 0;
|
||||
virtual void init() = 0;
|
||||
virtual void terminate() = 0;
|
||||
virtual bool open_device() = 0;
|
||||
virtual void deinit() = 0;
|
||||
virtual bool open_device(int playback_rate, int buffer_size) = 0;
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
};
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
\*****************************************************************************/
|
||||
|
||||
#include "gtk_sound_driver_alsa.h"
|
||||
#include "gtk_s9x.h"
|
||||
#include "snes9x.h"
|
||||
#include "apu/apu.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
@ -16,27 +13,22 @@
|
|||
S9xAlsaSoundDriver::S9xAlsaSoundDriver()
|
||||
{
|
||||
pcm = {};
|
||||
sound_buffer.clear();
|
||||
}
|
||||
|
||||
void S9xAlsaSoundDriver::init()
|
||||
{
|
||||
}
|
||||
|
||||
void S9xAlsaSoundDriver::terminate()
|
||||
void S9xAlsaSoundDriver::deinit()
|
||||
{
|
||||
stop();
|
||||
|
||||
S9xSetSamplesAvailableCallback(nullptr, nullptr);
|
||||
|
||||
if (pcm)
|
||||
{
|
||||
snd_pcm_drain(pcm);
|
||||
snd_pcm_close(pcm);
|
||||
pcm = nullptr;
|
||||
}
|
||||
|
||||
sound_buffer.clear();
|
||||
}
|
||||
|
||||
void S9xAlsaSoundDriver::start()
|
||||
|
@ -47,16 +39,17 @@ void S9xAlsaSoundDriver::stop()
|
|||
{
|
||||
}
|
||||
|
||||
bool S9xAlsaSoundDriver::open_device()
|
||||
bool S9xAlsaSoundDriver::open_device(int playback_rate, int buffer_size_ms)
|
||||
{
|
||||
int err;
|
||||
unsigned int periods = 8;
|
||||
unsigned int buffer_size = gui_config->sound_buffer_size * 1000;
|
||||
unsigned int buffer_size = buffer_size_ms * 1000;
|
||||
snd_pcm_sw_params_t *sw_params;
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
|
||||
unsigned int min = 0;
|
||||
unsigned int max = 0;
|
||||
unsigned int playback_rate_param = 0;
|
||||
|
||||
printf("ALSA sound driver initializing...\n");
|
||||
printf(" --> (Device: default)...\n");
|
||||
|
@ -69,8 +62,8 @@ bool S9xAlsaSoundDriver::open_device()
|
|||
}
|
||||
|
||||
printf(" --> (16-bit Stereo, %dhz, %d ms)...\n",
|
||||
Settings.SoundPlaybackRate,
|
||||
gui_config->sound_buffer_size);
|
||||
playback_rate,
|
||||
buffer_size_ms);
|
||||
|
||||
snd_pcm_hw_params_alloca(&hw_params);
|
||||
snd_pcm_hw_params_any(pcm, hw_params);
|
||||
|
@ -82,12 +75,14 @@ bool S9xAlsaSoundDriver::open_device()
|
|||
snd_pcm_hw_params_get_rate_min(hw_params, &min, nullptr);
|
||||
snd_pcm_hw_params_get_rate_max(hw_params, &max, nullptr);
|
||||
printf(" --> Available rates: %d to %d\n", min, max);
|
||||
if (Settings.SoundPlaybackRate > max && Settings.SoundPlaybackRate < min)
|
||||
if (playback_rate > (int)max || playback_rate < (int)min)
|
||||
{
|
||||
printf(" Rate %d not available. Using %d instead.\n", Settings.SoundPlaybackRate, max);
|
||||
Settings.SoundPlaybackRate = max;
|
||||
printf(" Rate %d not available. Using %d instead.\n", playback_rate, max);
|
||||
playback_rate = max;
|
||||
}
|
||||
snd_pcm_hw_params_set_rate_near(pcm, hw_params, &Settings.SoundPlaybackRate, nullptr);
|
||||
|
||||
playback_rate_param = playback_rate;
|
||||
snd_pcm_hw_params_set_rate_near(pcm, hw_params, &playback_rate_param, nullptr);
|
||||
|
||||
snd_pcm_hw_params_get_buffer_time_min(hw_params, &min, nullptr);
|
||||
snd_pcm_hw_params_get_buffer_time_max(hw_params, &max, nullptr);
|
||||
|
@ -121,7 +116,7 @@ bool S9xAlsaSoundDriver::open_device()
|
|||
snd_pcm_sw_params_set_start_threshold(pcm, sw_params, (alsa_buffer_size / 2));
|
||||
err = snd_pcm_sw_params(pcm, sw_params);
|
||||
|
||||
output_buffer_size = snd_pcm_frames_to_bytes(pcm, alsa_buffer_size);
|
||||
output_buffer_size_bytes = snd_pcm_frames_to_bytes(pcm, alsa_buffer_size);
|
||||
|
||||
if (err < 0)
|
||||
{
|
||||
|
@ -131,10 +126,6 @@ bool S9xAlsaSoundDriver::open_device()
|
|||
|
||||
printf("OK\n");
|
||||
|
||||
S9xSetSamplesAvailableCallback([](void *userdata) {
|
||||
((decltype(this)) userdata)->samples_available();;
|
||||
}, this);
|
||||
|
||||
return true;
|
||||
|
||||
close_fail:
|
||||
|
@ -148,7 +139,7 @@ fail:
|
|||
return false;
|
||||
}
|
||||
|
||||
void S9xAlsaSoundDriver::samples_available()
|
||||
void S9xAlsaSoundDriver::write_samples(int16_t *data, int samples)
|
||||
{
|
||||
snd_pcm_sframes_t frames_written, frames;
|
||||
size_t bytes;
|
||||
|
@ -161,47 +152,21 @@ void S9xAlsaSoundDriver::samples_available()
|
|||
return;
|
||||
}
|
||||
|
||||
int snes_frames_available = S9xGetSampleCount() >> 1;
|
||||
|
||||
if (Settings.DynamicRateControl && !Settings.SoundSync)
|
||||
{
|
||||
// Using rate control, we should always keep the emulator's sound buffers empty to
|
||||
// maintain an accurate measurement.
|
||||
if (frames < snes_frames_available)
|
||||
{
|
||||
S9xClearSamples();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
|
||||
{
|
||||
snd_pcm_nonblock(pcm, 0);
|
||||
frames = snes_frames_available;
|
||||
}
|
||||
else
|
||||
{
|
||||
snd_pcm_nonblock(pcm, 1);
|
||||
frames = MIN(frames, snes_frames_available);
|
||||
}
|
||||
snd_pcm_nonblock(pcm, 0);
|
||||
if (frames > samples / 2)
|
||||
frames = samples / 2;
|
||||
|
||||
bytes = snd_pcm_frames_to_bytes(pcm, frames);
|
||||
if (bytes <= 0)
|
||||
return;
|
||||
|
||||
if (sound_buffer.size() < bytes)
|
||||
sound_buffer.resize(bytes);
|
||||
|
||||
S9xMixSamples(sound_buffer.data(), frames * 2);
|
||||
|
||||
frames_written = 0;
|
||||
|
||||
while (frames_written < frames)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = snd_pcm_writei(pcm,
|
||||
&sound_buffer[snd_pcm_frames_to_bytes(pcm, frames_written)],
|
||||
&data[snd_pcm_frames_to_bytes(pcm, frames_written) / 2],
|
||||
frames - frames_written);
|
||||
|
||||
if (result < 0)
|
||||
|
@ -218,11 +183,14 @@ void S9xAlsaSoundDriver::samples_available()
|
|||
frames_written += result;
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.DynamicRateControl)
|
||||
{
|
||||
frames = snd_pcm_avail(pcm);
|
||||
S9xUpdateDynamicRate(snd_pcm_frames_to_bytes(pcm, frames),
|
||||
output_buffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
int S9xAlsaSoundDriver::space_free()
|
||||
{
|
||||
return snd_pcm_avail(pcm) * 2;
|
||||
}
|
||||
|
||||
std::pair<int, int> S9xAlsaSoundDriver::buffer_level()
|
||||
{
|
||||
return { snd_pcm_avail(pcm), output_buffer_size_bytes / 4 };
|
||||
}
|
||||
|
|
|
@ -15,17 +15,18 @@ class S9xAlsaSoundDriver : public S9xSoundDriver
|
|||
{
|
||||
public:
|
||||
S9xAlsaSoundDriver();
|
||||
void init();
|
||||
void terminate();
|
||||
bool open_device();
|
||||
void start();
|
||||
void stop();
|
||||
void samples_available();
|
||||
void init() override;
|
||||
void deinit() override;
|
||||
bool open_device(int playback_rate, int buffer_size_ms) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void write_samples(int16_t *data, int samples) override;
|
||||
int space_free() override;
|
||||
std::pair<int, int> buffer_level() override;
|
||||
|
||||
private:
|
||||
snd_pcm_t *pcm;
|
||||
std::vector<uint8_t> sound_buffer;
|
||||
int output_buffer_size;
|
||||
int output_buffer_size_bytes;
|
||||
};
|
||||
|
||||
#endif /* __GTK_SOUND_DRIVER_ALSA_H */
|
||||
|
|
|
@ -5,47 +5,30 @@
|
|||
\*****************************************************************************/
|
||||
|
||||
#include "gtk_sound_driver_oss.h"
|
||||
#include "gtk_s9x.h"
|
||||
#include "snes9x.h"
|
||||
#include "apu/apu.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static void oss_samples_available(void *data)
|
||||
{
|
||||
((S9xOSSSoundDriver *)data)->samples_available();
|
||||
}
|
||||
#include <unistd.h>
|
||||
|
||||
S9xOSSSoundDriver::S9xOSSSoundDriver()
|
||||
{
|
||||
filedes = -1;
|
||||
sound_buffer = NULL;
|
||||
sound_buffer_size = 0;
|
||||
}
|
||||
|
||||
void S9xOSSSoundDriver::init()
|
||||
{
|
||||
}
|
||||
|
||||
void S9xOSSSoundDriver::terminate()
|
||||
void S9xOSSSoundDriver::deinit()
|
||||
{
|
||||
stop();
|
||||
|
||||
S9xSetSamplesAvailableCallback(NULL, NULL);
|
||||
|
||||
if (filedes >= 0)
|
||||
{
|
||||
close(filedes);
|
||||
}
|
||||
|
||||
if (sound_buffer)
|
||||
{
|
||||
free(sound_buffer);
|
||||
sound_buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void S9xOSSSoundDriver::start()
|
||||
|
@ -56,16 +39,16 @@ void S9xOSSSoundDriver::stop()
|
|||
{
|
||||
}
|
||||
|
||||
bool S9xOSSSoundDriver::open_device()
|
||||
bool S9xOSSSoundDriver::open_device(int playback_rate, int buffer_size_ms)
|
||||
{
|
||||
int temp;
|
||||
audio_buf_info info;
|
||||
|
||||
output_buffer_size = (gui_config->sound_buffer_size * Settings.SoundPlaybackRate) / 1000;
|
||||
output_buffer_size_bytes = (buffer_size_ms * playback_rate) / 1000;
|
||||
|
||||
output_buffer_size *= 4;
|
||||
if (output_buffer_size < 256)
|
||||
output_buffer_size = 256;
|
||||
output_buffer_size_bytes *= 4;
|
||||
if (output_buffer_size_bytes < 256)
|
||||
output_buffer_size_bytes = 256;
|
||||
|
||||
printf("OSS sound driver initializing...\n");
|
||||
|
||||
|
@ -115,31 +98,29 @@ bool S9xOSSSoundDriver::open_device()
|
|||
|
||||
printf("OK\n");
|
||||
|
||||
printf(" --> (Frequency: %d)...", Settings.SoundPlaybackRate);
|
||||
if (ioctl(filedes, SNDCTL_DSP_SPEED, &Settings.SoundPlaybackRate) < 0)
|
||||
printf(" --> (Frequency: %d)...", playback_rate);
|
||||
if (ioctl(filedes, SNDCTL_DSP_SPEED, &playback_rate) < 0)
|
||||
goto close_fail;
|
||||
|
||||
printf("OK\n");
|
||||
|
||||
/* OSS requires a power-of-two buffer size, first 16 bits are the number
|
||||
* of fragments to generate, second 16 are the respective power-of-two. */
|
||||
temp = (4 << 16) | (S9xSoundBase2log(output_buffer_size / 4));
|
||||
temp = (4 << 16) | (S9xSoundBase2log(output_buffer_size_bytes / 4));
|
||||
|
||||
if (ioctl(filedes, SNDCTL_DSP_SETFRAGMENT, &temp) < 0)
|
||||
goto close_fail;
|
||||
|
||||
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
|
||||
|
||||
output_buffer_size = info.fragsize * info.fragstotal;
|
||||
output_buffer_size_bytes = info.fragsize * info.fragstotal;
|
||||
|
||||
printf(" --> (Buffer size: %d bytes, %dms latency)...",
|
||||
output_buffer_size,
|
||||
(output_buffer_size * 250) / Settings.SoundPlaybackRate);
|
||||
output_buffer_size_bytes,
|
||||
(output_buffer_size_bytes * 250) / playback_rate);
|
||||
|
||||
printf("OK\n");
|
||||
|
||||
S9xSetSamplesAvailableCallback(oss_samples_available, this);
|
||||
|
||||
return true;
|
||||
|
||||
close_fail:
|
||||
|
@ -152,68 +133,41 @@ fail:
|
|||
return false;
|
||||
}
|
||||
|
||||
void S9xOSSSoundDriver::samples_available()
|
||||
int S9xOSSSoundDriver::space_free()
|
||||
{
|
||||
audio_buf_info info;
|
||||
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
|
||||
return info.bytes / 2;
|
||||
}
|
||||
|
||||
std::pair<int, int> S9xOSSSoundDriver::buffer_level()
|
||||
{
|
||||
return { space_free(), output_buffer_size_bytes / 2};
|
||||
}
|
||||
|
||||
void S9xOSSSoundDriver::write_samples(int16_t *data, int samples)
|
||||
{
|
||||
audio_buf_info info;
|
||||
int samples_to_write;
|
||||
int bytes_to_write;
|
||||
int bytes_written;
|
||||
|
||||
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
|
||||
|
||||
if (Settings.DynamicRateControl)
|
||||
{
|
||||
S9xUpdateDynamicRate(info.bytes, output_buffer_size);
|
||||
}
|
||||
if (samples > info.bytes / 2)
|
||||
samples = info.bytes / 2;
|
||||
|
||||
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(filedes, SNDCTL_DSP_GETOSPACE, &info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
samples_to_write = MIN(info.bytes >> 1, samples_to_write) & ~1;
|
||||
}
|
||||
|
||||
if (samples_to_write < 0)
|
||||
if (samples == 0)
|
||||
return;
|
||||
|
||||
if (sound_buffer_size < samples_to_write * 2)
|
||||
{
|
||||
sound_buffer = (uint8 *)realloc(sound_buffer, samples_to_write * 2);
|
||||
sound_buffer_size = samples_to_write * 2;
|
||||
}
|
||||
|
||||
S9xMixSamples(sound_buffer, samples_to_write);
|
||||
|
||||
bytes_written = 0;
|
||||
bytes_to_write = samples_to_write * 2;
|
||||
bytes_to_write = samples * 2;
|
||||
|
||||
while (bytes_to_write > bytes_written)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = write(filedes,
|
||||
((char *)sound_buffer) + bytes_written,
|
||||
((char *)data) + bytes_written,
|
||||
bytes_to_write - bytes_written);
|
||||
|
||||
if (result < 0)
|
||||
|
|
|
@ -14,18 +14,18 @@ class S9xOSSSoundDriver : public S9xSoundDriver
|
|||
{
|
||||
public:
|
||||
S9xOSSSoundDriver();
|
||||
void init();
|
||||
void terminate();
|
||||
bool open_device();
|
||||
void start();
|
||||
void stop();
|
||||
void samples_available();
|
||||
void init() override;
|
||||
void deinit() override;
|
||||
bool open_device(int playback_rate, int buffer_size_ms) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void write_samples(int16_t *data, int samples) override;
|
||||
int space_free() override;
|
||||
std::pair<int, int> buffer_level() override;
|
||||
|
||||
private:
|
||||
int filedes;
|
||||
uint8 *sound_buffer;
|
||||
int sound_buffer_size;
|
||||
int output_buffer_size;
|
||||
int output_buffer_size_bytes;
|
||||
};
|
||||
|
||||
#endif /* __GTK_SOUND_DRIVER_OSS_H */
|
||||
|
|
|
@ -5,20 +5,11 @@
|
|||
\*****************************************************************************/
|
||||
|
||||
#include "gtk_sound_driver_portaudio.h"
|
||||
#include "gtk_s9x.h"
|
||||
#include "apu/apu.h"
|
||||
#include "snes9x.h"
|
||||
|
||||
static void port_audio_samples_available_callback(void *data)
|
||||
{
|
||||
((S9xPortAudioSoundDriver *)data)->samples_available();
|
||||
}
|
||||
#include <cstring>
|
||||
|
||||
S9xPortAudioSoundDriver::S9xPortAudioSoundDriver()
|
||||
{
|
||||
audio_stream = NULL;
|
||||
sound_buffer = NULL;
|
||||
sound_buffer_size = 0;
|
||||
}
|
||||
|
||||
void S9xPortAudioSoundDriver::init()
|
||||
|
@ -33,26 +24,18 @@ void S9xPortAudioSoundDriver::init()
|
|||
Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
void S9xPortAudioSoundDriver::terminate()
|
||||
void S9xPortAudioSoundDriver::deinit()
|
||||
{
|
||||
stop();
|
||||
|
||||
S9xSetSamplesAvailableCallback(NULL, NULL);
|
||||
|
||||
Pa_Terminate();
|
||||
|
||||
if (sound_buffer)
|
||||
{
|
||||
free(sound_buffer);
|
||||
sound_buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void S9xPortAudioSoundDriver::start()
|
||||
{
|
||||
PaError err;
|
||||
|
||||
if (audio_stream != NULL && !(gui_config->mute_sound))
|
||||
if (audio_stream != NULL)
|
||||
{
|
||||
if ((Pa_IsStreamActive(audio_stream)))
|
||||
return;
|
||||
|
@ -74,16 +57,7 @@ void S9xPortAudioSoundDriver::stop()
|
|||
}
|
||||
}
|
||||
|
||||
void S9xPortAudioSoundDriver::set_buffer_min(int frames)
|
||||
{
|
||||
if ((sound_buffer_size < frames) || sound_buffer == NULL)
|
||||
{
|
||||
sound_buffer = (uint8 *)realloc(sound_buffer, frames << 2);
|
||||
sound_buffer_size = frames;
|
||||
}
|
||||
}
|
||||
|
||||
bool S9xPortAudioSoundDriver::open_device()
|
||||
bool S9xPortAudioSoundDriver::open_device(int playback_rate, int buffer_size_ms)
|
||||
{
|
||||
PaStreamParameters param;
|
||||
const PaDeviceInfo *device_info;
|
||||
|
@ -133,7 +107,7 @@ bool S9xPortAudioSoundDriver::open_device()
|
|||
}
|
||||
|
||||
param.device = hostapi_info->defaultOutputDevice;
|
||||
param.suggestedLatency = gui_config->sound_buffer_size * 0.001;
|
||||
param.suggestedLatency = buffer_size_ms * 0.001;
|
||||
|
||||
printf("(%s : %s, latency %dms)...\n",
|
||||
hostapi_info->name,
|
||||
|
@ -145,7 +119,7 @@ bool S9xPortAudioSoundDriver::open_device()
|
|||
err = Pa_OpenStream(&audio_stream,
|
||||
NULL,
|
||||
¶m,
|
||||
Settings.SoundPlaybackRate,
|
||||
playback_rate,
|
||||
0,
|
||||
paNoFlag,
|
||||
NULL,
|
||||
|
@ -174,15 +148,20 @@ bool S9xPortAudioSoundDriver::open_device()
|
|||
return false;
|
||||
}
|
||||
|
||||
S9xSetSamplesAvailableCallback(port_audio_samples_available_callback, this);
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void S9xPortAudioSoundDriver::samples_available()
|
||||
int S9xPortAudioSoundDriver::space_free()
|
||||
{
|
||||
return Pa_GetStreamWriteAvailable(audio_stream) * 2;
|
||||
}
|
||||
|
||||
std::pair<int, int> S9xPortAudioSoundDriver::buffer_level()
|
||||
{
|
||||
return { Pa_GetStreamWriteAvailable(audio_stream), output_buffer_size };
|
||||
}
|
||||
|
||||
void S9xPortAudioSoundDriver::write_samples(int16_t *data, int samples)
|
||||
{
|
||||
int frames;
|
||||
|
||||
|
@ -191,38 +170,14 @@ void S9xPortAudioSoundDriver::samples_available()
|
|||
if (frames == output_buffer_size)
|
||||
{
|
||||
// Prime the stream
|
||||
set_buffer_min(output_buffer_size >> 1);
|
||||
memset(sound_buffer, 0, output_buffer_size << 1);
|
||||
Pa_WriteStream(audio_stream, sound_buffer, output_buffer_size >> 1);
|
||||
std::vector<int16_t> tmp(output_buffer_size);
|
||||
memset(tmp.data(), 0, output_buffer_size << 1);
|
||||
Pa_WriteStream(audio_stream, tmp.data(), output_buffer_size >> 1);
|
||||
frames -= output_buffer_size >> 1;
|
||||
}
|
||||
|
||||
int snes_frames_available = S9xGetSampleCount() >> 1;
|
||||
if (frames > samples / 2)
|
||||
frames = samples / 2;
|
||||
|
||||
if (Settings.DynamicRateControl && !Settings.SoundSync)
|
||||
{
|
||||
// Using rate control, we should always keep the emulator's sound buffers empty to
|
||||
// maintain an accurate measurement.
|
||||
if (frames < snes_frames_available)
|
||||
{
|
||||
S9xClearSamples();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Rely on PortAudio's blocking behavior to sync
|
||||
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
|
||||
frames = snes_frames_available;
|
||||
|
||||
frames = MIN(frames, snes_frames_available);
|
||||
|
||||
set_buffer_min(frames);
|
||||
S9xMixSamples(sound_buffer, frames << 1);
|
||||
Pa_WriteStream(audio_stream, sound_buffer, frames);
|
||||
|
||||
if (Settings.DynamicRateControl)
|
||||
{
|
||||
frames = Pa_GetStreamWriteAvailable(audio_stream);
|
||||
S9xUpdateDynamicRate(frames, output_buffer_size);
|
||||
}
|
||||
Pa_WriteStream(audio_stream, data, frames);
|
||||
}
|
||||
|
|
|
@ -17,18 +17,18 @@ class S9xPortAudioSoundDriver : public S9xSoundDriver
|
|||
{
|
||||
public:
|
||||
S9xPortAudioSoundDriver();
|
||||
void init();
|
||||
void terminate();
|
||||
bool open_device();
|
||||
void start();
|
||||
void stop();
|
||||
void init() override;
|
||||
void deinit() override;
|
||||
bool open_device(int playback_rate, int buffer_size) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void write_samples(int16_t *data, int samples) override;
|
||||
int space_free() override;
|
||||
std::pair<int, int> buffer_level() override;
|
||||
void samples_available();
|
||||
|
||||
private:
|
||||
void set_buffer_min(int frames);
|
||||
PaStream *audio_stream;
|
||||
int sound_buffer_size;
|
||||
uint8 *sound_buffer;
|
||||
int output_buffer_size;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,10 +5,8 @@
|
|||
\*****************************************************************************/
|
||||
|
||||
#include "gtk_sound_driver_pulse.h"
|
||||
#include "gtk_s9x.h"
|
||||
#include "snes9x.h"
|
||||
#include "apu/apu.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
|
@ -26,10 +24,8 @@ void S9xPulseSoundDriver::init()
|
|||
buffer_size = {};
|
||||
}
|
||||
|
||||
void S9xPulseSoundDriver::terminate()
|
||||
void S9xPulseSoundDriver::deinit()
|
||||
{
|
||||
S9xSetSamplesAvailableCallback(nullptr, nullptr);
|
||||
|
||||
if (mainloop)
|
||||
pa_threaded_mainloop_stop(mainloop);
|
||||
|
||||
|
@ -104,17 +100,17 @@ static void stream_state_callback(pa_stream *p, void *userdata)
|
|||
}
|
||||
}
|
||||
|
||||
bool S9xPulseSoundDriver::open_device()
|
||||
bool S9xPulseSoundDriver::open_device(int playback_rate, int buffer_size_ms)
|
||||
{
|
||||
init();
|
||||
|
||||
pa_sample_spec ss;
|
||||
ss.channels = 2;
|
||||
ss.format = PA_SAMPLE_S16NE;
|
||||
ss.rate = Settings.SoundPlaybackRate;
|
||||
ss.rate = playback_rate;
|
||||
|
||||
pa_buffer_attr buffer_attr;
|
||||
buffer_attr.tlength = pa_usec_to_bytes(gui_config->sound_buffer_size * 1000, &ss);
|
||||
buffer_attr.tlength = pa_usec_to_bytes(buffer_size_ms * 1000, &ss);
|
||||
buffer_attr.maxlength = buffer_attr.tlength * 2;
|
||||
buffer_attr.minreq = pa_usec_to_bytes(3000, &ss);
|
||||
buffer_attr.prebuf = -1;
|
||||
|
@ -122,8 +118,8 @@ bool S9xPulseSoundDriver::open_device()
|
|||
printf("PulseAudio sound driver initializing...\n");
|
||||
|
||||
printf(" --> (%dhz, 16-bit Stereo, %dms)...",
|
||||
Settings.SoundPlaybackRate,
|
||||
gui_config->sound_buffer_size);
|
||||
playback_rate,
|
||||
buffer_size_ms);
|
||||
|
||||
int err = PA_ERR_UNKNOWN;
|
||||
mainloop = pa_threaded_mainloop_new();
|
||||
|
@ -164,73 +160,39 @@ bool S9xPulseSoundDriver::open_device()
|
|||
|
||||
printf("OK\n");
|
||||
|
||||
S9xSetSamplesAvailableCallback([](void *userdata) {
|
||||
((decltype(this)) userdata)->samples_available();;
|
||||
}, this);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void S9xPulseSoundDriver::samples_available()
|
||||
int S9xPulseSoundDriver::space_free()
|
||||
{
|
||||
lock();
|
||||
size_t bytes = pa_stream_writable_size(stream);
|
||||
unlock();
|
||||
return bytes / 2;
|
||||
}
|
||||
|
||||
std::pair<int, int> S9xPulseSoundDriver::buffer_level()
|
||||
{
|
||||
bool clear_samples = false;
|
||||
|
||||
lock();
|
||||
size_t bytes = pa_stream_writable_size(stream);
|
||||
auto buffer_attr = pa_stream_get_buffer_attr(stream);
|
||||
unlock();
|
||||
|
||||
buffer_size = buffer_attr->tlength;
|
||||
return { bytes, buffer_size };
|
||||
}
|
||||
|
||||
size_t samples = S9xGetSampleCount();
|
||||
|
||||
int frames_available = samples / 2;
|
||||
int frames_writable = bytes / 4;
|
||||
|
||||
if (frames_writable < frames_available)
|
||||
{
|
||||
if (!Settings.SoundSync)
|
||||
{
|
||||
clear_samples = true;
|
||||
}
|
||||
|
||||
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
|
||||
{
|
||||
lock();
|
||||
int i;
|
||||
for (i = 0; i < 500; i++)
|
||||
{
|
||||
bytes = pa_stream_writable_size(stream);
|
||||
if (bytes / 2 < samples)
|
||||
{
|
||||
unlock();
|
||||
usleep(50);
|
||||
lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
unlock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 500)
|
||||
printf("PulseAudio: Timed out waiting for sound sync.\n");
|
||||
}
|
||||
}
|
||||
void S9xPulseSoundDriver::write_samples(int16_t *data, int samples)
|
||||
{
|
||||
lock();
|
||||
size_t bytes = pa_stream_writable_size(stream);
|
||||
unlock();
|
||||
|
||||
bytes = MIN(bytes, samples * 2) & ~1;
|
||||
|
||||
if (!bytes)
|
||||
{
|
||||
S9xClearSamples();
|
||||
if (bytes == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
lock();
|
||||
|
||||
void *output_buffer;;
|
||||
void *output_buffer;
|
||||
if (pa_stream_begin_write(stream, &output_buffer, &bytes) != 0)
|
||||
{
|
||||
pa_stream_flush(stream, nullptr, nullptr);
|
||||
|
@ -244,17 +206,7 @@ void S9xPulseSoundDriver::samples_available()
|
|||
return;
|
||||
}
|
||||
|
||||
S9xMixSamples((uint8_t *)output_buffer, bytes >> 1);
|
||||
std::memcpy(output_buffer, data, bytes);
|
||||
pa_stream_write(stream, output_buffer, bytes, nullptr, 0, PA_SEEK_RELATIVE);
|
||||
|
||||
if (Settings.DynamicRateControl)
|
||||
{
|
||||
bytes = pa_stream_writable_size(stream);
|
||||
S9xUpdateDynamicRate(bytes, buffer_size);
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
if (clear_samples)
|
||||
S9xClearSamples();
|
||||
}
|
||||
|
|
|
@ -15,21 +15,23 @@ class S9xPulseSoundDriver : public S9xSoundDriver
|
|||
{
|
||||
public:
|
||||
S9xPulseSoundDriver();
|
||||
void init();
|
||||
void terminate();
|
||||
bool open_device();
|
||||
void start();
|
||||
void stop();
|
||||
void samples_available();
|
||||
void lock();
|
||||
void unlock();
|
||||
void wait();
|
||||
|
||||
void init() override;
|
||||
void deinit() override;
|
||||
void write_samples(int16_t *data, int samples) override;
|
||||
bool open_device(int playback_rate, int buffer_size) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
int space_free() override;
|
||||
std::pair<int, int> buffer_level() override;
|
||||
pa_threaded_mainloop *mainloop;
|
||||
pa_context *context;
|
||||
pa_stream *stream;
|
||||
|
||||
private:
|
||||
void lock();
|
||||
void unlock();
|
||||
void wait();
|
||||
|
||||
int buffer_size;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,34 +6,13 @@
|
|||
|
||||
#include "gtk_sound_driver_sdl.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "gtk_s9x.h"
|
||||
#include "apu/apu.h"
|
||||
#include "snes9x.h"
|
||||
|
||||
void S9xSDLSoundDriver::samples_available()
|
||||
void S9xSDLSoundDriver::write_samples(int16_t *data, int samples)
|
||||
{
|
||||
int snes_samples_available = S9xGetSampleCount();
|
||||
S9xMixSamples((uint8 *)temp, snes_samples_available);
|
||||
|
||||
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
|
||||
{
|
||||
mutex.lock();
|
||||
int samples = buffer.space_empty();
|
||||
mutex.unlock();
|
||||
|
||||
while (samples < snes_samples_available)
|
||||
{
|
||||
usleep(100);
|
||||
mutex.lock();
|
||||
samples = buffer.space_empty();
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
mutex.lock();
|
||||
buffer.push(temp, snes_samples_available);
|
||||
if (Settings.DynamicRateControl)
|
||||
S9xUpdateDynamicRate(buffer.space_empty(), buffer.buffer_size);
|
||||
if (samples > buffer.space_empty())
|
||||
samples = buffer.space_empty();
|
||||
buffer.push(data, samples);
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
|
@ -55,7 +34,7 @@ void S9xSDLSoundDriver::init()
|
|||
stop();
|
||||
}
|
||||
|
||||
void S9xSDLSoundDriver::terminate()
|
||||
void S9xSDLSoundDriver::deinit()
|
||||
{
|
||||
stop();
|
||||
SDL_CloseAudio();
|
||||
|
@ -64,10 +43,7 @@ void S9xSDLSoundDriver::terminate()
|
|||
|
||||
void S9xSDLSoundDriver::start()
|
||||
{
|
||||
if (!gui_config->mute_sound)
|
||||
{
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
|
||||
void S9xSDLSoundDriver::stop()
|
||||
|
@ -75,10 +51,10 @@ void S9xSDLSoundDriver::stop()
|
|||
SDL_PauseAudio(1);
|
||||
}
|
||||
|
||||
bool S9xSDLSoundDriver::open_device()
|
||||
bool S9xSDLSoundDriver::open_device(int playback_rate, int buffer_size)
|
||||
{
|
||||
audiospec = {};
|
||||
audiospec.freq = Settings.SoundPlaybackRate;
|
||||
audiospec.freq = playback_rate;
|
||||
audiospec.channels = 2;
|
||||
audiospec.format = AUDIO_S16SYS;
|
||||
audiospec.samples = audiospec.freq * 4 / 1000; // 4ms per sampling
|
||||
|
@ -101,11 +77,23 @@ bool S9xSDLSoundDriver::open_device()
|
|||
|
||||
printf("OK\n");
|
||||
|
||||
buffer.resize(gui_config->sound_buffer_size * audiospec.freq / 500);
|
||||
|
||||
S9xSetSamplesAvailableCallback([](void *userdata) {
|
||||
((decltype(this)) userdata)->samples_available();;
|
||||
}, this);
|
||||
buffer.resize(buffer_size * audiospec.freq / 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int S9xSDLSoundDriver::space_free()
|
||||
{
|
||||
mutex.lock();
|
||||
auto space_empty = buffer.space_empty();
|
||||
mutex.unlock();
|
||||
return space_empty;
|
||||
}
|
||||
|
||||
std::pair<int, int> S9xSDLSoundDriver::buffer_level()
|
||||
{
|
||||
mutex.lock();
|
||||
std::pair<int, int> level = { buffer.space_empty(), buffer.buffer_size };
|
||||
mutex.unlock();
|
||||
return level;
|
||||
}
|
|
@ -23,15 +23,18 @@ class S9xSDLSoundDriver : public S9xSoundDriver
|
|||
{
|
||||
public:
|
||||
S9xSDLSoundDriver();
|
||||
void init();
|
||||
void terminate();
|
||||
bool open_device();
|
||||
void start();
|
||||
void stop();
|
||||
void mix(unsigned char *output, int bytes);
|
||||
void samples_available();
|
||||
void init() override;
|
||||
void deinit() override;
|
||||
bool open_device(int playback_rate, int buffer_size) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void write_samples(int16_t *data, int samples) override;
|
||||
int space_free() override;
|
||||
std::pair<int, int> buffer_level() override;
|
||||
|
||||
private:
|
||||
void mix(unsigned char *output, int bytes);
|
||||
|
||||
SDL_AudioSpec audiospec;
|
||||
Resampler buffer;
|
||||
std::mutex mutex;
|
||||
|
|
Loading…
Reference in New Issue