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