Merge pull request #491 from snes9xgit/apurefactor

APU Refactor
This commit is contained in:
bearoso 2019-02-12 16:11:07 -06:00 committed by GitHub
commit 1ba69b0d9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1594 additions and 2081 deletions

File diff suppressed because it is too large Load Diff

View File

@ -64,7 +64,19 @@ static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] =
out [0] = l;\
out [1] = r;\
out += 2;\
}\
}
#define SPC_DSP_OUT_HOOK(l, r) \
{ \
resampler->push_sample(l, r); \
if (Settings.MSU1) \
S9xMSU1Generate(2); \
}
void SPC_DSP::set_output( Resampler *resampler )
{
this->resampler = resampler;
}
void SPC_DSP::set_output( sample_t* out, int size )
{

View File

@ -22,6 +22,8 @@ public:
typedef short sample_t;
void set_output( sample_t* out, int out_size );
void set_output( Resampler* resampler );
// Number of samples written to output since it was last set, always
// a multiple of 2. Undefined if more samples were generated than
// output buffer could hold.
@ -135,6 +137,8 @@ public:
private:
enum { brr_block_size = 9 };
Resampler *resampler;
struct state_t
{
uint8_t regs [register_count];
@ -193,6 +197,7 @@ private:
// non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP
int mute_mask;
sample_t* out;
sample_t* out_end;
sample_t* out_begin;

View File

@ -2,6 +2,8 @@
#define __SNES_HPP
#include "../../../snes9x.h"
#include "../../resampler.h"
#include "../../../msu1.h"
#define SNES9X

View File

@ -1,145 +0,0 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __HERMITE_RESAMPLER_H
#define __HERMITE_RESAMPLER_H
#include "resampler.h"
#include <assert.h>
#undef CLAMP
#undef SHORT_CLAMP
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
#define SHORT_CLAMP(n) ((short) CLAMP((n), -32768, 32767))
class HermiteResampler : public Resampler
{
protected:
float r_step;
float r_frac;
int r_left[4], r_right[4];
static inline float
hermite (float mu1, float a, float b, float c, float d)
{
float mu2, mu3, m0, m1, a0, a1, a2, a3;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (c - a) * 0.5;
m1 = (d - b) * 0.5;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
public:
HermiteResampler (int num_samples) : Resampler (num_samples)
{
clear ();
}
~HermiteResampler ()
{
}
void
time_ratio (double ratio)
{
r_step = ratio;
}
void
clear (void)
{
ring_buffer::clear ();
r_frac = 1.0;
r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0;
r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0;
}
void
read (short *data, int num_samples)
{
//If we are outputting the exact same ratio as the input, pull directly from the input buffer
if (r_step == 1.0)
{
ring_buffer::pull((unsigned char*)data, num_samples * sizeof(short));
return;
}
assert((num_samples & 1) == 0); // resampler always processes both stereo samples
int i_position = start >> 1;
int max_samples = buffer_size >> 1;
short *internal_buffer = (short *) buffer;
int o_position = 0;
int consumed = 0;
while (o_position < num_samples && consumed < buffer_size)
{
int s_left = internal_buffer[i_position];
int s_right = internal_buffer[i_position + 1];
float hermite_val[2];
while (r_frac <= 1.0 && o_position < num_samples)
{
hermite_val[0] = hermite (r_frac, r_left [0], r_left [1], r_left [2], r_left [3]);
hermite_val[1] = hermite (r_frac, r_right[0], r_right[1], r_right[2], r_right[3]);
data[o_position] = SHORT_CLAMP (hermite_val[0]);
data[o_position + 1] = SHORT_CLAMP (hermite_val[1]);
o_position += 2;
r_frac += r_step;
}
if (r_frac > 1.0)
{
r_left [0] = r_left [1];
r_left [1] = r_left [2];
r_left [2] = r_left [3];
r_left [3] = s_left;
r_right[0] = r_right[1];
r_right[1] = r_right[2];
r_right[2] = r_right[3];
r_right[3] = s_right;
r_frac -= 1.0;
i_position += 2;
if (i_position >= max_samples)
i_position -= max_samples;
consumed += 2;
}
}
size -= consumed << 1;
start += consumed << 1;
if (start >= buffer_size)
start -= buffer_size;
}
inline int
avail (void)
{
//If we are outputting the exact same ratio as the input, find out directly from the input buffer
if (r_step == 1.0)
{
return (ring_buffer::space_filled() + sizeof(short) - 1) / sizeof(short);
}
return (int) floor (((size >> 2) - r_frac) / r_step) * 2;
}
};
#endif /* __HERMITE_RESAMPLER_H */

View File

@ -4,61 +4,212 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __RESAMPLER_H
#define __RESAMPLER_H
#ifndef __NEW_RESAMPLER_H
#define __NEW_RESAMPLER_H
#include "ring_buffer.h"
#include <cstring>
#include <cassert>
#include <cstdint>
#include <cmath>
class Resampler : public ring_buffer
class Resampler
{
public:
virtual void clear (void) = 0;
virtual void time_ratio (double) = 0;
virtual void read (short *, int) = 0;
virtual int avail (void) = 0;
public:
int size;
int buffer_size;
int start;
int16_t *buffer;
Resampler (int num_samples) : ring_buffer (num_samples << 1)
float r_step;
float r_frac;
int r_left[4], r_right[4];
static inline int16_t short_clamp(int n)
{
return (int16_t)(((int16_t)n != n) ? (n >> 31) ^ 0x7fff : n);
}
static inline int min(int a, int b)
{
return ((a) < (b) ? (a) : (b));
}
static inline float hermite(float mu1, float a, float b, float c, float d)
{
float mu2, mu3, m0, m1, a0, a1, a2, a3;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (c - a) * 0.5;
m1 = (d - b) * 0.5;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
Resampler(int num_samples)
{
this->buffer_size = num_samples;
buffer = new int16_t[this->buffer_size];
r_step = 1.0;
clear();
}
~Resampler()
{
delete[] buffer;
buffer = NULL;
}
inline void time_ratio(double ratio)
{
r_step = ratio;
}
inline void clear(void)
{
start = 0;
size = 0;
memset(buffer, 0, buffer_size * 2);
r_frac = 0.0;
r_left[0] = r_left[1] = r_left[2] = r_left[3] = 0;
r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0;
}
inline bool pull(int16_t *dst, int num_samples)
{
if (space_filled() < num_samples)
return false;
memcpy(dst, buffer + start, min(num_samples, buffer_size - start) * 2);
if (num_samples > (buffer_size - start))
memcpy(dst + (buffer_size - start), buffer, (num_samples - (buffer_size - start)) * 2);
start = (start + num_samples) % buffer_size;
size -= num_samples;
return true;
}
inline void push_sample(int16_t l, int16_t r)
{
if (space_empty() >= 2)
{
int end = start + size;
if (end >= buffer_size)
end -= buffer_size;
buffer[end] = l;
buffer[end + 1] = r;
size += 2;
}
}
inline bool push(int16_t *src, int num_samples)
{
if (space_empty() < num_samples)
return false;
int end = start + size;
if (end > buffer_size)
end -= buffer_size;
int first_write_size = min(num_samples, buffer_size - end);
memcpy(buffer + end, src, first_write_size * 2);
if (num_samples > first_write_size)
memcpy(buffer, src + first_write_size, (num_samples - first_write_size) * 2);
size += num_samples;
return true;
}
void read(int16_t *data, int num_samples)
{
//If we are outputting the exact same ratio as the input, pull directly from the input buffer
if (r_step == 1.0)
{
pull(data, num_samples);
return;
}
virtual ~Resampler ()
assert((num_samples & 1) == 0); // resampler always processes both stereo samples
int o_position = 0;
while (o_position < num_samples && size > 0)
{
int s_left = buffer[start];
int s_right = buffer[start + 1];
int hermite_val[2];
while (r_frac <= 1.0 && o_position < num_samples)
{
hermite_val[0] = (int)hermite(r_frac, (float)r_left[0], (float)r_left[1], (float)r_left[2], (float)r_left[3]);
hermite_val[1] = (int)hermite(r_frac, (float)r_right[0], (float)r_right[1], (float)r_right[2], (float)r_right[3]);
data[o_position] = short_clamp(hermite_val[0]);
data[o_position + 1] = short_clamp(hermite_val[1]);
o_position += 2;
r_frac += r_step;
}
if (r_frac > 1.0)
{
r_left[0] = r_left[1];
r_left[1] = r_left[2];
r_left[2] = r_left[3];
r_left[3] = s_left;
r_right[0] = r_right[1];
r_right[1] = r_right[2];
r_right[2] = r_right[3];
r_right[3] = s_right;
r_frac -= 1.0;
start += 2;
if (start >= buffer_size)
start -= buffer_size;
size -= 2;
}
}
}
inline bool
push (short *src, int num_samples)
{
if (max_write () < num_samples)
return false;
inline int space_empty(void) const
{
return buffer_size - size;
}
!num_samples || ring_buffer::push ((unsigned char *) src, num_samples << 1);
inline int space_filled(void) const
{
return size;
}
return true;
}
inline int
space_empty (void) const
{
return buffer_size - size;
}
inline int
space_filled (void) const
{
inline int avail(void)
{
//If we are outputting the exact same ratio as the input, find out directly from the input buffer
if (r_step == 1.0)
return size;
}
inline int
max_write (void) const
{
return space_empty () >> 1;
}
return (int)trunc(((size >> 1) - r_frac) / r_step) * 2;
}
inline void
resize (int num_samples)
{
ring_buffer::resize (num_samples << 1);
}
void resize(int num_samples)
{
if (buffer)
delete[] buffer;
buffer_size = num_samples;
buffer = new int16_t[buffer_size];
clear();
}
};
#endif /* __RESAMPLER_H */
#endif /* __NEW_RESAMPLER_H */

View File

@ -1,115 +0,0 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __RING_BUFFER_H
#define __RING_BUFFER_H
#include <string.h>
#undef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
class ring_buffer
{
protected:
int size;
int buffer_size;
int start;
unsigned char *buffer;
public:
ring_buffer (int buffer_size)
{
this->buffer_size = buffer_size;
buffer = new unsigned char[this->buffer_size];
memset (buffer, 0, this->buffer_size);
size = 0;
start = 0;
}
~ring_buffer (void)
{
delete[] buffer;
}
bool
push (unsigned char *src, int bytes)
{
if (space_empty () < bytes)
return false;
int end = (start + size) % buffer_size;
int first_write_size = MIN (bytes, buffer_size - end);
memcpy (buffer + end, src, first_write_size);
if (bytes > first_write_size)
memcpy (buffer, src + first_write_size, bytes - first_write_size);
size += bytes;
return true;
}
bool
pull (unsigned char *dst, int bytes)
{
if (space_filled () < bytes)
return false;
memcpy (dst, buffer + start, MIN (bytes, buffer_size - start));
if (bytes > (buffer_size - start))
memcpy (dst + (buffer_size - start), buffer, bytes - (buffer_size - start));
start = (start + bytes) % buffer_size;
size -= bytes;
return true;
}
inline int
space_empty (void) const
{
return buffer_size - size;
}
inline int
space_filled (void) const
{
return size;
}
void
clear (void)
{
start = 0;
size = 0;
memset (buffer, 0, buffer_size);
}
void
resize (int size)
{
delete[] buffer;
buffer_size = size;
buffer = new unsigned char[buffer_size];
memset (buffer, 0, this->buffer_size);
this->size = 0;
start = 0;
}
inline void
cache_silence (void)
{
clear ();
size = buffer_size;
}
};
#endif

View File

@ -285,9 +285,6 @@ int Snes9xConfig::save_config_file ()
outbool (cf, z"DynamicRateControl", Settings.DynamicRateControl);
cf.SetInt (z"DynamicRateControlLimit", Settings.DynamicRateLimit);
outbool (cf, z"AutomaticInputRate", auto_input_rate, "Guess input rate by asking the monitor what its refresh rate is");
outbool (cf, z"16bit", Settings.SixteenBitSound);
outbool (cf, z"Stereo", Settings.Stereo);
outbool (cf, z"ReverseStereo", Settings.ReverseStereo);
cf.SetInt (z"PlaybackRate", gui_config->sound_playback_rate, "1: 8000Hz, 2: 11025Hz, 3: 16000Hz, 4: 22050Hz, 5: 32000Hz, 6: 44100Hz, 7: 48000Hz");
#undef z
@ -517,9 +514,6 @@ int Snes9xConfig::load_config_file ()
inbool (z"DynamicRateControl", Settings.DynamicRateControl);
inint (z"DynamicRateControlLimit", Settings.DynamicRateLimit);
inbool (z"AutomaticInputRate", auto_input_rate);
inbool (z"16bit", Settings.SixteenBitSound);
inbool (z"Stereo", Settings.Stereo);
inbool (z"ReverseStereo", Settings.ReverseStereo);
inint (z"PlaybackRate", gui_config->sound_playback_rate);
#undef z

View File

@ -327,23 +327,6 @@ event_hw_accel_changed (GtkComboBox *widget, gpointer data)
}
}
static void
event_frameskip_combo_changed (GtkComboBox *widget, gpointer user_data)
{
Snes9xPreferences *window = (Snes9xPreferences *) user_data;
if (window->get_combo ("frameskip_combo") == THROTTLE_SOUND_SYNC)
{
window->set_check ("dynamic_rate_control", 0);
window->enable_widget ("dynamic_rate_control", 0);
}
else
{
window->enable_widget ("dynamic_rate_control", 1);
}
}
static void
event_scale_method_changed (GtkComboBox *widget, gpointer user_data)
{
@ -532,7 +515,6 @@ Snes9xPreferences::Snes9xPreferences (Snes9xConfig *config) :
{ "game_data_clear", G_CALLBACK (event_game_data_clear) },
{ "about_clicked", G_CALLBACK (event_about_clicked) },
{ "auto_input_rate_toggled", G_CALLBACK (event_auto_input_rate_toggled) },
{ "frameskip_combo_changed", G_CALLBACK (event_frameskip_combo_changed) },
{ "calibrate", G_CALLBACK (event_calibrate) },
{ NULL, NULL }
};
@ -633,7 +615,6 @@ Snes9xPreferences::move_settings_to_dialog ()
set_combo ("default_esc_behavior", config->default_esc_behavior);
set_check ("prevent_screensaver", config->prevent_screensaver);
set_check ("force_inverted_byte_order", config->force_inverted_byte_order);
set_check ("stereo_check", Settings.Stereo);
set_combo ("playback_combo", 7 - config->sound_playback_rate);
set_combo ("hw_accel", combo_value (config->hw_accel));
set_check ("pause_emulation_on_switch", config->pause_emulation_on_switch);
@ -651,8 +632,6 @@ Snes9xPreferences::move_settings_to_dialog ()
config->auto_input_rate ? false : true);
set_spin ("sound_buffer_size", config->sound_buffer_size);
if (Settings.SkipFrames == THROTTLE_SOUND_SYNC)
Settings.DynamicRateControl = 0;
set_check ("dynamic_rate_control", Settings.DynamicRateControl);
set_spin ("dynamic_rate_limit", Settings.DynamicRateLimit / 1000.0);
set_spin ("rewind_buffer_size", config->rewind_buffer_size);
@ -702,7 +681,6 @@ Snes9xPreferences::move_settings_to_dialog ()
set_combo ("scanline_filter_intensity", config->scanline_filter_intensity);
set_combo ("frameskip_combo", Settings.SkipFrames);
enable_widget ("dynamic_rate_control", Settings.SkipFrames != THROTTLE_SOUND_SYNC);
set_check ("bilinear_filter", Settings.BilinearFilter);
#ifdef USE_OPENGL
@ -748,7 +726,6 @@ Snes9xPreferences::get_settings_from_dialog ()
if ((config->sound_driver != get_combo ("sound_driver")) ||
(config->mute_sound != get_check ("mute_sound_check")) ||
(config->sound_buffer_size != (int) get_spin ("sound_buffer_size")) ||
(Settings.Stereo != get_check ("stereo_check")) ||
(config->sound_playback_rate != (7 - (get_combo ("playback_combo")))) ||
(config->sound_input_rate != get_slider ("sound_input_rate")) ||
(config->auto_input_rate != get_check ("auto_input_rate")) ||
@ -801,7 +778,6 @@ Snes9xPreferences::get_settings_from_dialog ()
Settings.UpAndDown = get_check ("upanddown");
Settings.SuperFXClockMultiplier = get_spin ("superfx_multiplier");
config->sound_driver = get_combo ("sound_driver");
Settings.Stereo = get_check ("stereo_check");
config->sound_playback_rate = 7 - (get_combo ("playback_combo"));
config->sound_buffer_size = get_spin ("sound_buffer_size");
config->sound_input_rate = get_slider ("sound_input_rate");

View File

@ -501,29 +501,11 @@ static void S9xThrottle ()
frame_clock = now;
}
if (Settings.SkipFrames == THROTTLE_SOUND_SYNC &&
!Settings.DynamicRateControl)
if (Settings.SkipFrames == THROTTLE_SOUND_SYNC ||
Settings.SkipFrames == THROTTLE_NONE)
{
while (!S9xSyncSound ())
{
usleep (100);
/* If we can't sync sound within a half-second, we're probably deadlocked */
if (g_get_monotonic_time () - now > 500000)
{
S9xClearSamples ();
break;
}
}
frame_clock = now;
IPPU.SkippedFrames = 0;
return;
}
else if (Settings.SkipFrames == THROTTLE_NONE)
{
frame_clock = now;
}
else // THROTTLE_TIMER or THROTTLE_TIMER_FRAMESKIP
{

View File

@ -144,7 +144,10 @@ S9xPortSoundInit ()
Settings.SoundPlaybackRate = playback_rates[gui_config->sound_playback_rate];
S9xInitSound (gui_config->sound_buffer_size, 0);
if (gui_config->sound_driver == sdl_driver)
S9xInitSound(0, 0);
else
S9xInitSound(0, 0);
S9xSetSoundMute (false);
}

View File

@ -7,13 +7,13 @@
#ifndef __GTK_SOUND_H
#define __GTK_SOUND_H
void S9xPortSoundInit ();
void S9xPortSoundDeinit ();
void S9xPortSoundReinit ();
void S9xSoundStart ();
void S9xSoundStop ();
void S9xPortSoundInit();
void S9xPortSoundDeinit();
void S9xPortSoundReinit();
void S9xSoundStart();
void S9xSoundStop();
int S9xSoundBase2log (int num);
int S9xSoundPowerof2 (int num);
int S9xSoundBase2log(int num);
int S9xSoundPowerof2(int num);
#endif /* __GTK_SOUND_H */

View File

@ -11,14 +11,15 @@
class S9xSoundDriver
{
public:
virtual ~S9xSoundDriver () {}
virtual void init () = 0;
virtual void terminate () = 0;
virtual bool open_device () = 0;
virtual void start () = 0;
virtual void stop () = 0;
public:
virtual ~S9xSoundDriver()
{
}
virtual void init() = 0;
virtual void terminate() = 0;
virtual bool open_device() = 0;
virtual void start() = 0;
virtual void stop() = 0;
};
#endif /* __GTK_SOUND_DRIVER_H */

View File

@ -4,59 +4,54 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "gtk_s9x.h"
#include "gtk_sound_driver_alsa.h"
#include "gtk_s9x.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <fcntl.h>
static void
alsa_samples_available (void *data)
static void alsa_samples_available(void *data)
{
((S9xAlsaSoundDriver *) data)->samples_available ();
((S9xAlsaSoundDriver *)data)->samples_available();
}
S9xAlsaSoundDriver::S9xAlsaSoundDriver ()
S9xAlsaSoundDriver::S9xAlsaSoundDriver()
{
pcm = NULL;
sound_buffer = NULL;
sound_buffer_size = 0;
}
void
S9xAlsaSoundDriver::init ()
void S9xAlsaSoundDriver::init()
{
}
void
S9xAlsaSoundDriver::terminate ()
void S9xAlsaSoundDriver::terminate()
{
stop ();
stop();
S9xSetSamplesAvailableCallback (NULL, NULL);
S9xSetSamplesAvailableCallback(NULL, NULL);
if (pcm)
{
snd_pcm_drain (pcm);
snd_pcm_close (pcm);
snd_pcm_drain(pcm);
snd_pcm_close(pcm);
pcm = NULL;
}
if (sound_buffer)
{
free (sound_buffer);
free(sound_buffer);
sound_buffer = NULL;
}
}
void
S9xAlsaSoundDriver::start ()
void S9xAlsaSoundDriver::start()
{
}
void
S9xAlsaSoundDriver::stop ()
void S9xAlsaSoundDriver::stop()
{
}
@ -71,148 +66,152 @@ bool S9xAlsaSoundDriver::open_device()
unsigned int min = 0;
unsigned int max = 0;
printf ("ALSA sound driver initializing...\n");
printf (" --> (Device: default)...\n");
printf("ALSA sound driver initializing...\n");
printf(" --> (Device: default)...\n");
err = snd_pcm_open (&pcm,
"default",
SND_PCM_STREAM_PLAYBACK,
SND_PCM_NONBLOCK);
err = snd_pcm_open(&pcm,
"default",
SND_PCM_STREAM_PLAYBACK,
SND_PCM_NONBLOCK);
if (err < 0)
{
goto fail;
}
printf (" --> (%s, %s, %dhz, %d ms)...\n",
Settings.SixteenBitSound ? "16-bit" : "8-bit",
Settings.Stereo ? "Stereo" : "Mono",
Settings.SoundPlaybackRate,
gui_config->sound_buffer_size);
printf(" --> (16-bit Stereo, %dhz, %d ms)...\n",
Settings.SoundPlaybackRate,
gui_config->sound_buffer_size);
snd_pcm_hw_params_alloca (&hw_params);
snd_pcm_hw_params_any (pcm, hw_params);
snd_pcm_hw_params_set_format (pcm, hw_params, Settings.SixteenBitSound ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U8);
snd_pcm_hw_params_set_access (pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_rate_resample (pcm, hw_params, 0);
snd_pcm_hw_params_set_channels (pcm, hw_params, Settings.Stereo ? 2 : 1);
snd_pcm_hw_params_alloca(&hw_params);
snd_pcm_hw_params_any(pcm, hw_params);
snd_pcm_hw_params_set_format(pcm, hw_params, SND_PCM_FORMAT_S16);
snd_pcm_hw_params_set_access(pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_rate_resample(pcm, hw_params, 0);
snd_pcm_hw_params_set_channels(pcm, hw_params, 2);
snd_pcm_hw_params_get_rate_min (hw_params, &min, NULL);
snd_pcm_hw_params_get_rate_max (hw_params, &max, NULL);
printf (" --> Available rates: %d to %d\n", min, max);
snd_pcm_hw_params_get_rate_min(hw_params, &min, NULL);
snd_pcm_hw_params_get_rate_max(hw_params, &max, NULL);
printf(" --> Available rates: %d to %d\n", min, max);
if (Settings.SoundPlaybackRate > max && Settings.SoundPlaybackRate < min)
{
printf (" Rate %d not available. Using %d instead.\n", Settings.SoundPlaybackRate, max);
printf(" Rate %d not available. Using %d instead.\n", Settings.SoundPlaybackRate, max);
Settings.SoundPlaybackRate = max;
}
snd_pcm_hw_params_set_rate_near (pcm, hw_params, &Settings.SoundPlaybackRate, NULL);
snd_pcm_hw_params_set_rate_near(pcm, hw_params, &Settings.SoundPlaybackRate, NULL);
snd_pcm_hw_params_get_buffer_time_min (hw_params, &min, NULL);
snd_pcm_hw_params_get_buffer_time_max (hw_params, &max, NULL);
printf (" --> Available buffer sizes: %dms to %dms\n", min / 1000, max / 1000);
snd_pcm_hw_params_get_buffer_time_min(hw_params, &min, NULL);
snd_pcm_hw_params_get_buffer_time_max(hw_params, &max, NULL);
printf(" --> Available buffer sizes: %dms to %dms\n", min / 1000, max / 1000);
if (buffer_size < min && buffer_size > max)
{
printf (" Buffer size %dms not available. Using %d instead.\n", buffer_size / 1000, (min + max) / 2000);
printf(" Buffer size %dms not available. Using %d instead.\n", buffer_size / 1000, (min + max) / 2000);
buffer_size = (min + max) / 2;
}
snd_pcm_hw_params_set_buffer_time_near (pcm, hw_params, &buffer_size, NULL);
snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_size, NULL);
snd_pcm_hw_params_get_periods_min (hw_params, &min, NULL);
snd_pcm_hw_params_get_periods_max (hw_params, &max, NULL);
printf (" --> Period ranges: %d to %d blocks\n", min, max);
snd_pcm_hw_params_get_periods_min(hw_params, &min, NULL);
snd_pcm_hw_params_get_periods_max(hw_params, &max, NULL);
printf(" --> Period ranges: %d to %d blocks\n", min, max);
if (periods > max)
{
periods = max;
}
snd_pcm_hw_params_set_periods_near (pcm, hw_params, &periods, NULL);
snd_pcm_hw_params_set_periods_near(pcm, hw_params, &periods, NULL);
if ((err = snd_pcm_hw_params (pcm, hw_params)) < 0)
if ((err = snd_pcm_hw_params(pcm, hw_params)) < 0)
{
printf (" Hardware parameter set failed.\n");
printf(" Hardware parameter set failed.\n");
goto close_fail;
}
snd_pcm_sw_params_alloca (&sw_params);
snd_pcm_sw_params_current (pcm, sw_params);
snd_pcm_get_params (pcm, &alsa_buffer_size, &alsa_period_size);
snd_pcm_sw_params_alloca(&sw_params);
snd_pcm_sw_params_current(pcm, sw_params);
snd_pcm_get_params(pcm, &alsa_buffer_size, &alsa_period_size);
/* Don't start until we're [nearly] full */
snd_pcm_sw_params_set_start_threshold (pcm, sw_params, (alsa_buffer_size / 2));
err = snd_pcm_sw_params (pcm, sw_params);
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 = snd_pcm_frames_to_bytes(pcm, alsa_buffer_size);
if (err < 0)
{
printf (" Software parameter set failed.\n");
printf(" Software parameter set failed.\n");
goto close_fail;
}
printf ("OK\n");
printf("OK\n");
S9xSetSamplesAvailableCallback (alsa_samples_available, this);
S9xSetSamplesAvailableCallback(alsa_samples_available, this);
return true;
close_fail:
snd_pcm_drain (pcm);
snd_pcm_close (pcm);
snd_pcm_drain(pcm);
snd_pcm_close(pcm);
pcm = NULL;
fail:
printf ("Failed: %s\n", snd_strerror (err));
printf("Failed: %s\n", snd_strerror(err));
return false;
}
void
S9xAlsaSoundDriver::samples_available ()
void S9xAlsaSoundDriver::samples_available()
{
snd_pcm_sframes_t frames_written, frames;
int bytes;
frames = snd_pcm_avail (pcm);
if (Settings.DynamicRateControl)
{
S9xUpdateDynamicRate (snd_pcm_frames_to_bytes (pcm, frames),
output_buffer_size);
}
frames = snd_pcm_avail(pcm);
if (frames < 0)
{
frames = snd_pcm_recover (pcm, frames, 1);
frames = snd_pcm_recover(pcm, frames, 1);
return;
}
S9xFinalizeSamples ();
if (Settings.DynamicRateControl)
{
S9xUpdateDynamicRate(snd_pcm_frames_to_bytes(pcm, frames),
output_buffer_size);
}
S9xFinalizeSamples();
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 < (S9xGetSampleCount () >> (Settings.Stereo ? 1 : 0)))
if (frames < snes_frames_available)
{
S9xClearSamples ();
S9xClearSamples();
return;
}
}
frames = MIN (frames, S9xGetSampleCount () >> (Settings.Stereo ? 1 : 0));
bytes = snd_pcm_frames_to_bytes (pcm, frames);
if (bytes <= 0)
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
{
return;
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);
if (bytes <= 0)
return;
if (sound_buffer_size < bytes || sound_buffer == NULL)
{
sound_buffer = (uint8 *) realloc (sound_buffer, bytes);
sound_buffer = (uint8 *)realloc(sound_buffer, bytes);
sound_buffer_size = bytes;
}
S9xMixSamples (sound_buffer, frames << (Settings.Stereo ? 1 : 0));
S9xMixSamples(sound_buffer, frames * 2);
frames_written = 0;
@ -220,14 +219,14 @@ S9xAlsaSoundDriver::samples_available ()
{
int result;
result = snd_pcm_writei (pcm,
sound_buffer +
snd_pcm_frames_to_bytes (pcm, frames_written),
frames - frames_written);
result = snd_pcm_writei(pcm,
sound_buffer +
snd_pcm_frames_to_bytes(pcm, frames_written),
frames - frames_written);
if (result < 0)
{
result = snd_pcm_recover (pcm, result, 1);
result = snd_pcm_recover(pcm, result, 1);
if (result < 0)
{

View File

@ -13,20 +13,20 @@
class S9xAlsaSoundDriver : public S9xSoundDriver
{
public:
S9xAlsaSoundDriver ();
void init ();
void terminate ();
bool open_device ();
void start ();
void stop ();
void samples_available ();
public:
S9xAlsaSoundDriver();
void init();
void terminate();
bool open_device();
void start();
void stop();
void samples_available();
private:
snd_pcm_t *pcm;
int sound_buffer_size;
uint8 *sound_buffer;
int output_buffer_size;
private:
snd_pcm_t *pcm;
int sound_buffer_size;
uint8 *sound_buffer;
int output_buffer_size;
};
#endif /* __GTK_SOUND_DRIVER_ALSA_H */

View File

@ -4,58 +4,53 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "gtk_s9x.h"
#include "gtk_sound_driver_oss.h"
#include "gtk_s9x.h"
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <sys/time.h>
static void
oss_samples_available (void *data)
static void oss_samples_available(void *data)
{
((S9xOSSSoundDriver *) data)->samples_available ();
((S9xOSSSoundDriver *)data)->samples_available();
}
S9xOSSSoundDriver::S9xOSSSoundDriver ()
S9xOSSSoundDriver::S9xOSSSoundDriver()
{
filedes = -1;
sound_buffer = NULL;
sound_buffer_size = 0;
}
void
S9xOSSSoundDriver::init ()
void S9xOSSSoundDriver::init()
{
}
void
S9xOSSSoundDriver::terminate ()
void S9xOSSSoundDriver::terminate()
{
stop ();
stop();
S9xSetSamplesAvailableCallback (NULL, NULL);
S9xSetSamplesAvailableCallback(NULL, NULL);
if (filedes >= 0)
{
close (filedes);
close(filedes);
}
if (sound_buffer)
{
free (sound_buffer);
free(sound_buffer);
sound_buffer = NULL;
}
}
void
S9xOSSSoundDriver::start ()
void S9xOSSSoundDriver::start()
{
}
void
S9xOSSSoundDriver::stop ()
void S9xOSSSoundDriver::stop()
{
}
@ -66,174 +61,160 @@ bool S9xOSSSoundDriver::open_device()
output_buffer_size = (gui_config->sound_buffer_size * Settings.SoundPlaybackRate) / 1000;
if (Settings.Stereo)
output_buffer_size *= 2;
if (Settings.SixteenBitSound)
output_buffer_size *= 2;
output_buffer_size *= 4;
if (output_buffer_size < 256)
output_buffer_size = 256;
printf ("OSS sound driver initializing...\n");
printf("OSS sound driver initializing...\n");
printf ("Device: /dev/dsp: ");
printf("Device: /dev/dsp: ");
filedes = open ("/dev/dsp", O_WRONLY | O_NONBLOCK);
filedes = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
if (filedes < 0)
{
printf ("Failed.\n");
printf("Failed.\n");
char dspstring[16] = "/dev/dspX\0";
for (int i = 1; i <= 9; i++)
{
dspstring[8] = '0' + i;
printf ("Trying %s: ", dspstring);
printf("Trying %s: ", dspstring);
filedes = open (dspstring, O_WRONLY | O_NONBLOCK);
filedes = open(dspstring, O_WRONLY | O_NONBLOCK);
if (filedes < 0)
{
if (i == 9)
goto fail;
printf ("Failed.\n");
printf("Failed.\n");
}
else
break;
}
}
printf ("OK\n");
printf("OK\n");
printf(" --> (Format: 16-bit)...");
if (Settings.SixteenBitSound)
{
printf (" --> (Format: 16-bit)...");
temp = AFMT_S16_LE;
if (ioctl (filedes, SNDCTL_DSP_SETFMT, &temp) < 0)
goto close_fail;
}
else
{
printf (" --> (Format: 8-bit)...");
temp = AFMT_U8;
if (ioctl (filedes, SNDCTL_DSP_SETFMT, &temp) < 0)
goto close_fail;
}
printf ("OK\n");
if (Settings.Stereo)
{
temp = 2;
printf (" --> (Stereo)...");
}
else
{
temp = 1;
printf (" --> (Mono)...");
}
if (ioctl (filedes, SNDCTL_DSP_CHANNELS, &temp) < 0)
temp = AFMT_S16_LE;
if (ioctl(filedes, SNDCTL_DSP_SETFMT, &temp) < 0)
goto close_fail;
printf ("OK\n");
printf("OK\n");
printf (" --> (Frequency: %d)...", Settings.SoundPlaybackRate);
if (ioctl (filedes, SNDCTL_DSP_SPEED, &Settings.SoundPlaybackRate) < 0)
temp = 2;
printf(" --> (Stereo)...");
if (ioctl(filedes, SNDCTL_DSP_CHANNELS, &temp) < 0)
goto close_fail;
printf ("OK\n");
printf("OK\n");
printf(" --> (Frequency: %d)...", Settings.SoundPlaybackRate);
if (ioctl(filedes, SNDCTL_DSP_SPEED, &Settings.SoundPlaybackRate) < 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 / 4));
if (ioctl (filedes, SNDCTL_DSP_SETFRAGMENT, &temp) < 0)
if (ioctl(filedes, SNDCTL_DSP_SETFRAGMENT, &temp) < 0)
goto close_fail;
ioctl (filedes, SNDCTL_DSP_GETOSPACE, &info);
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
output_buffer_size = info.fragsize * info.fragstotal;
printf (" --> (Buffer size: %d bytes, %dms latency)...",
output_buffer_size,
(((output_buffer_size * 1000) >> (Settings.Stereo ? 1 : 0))
>> (Settings.SixteenBitSound ? 1 : 0))
/ (Settings.SoundPlaybackRate));
printf(" --> (Buffer size: %d bytes, %dms latency)...",
output_buffer_size,
(output_buffer_size * 250) / Settings.SoundPlaybackRate);
printf ("OK\n");
printf("OK\n");
S9xSetSamplesAvailableCallback (oss_samples_available, this);
S9xSetSamplesAvailableCallback(oss_samples_available, this);
return true;
close_fail:
close (filedes);
close(filedes);
fail:
printf ("failed\n");
printf("failed\n");
return false;
}
void
S9xOSSSoundDriver::samples_available ()
void S9xOSSSoundDriver::samples_available()
{
audio_buf_info info;
int samples_to_write;
int bytes_to_write;
int bytes_written;
ioctl (filedes, SNDCTL_DSP_GETOSPACE, &info);
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
if (Settings.DynamicRateControl)
{
S9xUpdateDynamicRate (info.bytes, output_buffer_size);
S9xUpdateDynamicRate(info.bytes, output_buffer_size);
}
S9xFinalizeSamples ();
S9xFinalizeSamples();
samples_to_write = S9xGetSampleCount ();
samples_to_write = S9xGetSampleCount();
if (Settings.DynamicRateControl)
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 >> (Settings.SixteenBitSound ? 1 : 0)))
if (samples_to_write > (info.bytes >> 1))
{
S9xClearSamples ();
S9xClearSamples();
return;
}
}
samples_to_write = MIN (info.bytes >> (Settings.SixteenBitSound ? 1 : 0),
samples_to_write);
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;
if (sound_buffer_size < samples_to_write << (Settings.SixteenBitSound ? 1 : 0))
if (sound_buffer_size < samples_to_write * 2)
{
sound_buffer = (uint8 *) realloc (sound_buffer, samples_to_write << (Settings.SixteenBitSound ? 1 : 0));
sound_buffer_size = samples_to_write << (Settings.SixteenBitSound ? 1 : 0);
sound_buffer = (uint8 *)realloc(sound_buffer, samples_to_write * 2);
sound_buffer_size = samples_to_write * 2;
}
S9xMixSamples (sound_buffer, samples_to_write);
S9xMixSamples(sound_buffer, samples_to_write);
bytes_written = 0;
bytes_to_write = samples_to_write << (Settings.SixteenBitSound ? 1 : 0);
bytes_to_write = samples_to_write * 2;
while (bytes_to_write > bytes_written)
{
int result;
result = write (filedes,
((char *) sound_buffer) + bytes_written,
bytes_to_write - bytes_written);
result = write(filedes,
((char *)sound_buffer) + bytes_written,
bytes_to_write - bytes_written);
if (result < 0)
break;

View File

@ -12,22 +12,20 @@
class S9xOSSSoundDriver : public S9xSoundDriver
{
public:
S9xOSSSoundDriver ();
void init ();
void terminate ();
bool open_device ();
void start ();
void stop ();
void samples_available ();
public:
S9xOSSSoundDriver();
void init();
void terminate();
bool open_device();
void start();
void stop();
void samples_available();
private:
int filedes;
uint8 *sound_buffer;
int sound_buffer_size;
int output_buffer_size;
private:
int filedes;
uint8 *sound_buffer;
int sound_buffer_size;
int output_buffer_size;
};
#endif /* __GTK_SOUND_DRIVER_OSS_H */

View File

@ -4,19 +4,17 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "gtk_s9x.h"
#include "gtk_sound_driver_portaudio.h"
#include "gtk_s9x.h"
static inline int
frames_to_bytes (int frames)
static inline int frames_to_bytes(int frames)
{
return (frames * (Settings.SixteenBitSound ? 2 : 1) * (Settings.Stereo ? 2 : 1));
return frames * 4;
}
static void
port_audio_samples_available_callback (void *data)
static void port_audio_samples_available_callback(void *data)
{
((S9xPortAudioSoundDriver *) data)->samples_available ();
((S9xPortAudioSoundDriver *)data)->samples_available();
}
S9xPortAudioSoundDriver::S9xPortAudioSoundDriver()
@ -26,108 +24,104 @@ S9xPortAudioSoundDriver::S9xPortAudioSoundDriver()
sound_buffer_size = 0;
}
void
S9xPortAudioSoundDriver::init ()
void S9xPortAudioSoundDriver::init()
{
PaError err;
err = Pa_Initialize ();
err = Pa_Initialize();
if (err != paNoError)
fprintf (stderr,
"Couldn't initialize PortAudio: %s\n",
Pa_GetErrorText (err));
fprintf(stderr,
"Couldn't initialize PortAudio: %s\n",
Pa_GetErrorText(err));
}
void
S9xPortAudioSoundDriver::terminate ()
void S9xPortAudioSoundDriver::terminate()
{
stop ();
stop();
S9xSetSamplesAvailableCallback (NULL, NULL);
S9xSetSamplesAvailableCallback(NULL, NULL);
Pa_Terminate ();
Pa_Terminate();
if (sound_buffer)
{
free (sound_buffer);
free(sound_buffer);
sound_buffer = NULL;
}
}
void
S9xPortAudioSoundDriver::start ()
void S9xPortAudioSoundDriver::start()
{
PaError err;
if (audio_stream != NULL && !(gui_config->mute_sound))
{
if ((Pa_IsStreamActive (audio_stream)))
if ((Pa_IsStreamActive(audio_stream)))
return;
err = Pa_StartStream (audio_stream);
err = Pa_StartStream(audio_stream);
if (err != paNoError)
{
fprintf (stderr, "Error: %s\n", Pa_GetErrorText (err));
fprintf(stderr, "Error: %s\n", Pa_GetErrorText(err));
}
}
}
void
S9xPortAudioSoundDriver::stop ()
void S9xPortAudioSoundDriver::stop()
{
if (audio_stream != NULL)
{
Pa_StopStream (audio_stream);
Pa_StopStream(audio_stream);
}
}
bool S9xPortAudioSoundDriver::open_device()
{
PaStreamParameters param;
const PaDeviceInfo *device_info;
PaStreamParameters param;
const PaDeviceInfo *device_info;
const PaHostApiInfo *hostapi_info;
PaError err = paNoError;
PaError err = paNoError;
if (audio_stream != NULL)
{
printf ("Shutting down sound for reset\n");
err = Pa_CloseStream (audio_stream);
printf("Shutting down sound for reset\n");
err = Pa_CloseStream(audio_stream);
if (err != paNoError)
{
fprintf (stderr,
"Couldn't reset audio stream.\nError: %s\n",
Pa_GetErrorText (err));
fprintf(stderr,
"Couldn't reset audio stream.\nError: %s\n",
Pa_GetErrorText(err));
return true;
}
audio_stream = NULL;
}
param.channelCount = Settings.Stereo ? 2 : 1;
param.sampleFormat = Settings.SixteenBitSound ? paInt16 : paUInt8;
param.channelCount = 2;
param.sampleFormat = paInt16;
param.hostApiSpecificStreamInfo = NULL;
printf ("PortAudio sound driver initializing...\n");
printf("PortAudio sound driver initializing...\n");
for (int i = 0; i < Pa_GetHostApiCount (); i++)
for (int i = 0; i < Pa_GetHostApiCount(); i++)
{
printf (" --> ");
printf(" --> ");
hostapi_info = Pa_GetHostApiInfo (i);
hostapi_info = Pa_GetHostApiInfo(i);
if (!hostapi_info)
{
printf ("Host API #%d has no info\n", i);
printf("Host API #%d has no info\n", i);
err = paNotInitialized;
continue;
}
device_info = Pa_GetDeviceInfo (hostapi_info->defaultOutputDevice);
device_info = Pa_GetDeviceInfo(hostapi_info->defaultOutputDevice);
if (!device_info)
{
printf ("(%s)...No device info available.\n", hostapi_info->name);
printf("(%s)...No device info available.\n", hostapi_info->name);
err = paNotInitialized;
continue;
}
@ -135,88 +129,99 @@ bool S9xPortAudioSoundDriver::open_device()
param.device = hostapi_info->defaultOutputDevice;
param.suggestedLatency = gui_config->sound_buffer_size * 0.001;
printf ("(%s : %s, latency %dms)...",
hostapi_info->name,
device_info->name,
(int) (param.suggestedLatency * 1000.0));
printf("(%s : %s, latency %dms)...",
hostapi_info->name,
device_info->name,
(int)(param.suggestedLatency * 1000.0));
fflush (stdout);
fflush(stdout);
err = Pa_OpenStream (&audio_stream,
NULL,
&param,
Settings.SoundPlaybackRate,
0,
paNoFlag,
NULL,
NULL);
err = Pa_OpenStream(&audio_stream,
NULL,
&param,
Settings.SoundPlaybackRate,
0,
paNoFlag,
NULL,
NULL);
int frames = Pa_GetStreamWriteAvailable (audio_stream);
output_buffer_size = frames_to_bytes (frames);
int frames = Pa_GetStreamWriteAvailable(audio_stream);
output_buffer_size = frames_to_bytes(frames);
if (err == paNoError)
{
printf ("OK\n");
printf("OK\n");
break;
}
else
{
printf ("Failed (%s)\n",
Pa_GetErrorText (err));
printf("Failed (%s)\n",
Pa_GetErrorText(err));
}
}
if (err != paNoError || Pa_GetHostApiCount () < 1)
if (err != paNoError || Pa_GetHostApiCount() < 1)
{
fprintf (stderr,
"Couldn't initialize sound\n");
fprintf(stderr,
"Couldn't initialize sound\n");
return false;
}
S9xSetSamplesAvailableCallback (port_audio_samples_available_callback, this);
S9xSetSamplesAvailableCallback(port_audio_samples_available_callback, this);
fflush (stdout);
fflush (stderr);
fflush(stdout);
fflush(stderr);
return true;
}
void
S9xPortAudioSoundDriver::samples_available ()
void S9xPortAudioSoundDriver::samples_available()
{
int frames;
int bytes;
frames = Pa_GetStreamWriteAvailable (audio_stream);
frames = Pa_GetStreamWriteAvailable(audio_stream);
if (Settings.DynamicRateControl)
{
S9xUpdateDynamicRate (frames_to_bytes (frames), output_buffer_size);
S9xUpdateDynamicRate(frames_to_bytes(frames), output_buffer_size);
}
S9xFinalizeSamples ();
S9xFinalizeSamples();
int snes_frames_available = S9xGetSampleCount() >> 1;
if (Settings.DynamicRateControl)
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 < (S9xGetSampleCount () >> (Settings.Stereo ? 1 : 0)))
if (frames < (S9xGetSampleCount() >> 1))
{
S9xClearSamples ();
S9xClearSamples();
return;
}
}
frames = MIN (frames, S9xGetSampleCount () >> (Settings.Stereo ? 1 : 0));
bytes = frames_to_bytes (frames);
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
{
while (frames < snes_frames_available)
{
int usec_to_sleep = (snes_frames_available - frames) * 10000 /
(Settings.SoundPlaybackRate / 100);
usleep(usec_to_sleep > 0 ? usec_to_sleep : 0);
frames = Pa_GetStreamWriteAvailable(audio_stream);
}
}
frames = MIN(frames, snes_frames_available);
bytes = frames_to_bytes(frames);
if (sound_buffer_size < bytes || sound_buffer == NULL)
{
sound_buffer = (uint8 *) realloc (sound_buffer, bytes);
sound_buffer = (uint8 *)realloc(sound_buffer, bytes);
sound_buffer_size = bytes;
}
S9xMixSamples (sound_buffer, frames << (Settings.Stereo ? 1 : 0));
S9xMixSamples(sound_buffer, frames << 1);
Pa_WriteStream (audio_stream, sound_buffer, frames);
Pa_WriteStream(audio_stream, sound_buffer, frames);
}

View File

@ -7,29 +7,28 @@
#ifndef __GTK_SOUND_DRIVER_PORTAUDIO_H
#define __GTK_SOUND_DRIVER_PORTAUDIO_H
#include <portaudio.h>
#include <errno.h>
#include <portaudio.h>
#include "gtk_sound.h"
#include "gtk_sound_driver.h"
class S9xPortAudioSoundDriver : public S9xSoundDriver
{
public:
S9xPortAudioSoundDriver ();
void init ();
void terminate ();
bool open_device ();
void start ();
void stop ();
void samples_available ();
public:
S9xPortAudioSoundDriver();
void init();
void terminate();
bool open_device();
void start();
void stop();
void samples_available();
private:
PaStream *audio_stream;
int sound_buffer_size;
uint8 *sound_buffer;
int output_buffer_size;
private:
PaStream *audio_stream;
int sound_buffer_size;
uint8 *sound_buffer;
int output_buffer_size;
};
#endif /* __GTK_SOUND_DRIVER_PORTAUDIO_H */

View File

@ -4,20 +4,19 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "gtk_s9x.h"
#include "gtk_sound_driver_pulse.h"
#include "gtk_s9x.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <fcntl.h>
static void
pulse_samples_available (void *data)
static void pulse_samples_available(void *data)
{
((S9xPulseSoundDriver *) data)->samples_available ();
((S9xPulseSoundDriver *)data)->samples_available();
}
S9xPulseSoundDriver::S9xPulseSoundDriver ()
S9xPulseSoundDriver::S9xPulseSoundDriver()
{
mainloop = NULL;
context = NULL;
@ -25,94 +24,85 @@ S9xPulseSoundDriver::S9xPulseSoundDriver ()
buffer_size = 0;
}
void
S9xPulseSoundDriver::init ()
void S9xPulseSoundDriver::init()
{
}
void
S9xPulseSoundDriver::terminate ()
void S9xPulseSoundDriver::terminate()
{
S9xSetSamplesAvailableCallback (NULL, NULL);
S9xSetSamplesAvailableCallback(NULL, NULL);
if (mainloop)
pa_threaded_mainloop_stop (mainloop);
pa_threaded_mainloop_stop(mainloop);
if (stream)
{
pa_stream_disconnect (stream);
pa_stream_unref (stream);
pa_stream_disconnect(stream);
pa_stream_unref(stream);
}
if (context)
{
pa_context_disconnect (context);
pa_context_unref (context);
pa_context_disconnect(context);
pa_context_unref(context);
}
if (mainloop)
{
pa_threaded_mainloop_free (mainloop);
pa_threaded_mainloop_free(mainloop);
}
}
void
S9xPulseSoundDriver::start ()
void S9xPulseSoundDriver::start()
{
}
void
S9xPulseSoundDriver::stop ()
void S9xPulseSoundDriver::stop()
{
}
void
S9xPulseSoundDriver::lock ()
void S9xPulseSoundDriver::lock()
{
pa_threaded_mainloop_lock (mainloop);
pa_threaded_mainloop_lock(mainloop);
}
void
S9xPulseSoundDriver::unlock ()
void S9xPulseSoundDriver::unlock()
{
pa_threaded_mainloop_unlock (mainloop);
pa_threaded_mainloop_unlock(mainloop);
}
void
S9xPulseSoundDriver::wait ()
void S9xPulseSoundDriver::wait()
{
pa_threaded_mainloop_wait (mainloop);
pa_threaded_mainloop_wait(mainloop);
}
static void
context_state_cb (pa_context *c, void *userdata)
static void context_state_cb(pa_context *c, void *userdata)
{
S9xPulseSoundDriver *driver = (S9xPulseSoundDriver *) userdata;
S9xPulseSoundDriver *driver = (S9xPulseSoundDriver *)userdata;
int state;
state = pa_context_get_state (c);
state = pa_context_get_state(c);
if (state == PA_CONTEXT_READY ||
state == PA_CONTEXT_FAILED ||
if (state == PA_CONTEXT_READY ||
state == PA_CONTEXT_FAILED ||
state == PA_CONTEXT_TERMINATED)
{
pa_threaded_mainloop_signal (driver->mainloop, 0);
pa_threaded_mainloop_signal(driver->mainloop, 0);
}
}
static void
stream_state_callback (pa_stream *p, void *userdata)
static void stream_state_callback(pa_stream *p, void *userdata)
{
S9xPulseSoundDriver *driver = (S9xPulseSoundDriver *) userdata;
S9xPulseSoundDriver *driver = (S9xPulseSoundDriver *)userdata;
int state;
state = pa_stream_get_state (p);
state = pa_stream_get_state(p);
if (state == PA_STREAM_READY ||
state == PA_STREAM_FAILED ||
if (state == PA_STREAM_READY ||
state == PA_STREAM_FAILED ||
state == PA_STREAM_TERMINATED)
{
pa_threaded_mainloop_signal (driver->mainloop, 0);
pa_threaded_mainloop_signal(driver->mainloop, 0);
}
}
@ -123,154 +113,165 @@ bool S9xPulseSoundDriver::open_device()
pa_buffer_attr buffer_attr;
const pa_buffer_attr *actual_buffer_attr;
ss.channels = Settings.Stereo ? 2 : 1;
ss.format = Settings.SixteenBitSound ? PA_SAMPLE_S16NE : PA_SAMPLE_U8;
ss.rate = Settings.SoundPlaybackRate;
ss.channels = 2;
ss.format = PA_SAMPLE_S16NE;
ss.rate = Settings.SoundPlaybackRate;
buffer_attr.fragsize = -1;
buffer_attr.tlength = pa_usec_to_bytes (gui_config->sound_buffer_size * 1000, &ss);
buffer_attr.maxlength = -1;
buffer_attr.minreq = -1;
buffer_attr.prebuf = -1;
buffer_attr.fragsize = -1;
buffer_attr.tlength = pa_usec_to_bytes(gui_config->sound_buffer_size * 1000, &ss);
buffer_attr.maxlength = buffer_attr.tlength * 2;
buffer_attr.minreq = -1;
buffer_attr.prebuf = -1;
printf ("PulseAudio sound driver initializing...\n");
printf("PulseAudio sound driver initializing...\n");
printf (" --> (%dhz, %s %s, %dms)...",
Settings.SoundPlaybackRate,
Settings.SixteenBitSound ? "16-bit" : "8-bit",
Settings.Stereo ? "Stereo" : "Mono",
gui_config->sound_buffer_size);
fflush (stdout);
printf(" --> (%dhz, 16-bit Stereo, %dms)...",
Settings.SoundPlaybackRate,
gui_config->sound_buffer_size);
fflush(stdout);
mainloop = pa_threaded_mainloop_new ();
mainloop = pa_threaded_mainloop_new();
if (!mainloop)
{
fprintf (stderr, "Failed to create mainloop.\n");
fprintf(stderr, "Failed to create mainloop.\n");
goto error0;
}
context = pa_context_new (pa_threaded_mainloop_get_api (mainloop), "Snes9x");
context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Snes9x");
if (!context)
goto error1;
pa_context_set_state_callback (context, context_state_cb, this);
if ((err = pa_context_connect (context, NULL, PA_CONTEXT_NOFLAGS, NULL)) != 0)
pa_context_set_state_callback(context, context_state_cb, this);
if ((err = pa_context_connect(context, NULL, PA_CONTEXT_NOFLAGS, NULL)) != 0)
goto error2;
lock ();
lock();
if ((err = pa_threaded_mainloop_start (mainloop)) != 0)
if ((err = pa_threaded_mainloop_start(mainloop)) != 0)
goto error2;
wait ();
wait();
if ((err = pa_context_get_state (context)) != PA_CONTEXT_READY)
if ((err = pa_context_get_state(context)) != PA_CONTEXT_READY)
{
printf ("Coundn't create context: State: %d\n", err);
printf("Coundn't create context: State: %d\n", err);
goto error2;
}
stream = pa_stream_new (context, "Game", &ss, NULL);
stream = pa_stream_new(context, "Game", &ss, NULL);
if (!stream)
goto error2;
pa_stream_set_state_callback (stream, stream_state_callback, this);
pa_stream_set_state_callback(stream, stream_state_callback, this);
if (pa_stream_connect_playback (stream,
NULL,
&buffer_attr,
PA_STREAM_ADJUST_LATENCY,
NULL,
NULL) < 0)
if (pa_stream_connect_playback(stream,
NULL,
&buffer_attr,
PA_STREAM_ADJUST_LATENCY,
NULL,
NULL) < 0)
goto error3;
wait ();
wait();
if (pa_stream_get_state (stream) != PA_STREAM_READY)
if (pa_stream_get_state(stream) != PA_STREAM_READY)
{
goto error3;
}
actual_buffer_attr = pa_stream_get_buffer_attr (stream);
actual_buffer_attr = pa_stream_get_buffer_attr(stream);
buffer_size = actual_buffer_attr->tlength;
printf ("OK\n");
printf("OK\n");
S9xSetSamplesAvailableCallback (pulse_samples_available, this);
S9xSetSamplesAvailableCallback(pulse_samples_available, this);
unlock ();
unlock();
return true;
error3:
pa_stream_disconnect (stream);
pa_stream_unref (stream);
pa_stream_disconnect(stream);
pa_stream_unref(stream);
error2:
pa_context_disconnect (context);
pa_context_unref (context);
unlock ();
pa_context_disconnect(context);
pa_context_unref(context);
unlock();
error1:
pa_threaded_mainloop_free (mainloop);
pa_threaded_mainloop_free(mainloop);
error0:
printf ("Failed: %s\n", pa_strerror (err));
printf("Failed: %s\n", pa_strerror(err));
return false;
}
void
S9xPulseSoundDriver::samples_available ()
void S9xPulseSoundDriver::samples_available()
{
size_t bytes;
int samples;
const pa_buffer_attr *buffer_attr;
void *output_buffer = NULL;
lock ();
bytes = pa_stream_writable_size (stream);
buffer_attr = pa_stream_get_buffer_attr (stream);
unlock ();
lock();
bytes = pa_stream_writable_size(stream);
buffer_attr = pa_stream_get_buffer_attr(stream);
unlock();
buffer_size = buffer_attr->tlength;
if (Settings.DynamicRateControl)
{
S9xUpdateDynamicRate (bytes, buffer_size);
S9xUpdateDynamicRate(bytes, buffer_size);
}
S9xFinalizeSamples ();
S9xFinalizeSamples();
samples = S9xGetSampleCount ();
samples = S9xGetSampleCount();
if (Settings.DynamicRateControl)
if (Settings.DynamicRateControl && !Settings.SoundSync)
{
if ((int) bytes < (samples << (Settings.SixteenBitSound ? 1 : 0)))
if ((int)bytes < (samples * 2))
{
S9xClearSamples ();
S9xClearSamples();
return;
}
}
bytes = MIN ((int) bytes, (samples << (Settings.SixteenBitSound ? 1 : 0)));
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
{
while ((int)bytes < samples * 2)
{
int usec_to_sleep = ((samples >> 1) - (bytes >> 2)) * 10000 /
(Settings.SoundPlaybackRate / 100);
usleep(usec_to_sleep > 0 ? usec_to_sleep : 0);
lock();
bytes = pa_stream_writable_size(stream);
unlock();
}
}
bytes = MIN((int)bytes, samples * 2) & ~1;
if (!bytes)
return;
lock ();
lock();
if (pa_stream_begin_write (stream, &output_buffer, &bytes) != 0)
if (pa_stream_begin_write(stream, &output_buffer, &bytes) != 0)
{
pa_stream_flush (stream, NULL, NULL);
unlock ();
pa_stream_flush(stream, NULL, NULL);
unlock();
return;
}
if (bytes <= 0 || !output_buffer)
{
unlock ();
unlock();
return;
}
S9xMixSamples ((uint8 *) output_buffer, bytes >> (Settings.SixteenBitSound ? 1 : 0));
pa_stream_write (stream, output_buffer, bytes, NULL, 0, PA_SEEK_RELATIVE);
S9xMixSamples((uint8 *)output_buffer, bytes >> 1);
pa_stream_write(stream, output_buffer, bytes, NULL, 0, PA_SEEK_RELATIVE);
unlock ();
unlock();
}

View File

@ -13,25 +13,24 @@
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 ();
public:
S9xPulseSoundDriver();
void init();
void terminate();
bool open_device();
void start();
void stop();
void samples_available();
void lock();
void unlock();
void wait();
pa_threaded_mainloop *mainloop;
pa_context *context;
pa_stream *stream;
private:
int buffer_size;
pa_threaded_mainloop *mainloop;
pa_context *context;
pa_stream *stream;
private:
int buffer_size;
};
#endif /* __GTK_SOUND_DRIVER_PULSE_H */

View File

@ -4,108 +4,131 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "gtk_s9x.h"
#include "gtk_sound_driver_sdl.h"
#include "gtk_s9x.h"
static void
sdl_audio_callback (void *userdata, Uint8 *stream, int len)
static void sdl_audio_callback(void *userdata, Uint8 *stream, int len)
{
((S9xSDLSoundDriver *) userdata)->mix ((unsigned char *) stream, len);
((S9xSDLSoundDriver *)userdata)->mix((unsigned char *)stream, len);
}
static void
samples_available (void *data)
static void c_samples_available(void *data)
{
SDL_LockAudio ();
S9xFinalizeSamples ();
SDL_UnlockAudio ();
((S9xSDLSoundDriver *)data)->samples_available();
}
void
S9xSDLSoundDriver::mix (unsigned char *output, int bytes)
void S9xSDLSoundDriver::samples_available()
{
SDL_LockAudio ();
S9xMixSamples (output, bytes >> (Settings.SixteenBitSound ? 1 : 0));
SDL_UnlockAudio ();
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);
mutex.unlock();
}
S9xSDLSoundDriver::S9xSDLSoundDriver ()
void S9xSDLSoundDriver::mix(unsigned char *output, int bytes)
{
mutex.lock();
if (buffer->avail() >= bytes >> 1)
buffer->read((int16_t *)output, bytes >> 1);
mutex.unlock();
}
S9xSDLSoundDriver::S9xSDLSoundDriver()
{
buffer = NULL;
audiospec = NULL;
}
void
S9xSDLSoundDriver::init ()
void S9xSDLSoundDriver::init()
{
SDL_InitSubSystem (SDL_INIT_AUDIO);
stop ();
SDL_InitSubSystem(SDL_INIT_AUDIO);
stop();
}
void
S9xSDLSoundDriver::terminate ()
void S9xSDLSoundDriver::terminate()
{
stop ();
stop();
if (audiospec)
{
SDL_CloseAudio ();
free (audiospec);
SDL_CloseAudio();
if (buffer)
delete buffer;
free(audiospec);
audiospec = NULL;
}
SDL_QuitSubSystem (SDL_INIT_AUDIO);
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
void
S9xSDLSoundDriver::start ()
void S9xSDLSoundDriver::start()
{
if (!gui_config->mute_sound)
{
if (audiospec)
{
SDL_PauseAudio (0);
SDL_PauseAudio(0);
}
}
}
void
S9xSDLSoundDriver::stop ()
void S9xSDLSoundDriver::stop()
{
if (audiospec)
{
SDL_PauseAudio (1);
SDL_PauseAudio(1);
}
}
bool S9xSDLSoundDriver::open_device()
{
audiospec = (SDL_AudioSpec *) malloc (sizeof (SDL_AudioSpec));
audiospec = (SDL_AudioSpec *)malloc(sizeof(SDL_AudioSpec));
audiospec->freq = Settings.SoundPlaybackRate;
audiospec->channels = Settings.Stereo ? 2 : 1;
audiospec->format = Settings.SixteenBitSound ? AUDIO_S16SYS : AUDIO_U8;
audiospec->samples = (gui_config->sound_buffer_size * audiospec->freq / 1000) >> 1;
audiospec->channels = 2;
audiospec->format = AUDIO_S16SYS;
audiospec->samples = (gui_config->sound_buffer_size * audiospec->freq / 1000) >> 2;
audiospec->callback = sdl_audio_callback;
audiospec->userdata = this;
printf ("SDL sound driver initializing...\n");
printf (" --> (Frequency: %dhz, Latency: %dms)...",
audiospec->freq,
(audiospec->samples * 1000 / audiospec->freq) << 1);
printf("SDL sound driver initializing...\n");
printf(" --> (Frequency: %dhz, Latency: %dms)...",
audiospec->freq,
(audiospec->samples * 1000 / audiospec->freq));
if (SDL_OpenAudio (audiospec, NULL) < 0)
if (SDL_OpenAudio(audiospec, NULL) < 0)
{
printf ("Failed\n");
printf("Failed\n");
free (audiospec);
free(audiospec);
audiospec = NULL;
return false;
}
printf ("OK\n");
printf("OK\n");
S9xSetSamplesAvailableCallback (samples_available, NULL);
buffer = new Resampler(gui_config->sound_buffer_size * audiospec->freq / 500);
buffer->time_ratio(1.0);
S9xSetSamplesAvailableCallback(c_samples_available, this);
return true;
}

View File

@ -11,21 +11,28 @@
#include "gtk_sound.h"
#include "gtk_sound_driver.h"
#include "../../apu/resampler.h"
#include <mutex>
#include <cstdint>
class S9xSDLSoundDriver : public S9xSoundDriver
{
public:
S9xSDLSoundDriver ();
void init ();
void terminate ();
bool open_device ();
void start ();
void stop ();
void mix (unsigned char *output, int bytes);
public:
S9xSDLSoundDriver();
void init();
void terminate();
bool open_device();
void start();
void stop();
void mix(unsigned char *output, int bytes);
void samples_available();
private:
SDL_AudioSpec *audiospec;
private:
SDL_AudioSpec *audiospec;
Resampler *buffer;
std::mutex mutex;
int16_t temp[512];
};
#endif /* __GTK_SOUND_DRIVER_SDL_H */

View File

@ -4218,22 +4218,6 @@
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="stereo_check">
<property name="label" translatable="yes">Stereo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Output two channels, left and right</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkTable" id="table7">
<property name="visible">True</property>
@ -4451,7 +4435,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
<property name="position">5</property>
</packing>
</child>
</object>

View File

@ -8,6 +8,7 @@
#include "memmap.h"
#include "display.h"
#include "msu1.h"
#include "apu/resampler.h"
#include "apu/bapu/dsp/blargg_endian.h"
#include <fstream>
#include <sys/stat.h>
@ -18,7 +19,7 @@ uint32 audioLoopPos;
size_t partial_frames;
// Sample buffer
int16 *bufPos, *bufBegin, *bufEnd;
static Resampler *msu_resampler = NULL;
#ifdef UNZIP_SUPPORT
static int unzFindExtension(unzFile &file, const char *ext, bool restart = TRUE, bool print = TRUE, bool allowExact = FALSE)
@ -174,10 +175,8 @@ void S9xResetMSU(void)
MSU1.MSU1_AUDIO_POS = 0;
MSU1.MSU1_RESUME_POS = 0;
bufPos = 0;
bufBegin = 0;
bufEnd = 0;
if (msu_resampler)
msu_resampler->clear();
partial_frames = 0;
@ -231,7 +230,7 @@ void S9xMSU1Generate(size_t sample_count)
{
partial_frames += 4410 * (sample_count / 2);
while ((bufPos < (bufEnd - 2)) && partial_frames >= 3204)
while (partial_frames >= 3204)
{
if (MSU1.MSU1_STATUS & AudioPlaying && audioStream)
{
@ -245,9 +244,7 @@ void S9xMSU1Generate(size_t sample_count)
*left = ((int32)(int16)GET_LE16(left) * MSU1.MSU1_VOLUME / 255);
*right = ((int32)(int16)GET_LE16(right) * MSU1.MSU1_VOLUME / 255);
*(bufPos++) = *left;
*(bufPos++) = *right;
msu_resampler->push_sample(*left, *right);
MSU1.MSU1_AUDIO_POS += 4;
partial_frames -= 3204;
}
@ -274,8 +271,7 @@ void S9xMSU1Generate(size_t sample_count)
{
MSU1.MSU1_STATUS &= ~(AudioPlaying | AudioRepeating);
partial_frames -= 3204;
*(bufPos++) = 0;
*(bufPos++) = 0;
msu_resampler->push_sample(0, 0);
}
}
}
@ -392,13 +388,12 @@ void S9xMSU1WritePort(uint8 port, uint8 byte)
size_t S9xMSU1Samples(void)
{
return bufPos - bufBegin;
return msu_resampler->space_filled();
}
void S9xMSU1SetOutput(int16 * out, size_t size)
void S9xMSU1SetOutput(Resampler *resampler)
{
bufPos = bufBegin = out;
bufEnd = out + size;
msu_resampler = resampler;
}
void S9xMSU1PostLoadState(void)
@ -427,9 +422,8 @@ void S9xMSU1PostLoadState(void)
}
}
bufPos = 0;
bufBegin = 0;
bufEnd = 0;
if (msu_resampler)
msu_resampler->clear();
partial_frames = 0;
}

3
msu1.h
View File

@ -51,7 +51,8 @@ void S9xMSU1Generate(size_t sample_count);
uint8 S9xMSU1ReadPort(uint8 port);
void S9xMSU1WritePort(uint8 port, uint8 byte);
size_t S9xMSU1Samples(void);
void S9xMSU1SetOutput(int16 *out, size_t size);
class Resampler;
void S9xMSU1SetOutput(Resampler *resampler);
void S9xMSU1PostLoadState(void);
#endif

View File

@ -1,347 +0,0 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
// CDirectSound.cpp: implementation of the CDirectSound class.
//
//////////////////////////////////////////////////////////////////////
#include "wsnes9x.h"
#include "../snes9x.h"
#include "../apu/apu.h"
#include "CDirectSound.h"
#include <process.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CDirectSound::CDirectSound()
{
lpDS = NULL;
lpDSB = NULL;
lpDSBPrimary = NULL;
initDone = NULL;
blockCount = 0;
blockSize = 0;
bufferSize = 0;
blockSamples = 0;
hTimer = NULL;
}
CDirectSound::~CDirectSound()
{
DeInitDirectSound();
}
/* CDirectSound::InitDirectSound
initializes the DirectSound object, the timer queue for the mixing timer, and sets the cooperation level
-----
returns true if successful, false otherwise
*/
bool CDirectSound::InitDirectSound ()
{
HRESULT dErr;
if(initDone)
return true;
if (!lpDS)
{
dErr = DirectSoundCreate (NULL, &lpDS, NULL);
if (dErr != DS_OK)
{
MessageBox (GUI.hWnd, TEXT("\
Unable to initialise DirectSound. You will not be able to hear any\n\
sound effects or music while playing.\n\n\
It is usually caused by not having DirectX installed, another\n\
application that has already opened DirectSound in exclusive\n\
mode or the Windows WAVE device has been opened."),
TEXT("Snes9X - Unable to Open DirectSound"),
MB_OK | MB_ICONWARNING);
return (false);
}
}
initDone = true;
dErr = lpDS->SetCooperativeLevel (GUI.hWnd, DSSCL_PRIORITY | DSSCL_EXCLUSIVE);
if (!SUCCEEDED(dErr))
{
dErr = lpDS->SetCooperativeLevel (GUI.hWnd, DSSCL_PRIORITY);
if (!SUCCEEDED(dErr))
{
if (!SUCCEEDED(lpDS -> SetCooperativeLevel (GUI.hWnd, DSSCL_NORMAL)))
{
DeInitDirectSound();
initDone = false;
}
if (initDone)
MessageBox (GUI.hWnd, TEXT("\
Unable to set DirectSound's priority cooperative level.\n\
Another application is dicating the sound playback rate,\n\
sample size and mono/stereo setting."),
TEXT("Snes9X - Unable to Set DirectSound priority"),
MB_OK | MB_ICONWARNING);
else
MessageBox (GUI.hWnd, TEXT("\
Unable to set any DirectSound cooperative level. You will\n\
not be able to hear any sound effects or music while playing.\n\n\
It is usually caused by another application that has already\n\
opened DirectSound in exclusive mode."),
TEXT("Snes9X - Unable to DirectSound"),
MB_OK | MB_ICONWARNING);
}
}
return (initDone);
}
/* CDirectSound::DeInitDirectSound
releases all DirectSound objects and buffers
*/
void CDirectSound::DeInitDirectSound()
{
initDone = false;
DeInitSoundBuffer();
if( lpDS != NULL)
{
lpDS->SetCooperativeLevel (GUI.hWnd, DSSCL_NORMAL);
lpDS->Release ();
lpDS = NULL;
}
}
/* CDirectSound::InitSoundBuffer
creates the DirectSound buffers and allocates the temp buffer for SoundSync
-----
returns true if successful, false otherwise
*/
bool CDirectSound::InitSoundBuffer()
{
DSBUFFERDESC dsbd;
WAVEFORMATEX wfx,wfx_actual;
HRESULT dErr;
blockCount = 4;
blockTime = GUI.SoundBufferSize / blockCount;
blockSamples = (Settings.SoundPlaybackRate * blockTime) / 1000;
blockSamples *= (Settings.Stereo ? 2 : 1);
blockSize = blockSamples * (Settings.SixteenBitSound ? 2 : 1);
bufferSize = blockSize * blockCount;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = Settings.Stereo ? 2 : 1;
wfx.nSamplesPerSec = Settings.SoundPlaybackRate;
wfx.nBlockAlign = (Settings.SixteenBitSound ? 2 : 1) * (Settings.Stereo ? 2 : 1);
wfx.wBitsPerSample = Settings.SixteenBitSound ? 16 : 8;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
ZeroMemory (&dsbd, sizeof(DSBUFFERDESC) );
dsbd.dwSize = sizeof(dsbd);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_STICKYFOCUS;
dErr = lpDS->CreateSoundBuffer (&dsbd, &lpDSBPrimary, NULL);
if (dErr != DS_OK)
{
lpDSB = NULL;
return (false);
}
lpDSBPrimary->SetFormat (&wfx);
if (lpDSBPrimary->GetFormat (&wfx_actual, sizeof (wfx_actual), NULL) == DS_OK)
{
if(wfx.nSamplesPerSec!=wfx_actual.nSamplesPerSec ||
wfx_actual.nChannels != wfx.nChannels || wfx.wBitsPerSample != wfx_actual.wBitsPerSample) {
return false;
}
}
lpDSBPrimary->Play (0, 0, DSBPLAY_LOOPING);
ZeroMemory (&dsbd, sizeof (dsbd));
dsbd.dwSize = sizeof( dsbd);
dsbd.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLVOLUME |
DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_STICKYFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY;
dsbd.dwBufferBytes = bufferSize;
dsbd.lpwfxFormat = &wfx;
if (lpDS->CreateSoundBuffer (&dsbd, &lpDSB, NULL) != DS_OK)
{
lpDSBPrimary->Release ();
lpDSBPrimary = NULL;
lpDSB->Release();
lpDSB = NULL;
return (false);
}
return true;
}
/* CDirectSound::DeInitSoundBuffer
deinitializes the DirectSound/temp buffers and stops the mixing timer
*/
void CDirectSound::DeInitSoundBuffer()
{
if(hTimer) {
timeKillEvent(hTimer);
hTimer = NULL;
}
if( lpDSB != NULL)
{
lpDSB->Stop ();
lpDSB->Release();
lpDSB = NULL;
}
if( lpDSBPrimary != NULL)
{
lpDSBPrimary->Stop ();
lpDSBPrimary->Release();
lpDSBPrimary = NULL;
}
}
/* CDirectSound::SetupSound
applies sound setting changes by recreating the buffers and starting a new mixing timer
it fills the buffer before starting playback
-----
returns true if successful, false otherwise
*/
bool CDirectSound::SetupSound()
{
HRESULT hResult;
if(!initDone)
return false;
DeInitSoundBuffer();
InitSoundBuffer();
BYTE *B1;
DWORD S1;
hResult = lpDSB->Lock (0, 0, (void **)&B1,
&S1, NULL, NULL, DSBLOCK_ENTIREBUFFER);
if (hResult == DSERR_BUFFERLOST)
{
lpDSB->Restore ();
hResult = lpDSB->Lock (0, 0, (void **)&B1,
&S1, NULL, NULL, DSBLOCK_ENTIREBUFFER);
}
if (!SUCCEEDED(hResult))
{
hResult = lpDSB -> Unlock (B1, S1, NULL, NULL);
return true;
}
S9xMixSamples(B1,blockSamples * blockCount);
lpDSB->Unlock(B1,S1,NULL,NULL);
lpDSB->Play (0, 0, DSBPLAY_LOOPING);
last_block = blockCount - 1;
hTimer = timeSetEvent (blockTime/2, blockTime/2, SoundTimerCallback, (DWORD_PTR)this, TIME_PERIODIC);
if(!hTimer) {
DeInitSoundBuffer();
return false;
}
return (true);
}
void CDirectSound::SetVolume(double volume)
{
if (!initDone)
return;
// convert percentage to hundredths of dB
LONG dbVolume = (LONG)(10 * log10(volume) * 2 * 100);
lpDSB->SetVolume(dbVolume);
}
/* CDirectSound::ProcessSound
Finishes core sample creation, syncronizes the buffer access.
*/
void CDirectSound::ProcessSound()
{
EnterCriticalSection(&GUI.SoundCritSect);
S9xFinalizeSamples();
LeaveCriticalSection(&GUI.SoundCritSect);
}
/* CDirectSound::MixSound
the mixing function called by the mix timer
uses the current play position to decide if a new block can be filled with audio data
synchronizes the core buffer access with a critical section
*/
void CDirectSound::MixSound()
{
DWORD play_pos = 0, write_pos = 0;
HRESULT hResult;
DWORD curr_block;
if(!initDone)
return;
lpDSB->GetCurrentPosition (&play_pos, NULL);
curr_block = ((play_pos / blockSize) + blockCount) % blockCount;
if (curr_block != last_block)
{
BYTE *B1, *B2;
DWORD S1, S2;
write_pos = curr_block * blockSize;
last_block = curr_block;
hResult = lpDSB->Lock (write_pos, blockSize, (void **)&B1,
&S1, (void **)&B2, &S2, 0);
if (hResult == DSERR_BUFFERLOST)
{
lpDSB->Restore ();
hResult = lpDSB->Lock (write_pos, blockSize,
(void **)&B1, &S1, (void **)&B2,
&S2, 0);
}
if (!SUCCEEDED(hResult))
{
hResult = lpDSB -> Unlock (B1, S1, B2, S2);
return;
}
EnterCriticalSection(&GUI.SoundCritSect);
if (B1)
{
S9xMixSamples(B1,(Settings.SixteenBitSound?S1>>1:S1));
}
if (B2)
{
S9xMixSamples(B2,(Settings.SixteenBitSound?S2>>1:S2));
}
LeaveCriticalSection(&GUI.SoundCritSect);
SetEvent(GUI.SoundSyncEvent);
hResult = lpDSB -> Unlock (B1, S1, B2, S2);
if (!SUCCEEDED(hResult))
return;
}
}
/* CDirectSound::SoundTimerCallback
Timer callback that tries to mix a new block. Called twice each block.
*/
VOID CALLBACK CDirectSound::SoundTimerCallback(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) {
CDirectSound *S9xDirectSound = (CDirectSound *)dwUser;
S9xDirectSound->MixSound();
}

View File

@ -1,64 +0,0 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
// CDirectSound.h: interface for the CDirectSound class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(DIRECTSOUND_H_INCLUDED)
#define DIRECTSOUND_H_INCLUDED
#include <windows.h>
#include "IS9xSoundOutput.h"
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class CDirectSound : public IS9xSoundOutput
{
private:
LPDIRECTSOUND lpDS;
LPDIRECTSOUNDBUFFER lpDSB; // the buffer used for mixing
LPDIRECTSOUNDBUFFER lpDSBPrimary;
int blockCount; // number of blocks in the buffer
int blockSize; // bytes in one block
int blockSamples; // samples in one block
int bufferSize; // bytes in the whole buffer
int blockTime; // ms in one block
DWORD last_block; // the last block that was mixed
bool initDone; // has init been called successfully?
DWORD hTimer; // mixing timer
bool InitDirectSound ();
void DeInitDirectSound();
bool InitSoundBuffer();
void DeInitSoundBuffer();
static VOID CALLBACK SoundTimerCallback(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2);
void ProcessSound();
void MixSound();
public:
CDirectSound();
~CDirectSound();
// Inherited from IS9xSoundOutput
bool InitSoundOutput(void) { return InitDirectSound(); }
void DeInitSoundOutput(void) { DeInitDirectSound(); }
bool SetupSound(void);
void SetVolume(double volume);
};
extern CDirectSound DirectSound;
#endif // !defined(DIRECTSOUND_H_INCLUDED)

261
win32/CWaveOut.cpp Normal file
View File

@ -0,0 +1,261 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "CWaveOut.h"
#include "../snes9x.h"
#include "../apu/apu.h"
#include "wsnes9x.h"
CWaveOut::CWaveOut(void)
{
hWaveOut = NULL;
initDone = false;
}
CWaveOut::~CWaveOut(void)
{
DeInitSoundOutput();
}
void CALLBACK WaveCallback(HWAVEOUT hWave, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
{
if (uMsg == WOM_DONE)
{
InterlockedDecrement(((volatile LONG *)dwUser));
SetEvent(GUI.SoundSyncEvent);
}
}
bool CWaveOut::SetupSound()
{
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = Settings.SoundPlaybackRate;
wfx.nBlockAlign = 2 * 2;
wfx.wBitsPerSample = 16;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
// subtract -1, we added "Default" as first index - Default will yield -1, which is WAVE_MAPPER
int device_index = FindDeviceIndex(GUI.AudioDevice) - 1;
waveOutOpen(&hWaveOut, device_index, &wfx, (DWORD_PTR)WaveCallback, (DWORD_PTR)&bufferCount, CALLBACK_FUNCTION);
UINT32 blockTime = GUI.SoundBufferSize / blockCount;
singleBufferSamples = (Settings.SoundPlaybackRate * blockTime) / 1000;
if (singleBufferSamples < 256)
singleBufferSamples = 256;
singleBufferSamples *= 2;
singleBufferBytes = singleBufferSamples * 2;
sumBufferSize = singleBufferBytes * blockCount;
writeOffset = 0;
partialOffset = 0;
waveHeaders.resize(blockCount);
for (auto &w : waveHeaders)
{
w.lpData = (LPSTR)LocalAlloc(LMEM_FIXED, singleBufferBytes);
w.dwBufferLength = singleBufferBytes;
w.dwBytesRecorded = 0;
w.dwUser = 0;
w.dwFlags = 0;
w.dwLoops = 0;
w.lpNext = 0;
w.reserved = 0;
waveOutPrepareHeader(hWaveOut, &w, sizeof(WAVEHDR));
}
initDone = true;
return true;
}
void CWaveOut::SetVolume(double volume)
{
waveOutSetVolume(hWaveOut, (DWORD)(volume * 0xffffffff));
}
void CWaveOut::BeginPlayback()
{
waveOutRestart(hWaveOut);
}
bool CWaveOut::InitSoundOutput()
{
return true;
}
void CWaveOut::DeInitSoundOutput()
{
if (!initDone)
return;
StopPlayback();
waveOutReset(hWaveOut);
if (!waveHeaders.empty())
{
for (auto &w : waveHeaders)
{
waveOutUnprepareHeader(hWaveOut, &w, sizeof(WAVEHDR));
LocalFree(w.lpData);
}
}
waveHeaders.clear();
waveOutClose(hWaveOut);
initDone = false;
}
void CWaveOut::StopPlayback()
{
waveOutPause(hWaveOut);
}
int CWaveOut::GetAvailableBytes()
{
return ((blockCount - bufferCount) * singleBufferBytes) - partialOffset;
}
// Fill the set of blocks preceding writeOffset with silence and write them
// to the output to get the buffer back to 50%
void CWaveOut::RecoverFromUnderrun()
{
writeOffset = (writeOffset - (blockCount / 2) + blockCount) % blockCount;
for (int i = 0; i < blockCount / 2; i++)
{
memset(waveHeaders[writeOffset].lpData, 0, singleBufferBytes);
waveOutWrite(hWaveOut, &waveHeaders[writeOffset], sizeof(WAVEHDR));
InterlockedIncrement(&bufferCount);
writeOffset++;
writeOffset %= blockCount;
}
}
void CWaveOut::ProcessSound()
{
int freeBytes = ((blockCount - bufferCount) * singleBufferBytes) - partialOffset;
if (bufferCount == 0)
RecoverFromUnderrun();
if (Settings.DynamicRateControl)
{
S9xUpdateDynamicRate(freeBytes, sumBufferSize);
}
UINT32 availableSamples;
availableSamples = 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 (availableSamples > (freeBytes >> 1))
{
S9xClearSamples();
return;
}
}
if (!initDone)
return;
if(Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
{
// no sound sync when speed is not set to 100%
while((freeBytes >> 1) < availableSamples)
{
ResetEvent(GUI.SoundSyncEvent);
if(!GUI.AllowSoundSync || WaitForSingleObject(GUI.SoundSyncEvent, 1000) != WAIT_OBJECT_0)
{
S9xClearSamples();
return;
}
freeBytes = GetAvailableBytes();
}
}
if (partialOffset != 0) {
UINT32 samplesleftinblock = (singleBufferBytes - partialOffset) >> 1;
BYTE *offsetBuffer = (BYTE *)waveHeaders[writeOffset].lpData + partialOffset;
if (availableSamples <= samplesleftinblock)
{
S9xMixSamples(offsetBuffer, availableSamples);
partialOffset += availableSamples << 1;
availableSamples = 0;
}
else
{
S9xMixSamples(offsetBuffer, samplesleftinblock);
partialOffset = 0;
availableSamples -= samplesleftinblock;
waveOutWrite(hWaveOut, &waveHeaders[writeOffset], sizeof(WAVEHDR));
InterlockedIncrement(&bufferCount);
writeOffset++;
writeOffset %= blockCount;
}
}
while (availableSamples >= singleBufferSamples && bufferCount < blockCount) {
BYTE *curBuffer = (BYTE *)waveHeaders[writeOffset].lpData;
S9xMixSamples(curBuffer, singleBufferSamples);
waveOutWrite(hWaveOut, &waveHeaders[writeOffset], sizeof(WAVEHDR));
InterlockedIncrement(&bufferCount);
writeOffset++;
writeOffset %= blockCount;
availableSamples -= singleBufferSamples;
}
if (availableSamples > 0 && bufferCount < blockCount) {
S9xMixSamples((BYTE *)waveHeaders[writeOffset].lpData, availableSamples);
partialOffset = availableSamples << 1;
}
}
std::vector<std::wstring> CWaveOut::GetDeviceList()
{
std::vector<std::wstring> device_list;
UINT num_devices = waveOutGetNumDevs();
device_list.push_back(_T("Default"));
for (unsigned int i = 0; i < num_devices; i++)
{
WAVEOUTCAPS caps;
if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
{
device_list.push_back(caps.szPname);
}
}
return device_list;
}
int CWaveOut::FindDeviceIndex(TCHAR *audio_device)
{
std::vector<std::wstring> device_list = GetDeviceList();
int index = 0;
for (int i = 0; i < device_list.size(); i++)
{
if (_tcsstr(device_list[i].c_str(), audio_device) != NULL)
{
index = i;
break;
}
}
return index;
}

47
win32/CWaveOut.h Normal file
View File

@ -0,0 +1,47 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#pragma once
#include "../snes9x.h"
#include <windows.h>
#include "IS9xSoundOutput.h"
#include <mmsystem.h>
#include <vector>
class CWaveOut : public IS9xSoundOutput
{
private:
void BeginPlayback(void);
void StopPlayback(void);
void ProcessSound(void);
int GetAvailableBytes();
void RecoverFromUnderrun();
HWAVEOUT hWaveOut;
bool initDone;
volatile LONG bufferCount;
UINT32 sumBufferSize;
UINT32 singleBufferSamples;
UINT32 singleBufferBytes;
const UINT32 blockCount = 8;
UINT32 writeOffset;
UINT32 partialOffset;
std::vector<WAVEHDR> waveHeaders;
public:
CWaveOut(void);
~CWaveOut(void);
// Inherited from IS9xSoundOutput
bool InitSoundOutput(void);
void DeInitSoundOutput(void);
bool SetupSound(void);
void SetVolume(double volume);
std::vector<std::wstring> GetDeviceList();
int FindDeviceIndex(TCHAR *audio_device);
};

View File

@ -130,7 +130,7 @@ bool CXAudio2::InitVoices(void)
if (device_index < 0)
device_index = 0;
if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasterVoice, (Settings.Stereo?2:1),
if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasterVoice, 2,
Settings.SoundPlaybackRate, 0, device_index, NULL ) ) ) {
DXTRACE_ERR_MSGBOX(TEXT("Unable to create mastering voice."),hr);
return false;
@ -138,15 +138,15 @@ bool CXAudio2::InitVoices(void)
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = Settings.Stereo ? 2 : 1;
wfx.nChannels = 2;
wfx.nSamplesPerSec = Settings.SoundPlaybackRate;
wfx.nBlockAlign = (Settings.SixteenBitSound ? 2 : 1) * (Settings.Stereo ? 2 : 1);
wfx.wBitsPerSample = Settings.SixteenBitSound ? 16 : 8;
wfx.nBlockAlign = 2 * 2;
wfx.wBitsPerSample = 16;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
if( FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&wfx,
XAUDIO2_VOICE_NOSRC , XAUDIO2_DEFAULT_FREQ_RATIO, this, NULL, NULL ) ) ) {
XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, NULL, NULL ) ) ) {
DXTRACE_ERR_MSGBOX(TEXT("Unable to create source voice."),hr);
return false;
}
@ -231,14 +231,15 @@ bool CXAudio2::SetupSound()
UINT32 blockTime = GUI.SoundBufferSize / blockCount;
singleBufferSamples = (Settings.SoundPlaybackRate * blockTime) / 1000;
singleBufferSamples *= (Settings.Stereo ? 2 : 1);
singleBufferBytes = singleBufferSamples * (Settings.SixteenBitSound ? 2 : 1);
singleBufferSamples *= 2;
singleBufferBytes = singleBufferSamples * 2;
sum_bufferSize = singleBufferBytes * blockCount;
if (InitVoices())
{
soundBuffer = new uint8[sum_bufferSize];
writeOffset = 0;
partialOffset = 0;
}
else {
DeInitVoices();
@ -267,6 +268,11 @@ void CXAudio2::StopPlayback()
pSourceVoice->Stop(0);
}
int CXAudio2::GetAvailableBytes()
{
return ((blockCount - bufferCount) * singleBufferBytes) - partialOffset;
}
/* CXAudio2::ProcessSound
The mixing function called by the sound core when new samples are available.
SoundBuffer is divided into blockCount blocks. If there are enough available samples and a free block,
@ -275,7 +281,7 @@ the OnBufferComplete callback.
*/
void CXAudio2::ProcessSound()
{
int freeBytes = (blockCount - bufferCount) * singleBufferBytes;
int freeBytes = GetAvailableBytes();
if (Settings.DynamicRateControl)
{
@ -288,11 +294,11 @@ void CXAudio2::ProcessSound()
availableSamples = S9xGetSampleCount();
if (Settings.DynamicRateControl)
if (Settings.DynamicRateControl && !Settings.SoundSync)
{
// Using rate control, we should always keep the emulator's sound buffers empty to
// maintain an accurate measurement.
if (availableSamples > (freeBytes >> (Settings.SixteenBitSound ? 1 : 0)))
if (availableSamples > (freeBytes >> 1))
{
S9xClearSamples();
return;
@ -302,15 +308,54 @@ void CXAudio2::ProcessSound()
if(!initDone)
return;
BYTE * curBuffer;
if(Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
{
// no sound sync when speed is not set to 100%
while((freeBytes >> 1) < availableSamples)
{
ResetEvent(GUI.SoundSyncEvent);
if(!GUI.AllowSoundSync || WaitForSingleObject(GUI.SoundSyncEvent, 1000) != WAIT_OBJECT_0)
{
S9xClearSamples();
return;
}
freeBytes = GetAvailableBytes();
}
}
while(availableSamples > singleBufferSamples && bufferCount < blockCount) {
curBuffer = soundBuffer + writeOffset;
S9xMixSamples(curBuffer,singleBufferSamples);
PushBuffer(singleBufferBytes,curBuffer,NULL);
writeOffset+=singleBufferBytes;
writeOffset%=sum_bufferSize;
availableSamples -= singleBufferSamples;
if (partialOffset != 0) {
BYTE *offsetBuffer = soundBuffer + writeOffset + partialOffset;
UINT32 samplesleftinblock = (singleBufferBytes - partialOffset) >> 1;
if (availableSamples <= samplesleftinblock)
{
S9xMixSamples(offsetBuffer, availableSamples);
partialOffset += availableSamples << 1;
availableSamples = 0;
}
else
{
S9xMixSamples(offsetBuffer, samplesleftinblock);
partialOffset = 0;
availableSamples -= samplesleftinblock;
PushBuffer(singleBufferBytes, soundBuffer + writeOffset, NULL);
writeOffset += singleBufferBytes;
writeOffset %= sum_bufferSize;
}
}
while (availableSamples >= singleBufferSamples && bufferCount < blockCount) {
BYTE *curBuffer = soundBuffer + writeOffset;
S9xMixSamples(curBuffer, singleBufferSamples);
PushBuffer(singleBufferBytes, curBuffer, NULL);
writeOffset += singleBufferBytes;
writeOffset %= sum_bufferSize;
availableSamples -= singleBufferSamples;
}
if (availableSamples > 0 && bufferCount < blockCount) {
S9xMixSamples(soundBuffer + writeOffset, availableSamples);
partialOffset = availableSamples << 1;
}
}

View File

@ -26,9 +26,9 @@ private:
UINT32 singleBufferSamples; // samples in one block
UINT32 singleBufferBytes; // bytes in one block
UINT32 blockCount; // soundBuffer is divided into blockCount blocks
// currently set to 4
// currently set to 8
UINT32 writeOffset; // offset into the buffer for the next block
UINT32 partialOffset; // offset into non-complete block
uint8 *soundBuffer; // the buffer itself
bool InitVoices(void);
@ -40,9 +40,7 @@ private:
void ProcessSound(void);
bool InitXAudio2(void);
void DeInitXAudio2(void);
std::vector<std::wstring> GetDeviceList();
int FindDeviceIndex(TCHAR *audio_device);
int GetAvailableBytes();
public:
CXAudio2(void);
@ -63,6 +61,8 @@ public:
void DeInitSoundOutput(void) { DeInitXAudio2(); }
bool SetupSound(void);
void SetVolume(double volume);
std::vector<std::wstring> GetDeviceList();
int FindDeviceIndex(TCHAR *audio_device);
};
#endif

View File

@ -48,8 +48,6 @@
#define IDC_MIX 1004
#define IDC_OUTPUT_DEVICE 1004
#define IDC_DYNRATECONTROL 1005
#define IDC_STEREO 1006
#define IDC_REV_STEREO 1007
#define IDC_LINEAR_INTER 1008
#define IDC_SYNC_TO_SOUND_CPU 1009
#define IDC_ECHO 1011
@ -405,8 +403,6 @@
#define ID_SOUND_INTERPOLATED 40027
#define ID_SOUND_SYNC 40028
#define ID_WINDOW_FULLSCREEN 40029
#define ID_SOUND_16BIT 40030
#define ID_SOUND_STEREO 40031
#define ID_WINDOW_STRETCH 40032
#define ID_SOUND_8000HZ 40033
#define ID_SOUND_11025HZ 40034
@ -436,7 +432,6 @@
#define ID_SOUND_48000HZ 40085
#define ID_SOUND_16000HZ 40086
#define ID_SOUND_35000HZ 40087
#define ID_SOUND_REVERSE_STEREO 40088
#define ID_FILE_SAVE_SRAM_DATA 40089
#define ID_RECENT_DUMMY 40090
#define IDM_ROM_INFO 40094
@ -498,8 +493,8 @@
#define ID_SOUND_144MS 40162
#define ID_SOUND_160MS 40163
#define ID_SOUND_176MS 40164
#define ID_SOUND_194MS 40165
#define ID_SOUND_210MS 40166
#define ID_SOUND_192MS 40165
#define ID_SOUND_208MS 40166
#define ID_EMULATION_PAUSEWHENINACTIVE 40167
#define ID_VIDEO_SHOWFRAMERATE 40168
#define ID_WINDOW_SIZE_1X 40169

View File

@ -26,42 +26,40 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// Dialog
//
IDD_SOUND_OPTS DIALOGEX 0, 0, 413, 157
IDD_SOUND_OPTS DIALOGEX 0, 0, 414, 156
STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU
CAPTION "Sound Settings"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
GROUPBOX "Sound Quality",IDC_STATIC,119,7,286,124,0,WS_EX_TRANSPARENT
DEFPUSHBUTTON "&OK",IDOK,288,134,56,16
COMBOBOX IDC_DRIVER,177,34,106,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Sound Driver:",IDC_STATIC,125,36,49,11
COMBOBOX IDC_BUFLEN,177,68,106,101,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Buffer Length:",IDC_STATIC,125,70,49,11
COMBOBOX IDC_RATE,177,51,106,171,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Playback Rate:",IDC_STATIC,125,53,49,11
CONTROL "&Dynamic Rate Control",IDC_DYNRATECONTROL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,295,97,102,10
CONTROL "&Automatic Input Rate",IDC_AUTOMATICINPUTRATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,295,109,102,10
CONTROL "&Stereo",IDC_STEREO,"Button",BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,295,37,102,10
CONTROL "&Reverse Stereo",IDC_REV_STEREO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,295,49,102,10
GROUPBOX "Sound Quality",IDC_STATIC,119,6,289,126,0,WS_EX_TRANSPARENT
DEFPUSHBUTTON "&OK",IDOK,318,138,42,14
COMBOBOX IDC_DRIVER,180,36,106,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Sound Driver:",IDC_STATIC,126,36,51,12,SS_CENTERIMAGE
COMBOBOX IDC_BUFLEN,180,72,106,101,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Buffer Length:",IDC_STATIC,126,72,51,12,SS_CENTERIMAGE
COMBOBOX IDC_RATE,180,54,106,171,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Playback Rate:",IDC_STATIC,126,54,51,12,SS_CENTERIMAGE
CONTROL "&Dynamic Rate Control",IDC_DYNRATECONTROL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,294,72,102,10
CONTROL "&Automatic Input Rate",IDC_AUTOMATICINPUTRATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,294,114,102,10
CONTROL "&Synchronize with sound core",IDC_SYNC_TO_SOUND_CPU,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,295,85,102,10
PUSHBUTTON "&Cancel",IDCANCEL,350,134,56,16
CONTROL "&Mute sound",IDC_MUTE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,295,61,102,10
CONTROL "Frame Advance mu&te",IDC_FAMT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,295,73,102,10
CONTROL "",IDC_INRATE,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | WS_TABSTOP,124,100,157,26
EDITTEXT IDC_INRATEEDIT,177,86,105,12,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "Input Rate:",IDC_INRATETEXT,126,86,46,11
CONTROL "",IDC_SLIDER_VOLUME_REGULAR,"msctls_trackbar32",TBS_AUTOTICKS | TBS_VERT | TBS_BOTH | WS_TABSTOP | 0x400,22,25,23,80
EDITTEXT IDC_EDIT_VOLUME_REGULAR,22,111,22,13,ES_AUTOHSCROLL
LTEXT "Regular",IDC_STATIC,22,17,49,10
GROUPBOX "Volume",IDC_STATIC,7,7,105,124,0,WS_EX_TRANSPARENT
CONTROL "",IDC_SLIDER_VOLUME_TURBO,"msctls_trackbar32",TBS_AUTOTICKS | TBS_VERT | TBS_BOTH | WS_TABSTOP | 0x400,70,25,23,80
EDITTEXT IDC_EDIT_VOLUME_TURBO,70,111,22,13,ES_AUTOHSCROLL
LTEXT "Fast-Forward",IDC_STATIC,62,17,49,10
LTEXT "%",IDC_STATIC,47,113,8,8
LTEXT "%",IDC_STATIC,95,113,8,8
COMBOBOX IDC_OUTPUT_DEVICE,177,17,220,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Output Device:",IDC_STATIC,125,19,49,11
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,294,60,112,10
PUSHBUTTON "&Cancel",IDCANCEL,366,138,42,14
CONTROL "&Mute sound",IDC_MUTE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,294,36,102,10
CONTROL "Frame Advance mu&te",IDC_FAMT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,294,48,102,10
CONTROL "",IDC_INRATE,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | WS_TABSTOP,126,108,162,18
EDITTEXT IDC_INRATEEDIT,180,90,108,12,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "Input Rate:",IDC_INRATETEXT,126,90,51,12,SS_CENTERIMAGE
CONTROL "",IDC_SLIDER_VOLUME_REGULAR,"msctls_trackbar32",TBS_AUTOTICKS | TBS_VERT | TBS_BOTH | WS_TABSTOP | 0x400,18,30,24,78
EDITTEXT IDC_EDIT_VOLUME_REGULAR,18,108,24,12,ES_AUTOHSCROLL
CTEXT "Regular",IDC_STATIC,12,18,36,8
GROUPBOX "Volume",IDC_STATIC,7,6,107,126,0,WS_EX_TRANSPARENT
CONTROL "",IDC_SLIDER_VOLUME_TURBO,"msctls_trackbar32",TBS_AUTOTICKS | TBS_VERT | TBS_BOTH | WS_TABSTOP | 0x400,72,30,24,78
EDITTEXT IDC_EDIT_VOLUME_TURBO,72,108,24,12,ES_AUTOHSCROLL
CTEXT "Fast-Forward",IDC_STATIC,60,18,45,8
CTEXT "%",IDC_STATIC,42,108,12,12,SS_CENTERIMAGE
CTEXT "%",IDC_STATIC,96,108,12,12,SS_CENTERIMAGE
COMBOBOX IDC_OUTPUT_DEVICE,180,18,222,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Output Device:",IDC_STATIC,126,18,51,12,SS_CENTERIMAGE
END
IDD_ROM_INFO DIALOGEX 0, 0, 233, 185
@ -168,61 +166,61 @@ BEGIN
CONTROL "Sync By Reset",IDC_SYNCBYRESET,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,90,174,15
END
IDD_NEWDISPLAY DIALOGEX 0, 0, 353, 283
IDD_NEWDISPLAY DIALOGEX 0, 0, 348, 288
STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CLIPCHILDREN | WS_CAPTION
CAPTION "Display Settings"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,239,259,50,14
PUSHBUTTON "Cancel",IDCANCEL,296,259,50,14
COMBOBOX IDC_OUTPUTMETHOD,68,17,101,58,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "Fullscreen",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,30,48,10
DEFPUSHBUTTON "OK",IDOK,240,270,48,14
PUSHBUTTON "Cancel",IDCANCEL,294,270,48,14
COMBOBOX IDC_OUTPUTMETHOD,72,18,95,58,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "Fullscreen",IDC_FULLSCREEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,30,50,10
CONTROL "Emulate Fullscreen",IDC_EMUFULLSCREEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,41,75,10
CONTROL "Stretch Image",IDC_STRETCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,52,60,10
CONTROL "Maintain Aspect Ratio",IDC_ASPECT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,63,85,10
COMBOBOX IDC_ASPECTDROP,104,63,63,41,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
COMBOBOX IDC_ASPECTDROP,102,63,65,41,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "Integer Scaling",IDC_INTEGERSCALING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,74,75,10
CONTROL "Bilinear Filtering",IDC_BILINEAR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,85,75,10
CONTROL "Show Frame Rate",IDC_SHOWFPS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,96,73,10
CONTROL "Automatic",IDC_AUTOFRAME,"Button",BS_AUTORADIOBUTTON,11,145,43,8
CONTROL "Fixed",IDC_FIXEDSKIP,"Button",BS_AUTORADIOBUTTON,11,162,43,10
EDITTEXT IDC_MAXSKIP,133,144,35,14,ES_AUTOHSCROLL | ES_NUMBER
CONTROL "",IDC_SPIN_MAX_SKIP_DISP,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,169,146,11,13
EDITTEXT IDC_SKIPCOUNT,133,161,35,14,ES_AUTOHSCROLL | ES_NUMBER
CONTROL "",IDC_SPIN_MAX_SKIP_DISP_FIXED,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,169,158,11,13
COMBOBOX IDC_FILTERBOX,186,17,153,90,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
COMBOBOX IDC_FILTERBOX2,217,33,122,90,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "NTSC Filter Scanlines",IDC_NTSCSCANLINES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,186,47,85,10
LTEXT "Resolution",IDC_CURRMODE,187,76,41,8
COMBOBOX IDC_RESOLUTION,234,74,105,95,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "Enable Triple Buffering",IDC_DBLBUFFER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,187,88,87,10
CONTROL "Transparency Effects",IDC_TRANS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,187,118,153,10
CONTROL "Extend Height of SNES Image",IDC_HEIGHT_EXTEND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,187,140,153,10
CONTROL "Automatic",IDC_AUTOFRAME,"Button",BS_AUTORADIOBUTTON,12,150,43,12
CONTROL "Fixed",IDC_FIXEDSKIP,"Button",BS_AUTORADIOBUTTON,12,168,43,12
EDITTEXT IDC_MAXSKIP,138,150,29,14,ES_AUTOHSCROLL | ES_NUMBER
CONTROL "",IDC_SPIN_MAX_SKIP_DISP,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,156,150,12,14
EDITTEXT IDC_SKIPCOUNT,138,168,29,14,ES_AUTOHSCROLL | ES_NUMBER
CONTROL "",IDC_SPIN_MAX_SKIP_DISP_FIXED,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,156,168,12,14
COMBOBOX IDC_FILTERBOX,186,18,150,90,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
COMBOBOX IDC_FILTERBOX2,222,36,114,90,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "NTSC Filter Scanlines",IDC_NTSCSCANLINES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,186,54,84,8
LTEXT "Resolution:",IDC_CURRMODE,186,84,42,8
COMBOBOX IDC_RESOLUTION,234,83,102,95,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "Enable Triple Buffering",IDC_DBLBUFFER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,186,98,96,8
CONTROL "Transparency Effects",IDC_TRANS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,186,126,132,8
CONTROL "Extend Height of SNES Image",IDC_HEIGHT_EXTEND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,186,150,150,8
CONTROL "Display messages before applying filters",IDC_MESSAGES_IN_IMAGE,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,187,151,153,10
GROUPBOX "",IDC_SHADER_GROUP,8,184,338,71,0,WS_EX_TRANSPARENT
EDITTEXT IDC_SHADER_HLSL_FILE,13,206,306,14,ES_AUTOHSCROLL | WS_DISABLED
PUSHBUTTON "...",IDC_SHADER_HLSL_BROWSE,322,206,19,14,WS_DISABLED
GROUPBOX "Frame Skipping:",IDC_STATIC,7,132,167,46,0,WS_EX_TRANSPARENT
GROUPBOX "SNES Image",IDC_STATIC,180,106,166,72,0,WS_EX_TRANSPARENT
GROUPBOX "Output Image Processing",IDC_STATIC,179,7,166,53,0,WS_EX_TRANSPARENT
GROUPBOX "Fullscreen Display Settings",IDC_STATIC,179,63,166,39,0,WS_EX_TRANSPARENT
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,186,162,150,8
GROUPBOX "",IDC_SHADER_GROUP,6,192,336,72,0,WS_EX_TRANSPARENT
EDITTEXT IDC_SHADER_HLSL_FILE,12,216,306,14,ES_AUTOHSCROLL | WS_DISABLED
PUSHBUTTON "...",IDC_SHADER_HLSL_BROWSE,318,216,18,14,WS_DISABLED
GROUPBOX "Frame Skipping:",IDC_STATIC,6,138,167,48,0,WS_EX_TRANSPARENT
GROUPBOX "SNES Image",IDC_STATIC,180,114,162,72,0,WS_EX_TRANSPARENT
GROUPBOX "Output Image Processing",IDC_STATIC,180,6,162,60,0,WS_EX_TRANSPARENT
GROUPBOX "Fullscreen Display Settings",IDC_STATIC,180,72,163,38,0,WS_EX_TRANSPARENT
LTEXT "Hi Res:",IDC_HIRESLABEL,186,36,31,8
GROUPBOX "General",IDC_STATIC,7,7,167,123,0,WS_EX_TRANSPARENT
LTEXT "Max skipped frames:",IDC_STATIC,62,145,67,8
LTEXT "Amount skipped:",IDC_STATIC,62,163,67,8
LTEXT "Output Method",IDC_STATIC,10,17,51,12
CONTROL "Use Shader",IDC_SHADER_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,184,52,9
LTEXT "Direct3D Shader File",IDC_STATIC,13,195,104,8
EDITTEXT IDC_SHADER_GLSL_FILE,13,235,306,14,ES_AUTOHSCROLL | WS_DISABLED
PUSHBUTTON "...",IDC_SHADER_GLSL_BROWSE,322,235,19,14,WS_DISABLED
LTEXT "OpenGL Shader File",IDC_STATIC,13,224,104,8
CONTROL "Blend Hi-Res Images",IDC_HIRESBLEND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,187,129,153,10
GROUPBOX "General",IDC_STATIC,6,6,168,128,0,WS_EX_TRANSPARENT
LTEXT "Max skipped frames:",IDC_STATIC,60,150,70,11,SS_CENTERIMAGE
LTEXT "Frames skipped:",IDC_STATIC,60,168,60,12,SS_CENTERIMAGE
LTEXT "Output Method",IDC_STATIC,12,18,51,12
CONTROL "Use Shader",IDC_SHADER_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,192,52,9
LTEXT "Direct3D Shader File",IDC_STATIC,12,204,70,8
EDITTEXT IDC_SHADER_GLSL_FILE,12,246,246,14,ES_AUTOHSCROLL | WS_DISABLED
PUSHBUTTON "...",IDC_SHADER_GLSL_BROWSE,258,246,18,14,WS_DISABLED
LTEXT "OpenGL Shader File",IDC_STATIC,12,234,68,8
CONTROL "Blend Hi-Res Images",IDC_HIRESBLEND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,186,138,144,8
CONTROL "VSync",IDC_VSYNC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,107,37,10
CONTROL "Reduce Input Lag",IDC_REDUCEINPUTLAG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,118,76,10
CONTROL "Scale messages with EPX if possible",IDC_MESSAGES_SCALE,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,187,162,153,10
PUSHBUTTON "Parameters...",IDC_SHADER_GLSL_PARAMETERS,112,222,60,12,WS_DISABLED
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,186,174,132,8
PUSHBUTTON "Parameters...",IDC_SHADER_GLSL_PARAMETERS,282,246,54,14,WS_DISABLED
END
IDD_CHEATER DIALOGEX 0, 0, 375, 194
@ -574,7 +572,7 @@ BEGIN
IDD_SOUND_OPTS, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 406
RIGHTMARGIN, 407
TOPMARGIN, 7
BOTTOMMARGIN, 150
END
@ -613,9 +611,9 @@ BEGIN
IDD_NEWDISPLAY, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 346
RIGHTMARGIN, 341
TOPMARGIN, 7
BOTTOMMARGIN, 275
BOTTOMMARGIN, 280
END
IDD_CHEATER, DIALOG
@ -786,7 +784,6 @@ BEGIN
VK_F8, ID_OPTIONS_SETTINGS, VIRTKEY, ALT, NOINVERT
"[", ID_SOUND_INTERPOLATED, ASCII, ALT, NOINVERT
"T", ID_SOUND_OPTIONS, VIRTKEY, ALT, NOINVERT
"R", ID_SOUND_REVERSE_STEREO, VIRTKEY, ALT, NOINVERT
"]", ID_SOUND_SYNC, ASCII, ALT, NOINVERT
VK_RETURN, ID_WINDOW_FULLSCREEN, VIRTKEY, ALT, NOINVERT
VK_BACK, ID_WINDOW_STRETCH, VIRTKEY, ALT, NOINVERT
@ -1018,8 +1015,8 @@ BEGIN
MENUITEM "144ms", ID_SOUND_144MS
MENUITEM "160ms", ID_SOUND_160MS
MENUITEM "176ms", ID_SOUND_176MS
MENUITEM "194ms", ID_SOUND_194MS
MENUITEM "210ms", ID_SOUND_210MS
MENUITEM "192ms", ID_SOUND_192MS
MENUITEM "208ms", ID_SOUND_208MS
END
POPUP "&Channels"
BEGIN
@ -1034,9 +1031,6 @@ BEGIN
MENUITEM SEPARATOR
MENUITEM "Enable All", ID_CHANNELS_ENABLEALL
END
MENUITEM "&16-Bit Sound", ID_SOUND_16BIT
MENUITEM "&Stereo", ID_SOUND_STEREO
MENUITEM "&Reverse Stereo\tAlt+R", ID_SOUND_REVERSE_STEREO
MENUITEM SEPARATOR
MENUITEM "S&ync Sound\tAlt+]", ID_SOUND_SYNC
MENUITEM "&Settings...\tAlt+T", ID_SOUND_OPTIONS

View File

@ -320,8 +320,6 @@
<ClInclude Include="..\apu\bapu\dsp\sdsp.hpp" />
<ClInclude Include="..\apu\bapu\dsp\SPC_DSP.h" />
<ClInclude Include="..\apu\bapu\snes\snes.hpp" />
<ClInclude Include="..\apu\hermite_resampler.h" />
<ClInclude Include="..\apu\resampler.h" />
<ClInclude Include="..\apu\ring_buffer.h" />
<CustomBuild Include="..\bsx.h" />
<CustomBuild Include="..\c4.h" />
@ -433,12 +431,12 @@
<ClInclude Include="CD3DCG.h" />
<CustomBuild Include="CDirect3D.h" />
<ClInclude Include="CDirectDraw.h" />
<ClInclude Include="CDirectSound.h" />
<ClInclude Include="cgFunctions.h" />
<ClInclude Include="CGLCG.h" />
<ClInclude Include="cgMini.h" />
<ClInclude Include="COpenGL.h" />
<ClInclude Include="CShaderParamDlg.h" />
<ClInclude Include="CWaveOut.h" />
<ClInclude Include="CXAudio2.h" />
<ClInclude Include="dxerr.h" />
<ClInclude Include="gl_core_3_1.h" />
@ -577,11 +575,11 @@
<ClCompile Include="CD3DCG.cpp" />
<ClCompile Include="CDirect3D.cpp" />
<ClCompile Include="CDirectDraw.cpp" />
<ClCompile Include="CDirectSound.cpp" />
<ClCompile Include="cgFunctions.cpp" />
<ClCompile Include="CGLCG.cpp" />
<ClCompile Include="COpenGL.cpp" />
<ClCompile Include="CShaderParamDlg.cpp" />
<ClCompile Include="CWaveOut.cpp" />
<ClCompile Include="CXAudio2.cpp" />
<ClCompile Include="DumpAtEnd.cpp" />
<ClCompile Include="dxerr.cpp" />

View File

@ -96,9 +96,6 @@
<ClInclude Include="wsnes9x.h">
<Filter>GUI</Filter>
</ClInclude>
<ClInclude Include="CDirectSound.h">
<Filter>GUI\SoundDriver</Filter>
</ClInclude>
<ClInclude Include="CXAudio2.h">
<Filter>GUI\SoundDriver</Filter>
</ClInclude>
@ -186,12 +183,6 @@
<ClInclude Include="..\apu\apu.h">
<Filter>APU</Filter>
</ClInclude>
<ClInclude Include="..\apu\hermite_resampler.h">
<Filter>APU</Filter>
</ClInclude>
<ClInclude Include="..\apu\resampler.h">
<Filter>APU</Filter>
</ClInclude>
<ClInclude Include="..\apu\ring_buffer.h">
<Filter>APU</Filter>
</ClInclude>
@ -291,6 +282,9 @@
<ClInclude Include="..\shaders\SPIRV-Cross\spirv_parser.hpp">
<Filter>Shaders</Filter>
</ClInclude>
<ClInclude Include="CWaveOut.h">
<Filter>GUI\SoundDriver</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\bsx.cpp">
@ -494,9 +488,6 @@
<ClCompile Include="wsnes9x.cpp">
<Filter>GUI</Filter>
</ClCompile>
<ClCompile Include="CDirectSound.cpp">
<Filter>GUI\SoundDriver</Filter>
</ClCompile>
<ClCompile Include="CXAudio2.cpp">
<Filter>GUI\SoundDriver</Filter>
</ClCompile>
@ -623,6 +614,9 @@
<ClCompile Include="..\shaders\SPIRV-Cross\spirv_parser.cpp">
<Filter>Shaders</Filter>
</ClCompile>
<ClCompile Include="CWaveOut.cpp">
<Filter>GUI\SoundDriver</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="rsrc\nodrop.cur">

View File

@ -117,6 +117,7 @@ void WinSetDefaultValues ()
Settings.TakeScreenshot=false;
GUI.Language=0;
GUI.AllowSoundSync = true;
}
@ -385,16 +386,16 @@ struct ConfigItem
if(size == 8) *(uint64*)addr = (uint64)conf.GetBool(name, def!=0);
break;
case CIT_INT:
if(size == 1) *(uint8 *)addr = (uint8) conf.GetInt(name, reinterpret_cast<int32>(def));
if(size == 2) *(uint16*)addr = (uint16)conf.GetInt(name, reinterpret_cast<int32>(def));
if(size == 4) *(uint32*)addr = (uint32)conf.GetInt(name, reinterpret_cast<int32>(def));
if(size == 8) *(uint64*)addr = (uint64)conf.GetInt(name, reinterpret_cast<int32>(def));
if(size == 1) *(uint8 *)addr = (uint8) conf.GetInt(name, PtrToInt(def));
if(size == 2) *(uint16*)addr = (uint16)conf.GetInt(name, PtrToInt(def));
if(size == 4) *(uint32*)addr = (uint32)conf.GetInt(name, PtrToInt(def));
if(size == 8) *(uint64*)addr = (uint64)conf.GetInt(name, PtrToInt(def));
break;
case CIT_UINT:
if(size == 1) *(uint8 *)addr = (uint8) conf.GetUInt(name, reinterpret_cast<uint32>(def));
if(size == 2) *(uint16*)addr = (uint16)conf.GetUInt(name, reinterpret_cast<uint32>(def));
if(size == 4) *(uint32*)addr = (uint32)conf.GetUInt(name, reinterpret_cast<uint32>(def));
if(size == 8) *(uint64*)addr = (uint64)conf.GetUInt(name, reinterpret_cast<uint32>(def));
if(size == 1) *(uint8 *)addr = (uint8) conf.GetUInt(name, PtrToUint(def));
if(size == 2) *(uint16*)addr = (uint16)conf.GetUInt(name, PtrToUint(def));
if(size == 4) *(uint32*)addr = (uint32)conf.GetUInt(name, PtrToUint(def));
if(size == 8) *(uint64*)addr = (uint64)conf.GetUInt(name, PtrToUint(def));
break;
case CIT_STRING:
lstrcpyn((TCHAR*)addr, _tFromChar(conf.GetString(name, reinterpret_cast<const char*>(def))), size-1);
@ -409,7 +410,7 @@ struct ConfigItem
break;
case CIT_VKEY:
{
uint16 keyNum = (uint16)conf.GetUInt(name, reinterpret_cast<uint32>(def));
uint16 keyNum = (uint16)conf.GetUInt(name, PtrToUint(def));
const char* keyStr = conf.GetString(name);
if(keyStr)
{
@ -460,7 +461,7 @@ struct ConfigItem
if(strstr(modStr, "lt") || strstr(modStr, "LT")) modNum |= CUSTKEY_ALT_MASK;
}
if(!modNum && (!modStr || strcasecmp(modStr, "none")))
modNum = conf.GetUInt(name, reinterpret_cast<uint32>(def));
modNum = conf.GetUInt(name, PtrToUint(def));
if(size == 1) *(uint8 *)addr = (uint8) modNum;
if(size == 2) *(uint16*)addr = (uint16)modNum;
if(size == 4) *(uint32*)addr = (uint32)modNum;
@ -669,7 +670,6 @@ void WinPostLoad(ConfigFile& conf)
i = -1;
}
}
if(conf.Exists("Sound::Mono")) Settings.Stereo = !conf.GetBool("Sound::Mono"); // special case
ConfigFile::SetNiceAlignment(niceAlignment);
ConfigFile::SetShowComments(showComments);
@ -812,18 +812,15 @@ void WinRegisterConfigItems()
#undef CATEGORY
#define CATEGORY "Sound"
AddIntC("Sync", Settings.SoundSync, 1, "1 to sync emulation to sound output, 0 to disable.");
AddBool2("Stereo", Settings.Stereo, true);
AddBool("SixteenBitSound", Settings.SixteenBitSound, true);
AddUIntC("Rate", Settings.SoundPlaybackRate, 44100, "sound playback quality, in Hz");
AddUIntC("InputRate", Settings.SoundInputRate, 31950, "for each 'Input rate' samples generated by the SNES, 'Playback rate' samples will produced. If you experience crackling you can try to lower this setting.");
AddBoolC("ReverseStereo", Settings.ReverseStereo, false, "true to swap speaker outputs");
AddBoolC("Mute", GUI.Mute, false, "true to mute sound output (does not disable the sound CPU)");
AddBool("DynamicRateControl", Settings.DynamicRateControl, false);
AddBool("AutomaticInputRate", GUI.AutomaticInputRate, true);
AddIntC("InterpolationMethod", Settings.InterpolationMethod, 2, "0 = None, 1 = Linear, 2 = Gaussian (accurate), 3 = Cubic, 4 = Sinc");
#undef CATEGORY
#define CATEGORY "Sound\\Win"
AddUIntC("SoundDriver", GUI.SoundDriver, 4, "0=Snes9xDirectSound, 4=XAudio2 (recommended)");
AddUIntC("SoundDriver", GUI.SoundDriver, 4, "4=XAudio2 (recommended), 8=WaveOut");
AddUIntC("BufferSize", GUI.SoundBufferSize, 64, "sound buffer size in ms - determines the internal and output sound buffer sizes. actual mixing is done every SoundBufferSize/4 samples");
AddBoolC("MuteFrameAdvance", GUI.FAMute, false, "true to prevent Snes9x from outputting sound when the Frame Advance command is in use");
AddUIntC("VolumeRegular", GUI.VolumeRegular, 100, "volume during regular play (percentage between 0 and 100)");

View File

@ -1226,10 +1226,10 @@ void DoAVIOpen(const TCHAR* filename)
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = Settings.Stereo ? 2 : 1;
wfx.nChannels = 2;
wfx.nSamplesPerSec = Settings.SoundPlaybackRate;
wfx.nBlockAlign = (Settings.SixteenBitSound ? 2 : 1) * (Settings.Stereo ? 2 : 1);
wfx.wBitsPerSample = Settings.SixteenBitSound ? 16 : 8;
wfx.nBlockAlign = 2 * 2;
wfx.wBitsPerSample = 16;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
@ -1303,10 +1303,10 @@ void DoAVIVideoFrame()
const WAVEFORMATEX* pwfex = NULL;
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = Settings.Stereo ? 2 : 1;
wfx.nChannels = 2;
wfx.nSamplesPerSec = Settings.SoundPlaybackRate;
wfx.nBlockAlign = (Settings.SixteenBitSound ? 2 : 1) * (Settings.Stereo ? 2 : 1);
wfx.wBitsPerSample = Settings.SixteenBitSound ? 16 : 8;
wfx.nBlockAlign = 2 * 2;
wfx.wBitsPerSample = 16;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
if(avi_skip_frames != Settings.SkipFrames ||
@ -1327,7 +1327,7 @@ void DoAVIVideoFrame()
// generate sound
if(pwfex)
{
const int stereo_multiplier = (Settings.Stereo) ? 2 : 1;
const int stereo_multiplier = 2;
avi_sound_samples_error += avi_sound_samples_per_update;
int samples = (int) avi_sound_samples_error;

View File

@ -8,16 +8,16 @@
#include "../snes9x.h"
#include "../apu/apu.h"
#include "wsnes9x.h"
#include "CDirectSound.h"
#include "CXAudio2.h"
#include "CWaveOut.h"
#include "win32_sound.h"
#include "win32_display.h"
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
// available sound output methods
CDirectSound S9xDirectSound;
CXAudio2 S9xXAudio2;
CWaveOut S9xWaveOut;
// Interface used to access the sound output
IS9xSoundOutput *S9xSoundOutput = &S9xXAudio2;
@ -51,7 +51,7 @@ bool ReInitSound()
if(S9xSoundOutput)
S9xSoundOutput->DeInitSoundOutput();
return S9xInitSound(GUI.SoundBufferSize,0);
return S9xInitSound(0, 0);
}
void CloseSoundDevice() {
@ -70,16 +70,15 @@ bool8 S9xOpenSoundDevice ()
S9xSetSamplesAvailableCallback (NULL, NULL);
// point the interface to the correct output object
switch(GUI.SoundDriver) {
case WIN_SNES9X_DIRECT_SOUND_DRIVER:
S9xSoundOutput = &S9xDirectSound;
Settings.DynamicRateControl = false;
case WIN_WAVEOUT_DRIVER:
S9xSoundOutput = &S9xWaveOut;
break;
case WIN_XAUDIO2_SOUND_DRIVER:
S9xSoundOutput = &S9xXAudio2;
break;
default: // we default to DirectSound
GUI.SoundDriver = WIN_SNES9X_DIRECT_SOUND_DRIVER;
S9xSoundOutput = &S9xDirectSound;
default: // we default to WaveOut
GUI.SoundDriver = WIN_WAVEOUT_DRIVER;
S9xSoundOutput = &S9xWaveOut;
}
if(!S9xSoundOutput->InitSoundOutput())
return false;

View File

@ -87,7 +87,6 @@ extern SNPServer NPServer;
__int64 PCBase, PCFrameTime, PCFrameTimeNTSC, PCFrameTimePAL, PCStart, PCEnd;
DWORD PCStartTicks, PCEndTicks;
bool PCFrameTimeIsDefault = true;
INT_PTR CALLBACK DlgSoundConf(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK DlgInfoProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
@ -1980,12 +1979,12 @@ LRESULT CALLBACK WinProc(
GUI.SoundBufferSize = 176;
ReInitSound();
break;
case ID_SOUND_194MS:
GUI.SoundBufferSize = 194;
case ID_SOUND_192MS:
GUI.SoundBufferSize = 192;
ReInitSound();
break;
case ID_SOUND_210MS:
GUI.SoundBufferSize = 210;
case ID_SOUND_208MS:
GUI.SoundBufferSize = 208;
ReInitSound();
break;
@ -2005,17 +2004,6 @@ LRESULT CALLBACK WinProc(
GUI.Mute = !GUI.Mute;
break;
case ID_SOUND_STEREO:
Settings.Stereo = !Settings.Stereo;
ReInitSound();
break;
case ID_SOUND_REVERSE_STEREO:
Settings.ReverseStereo = !Settings.ReverseStereo;
break;
case ID_SOUND_16BIT:
Settings.SixteenBitSound = !Settings.SixteenBitSound;
ReInitSound();
break;
case ID_SOUND_SYNC:
Settings.SoundSync = !Settings.SoundSync;
S9xDisplayStateChange (WINPROC_SYNC_SND, Settings.SoundSync);
@ -3468,13 +3456,6 @@ int WINAPI WinMain(
{
ProcessInput();
// no sound sync when speed is not set to 100%
while(!S9xSyncSound()) {
ResetEvent(GUI.SoundSyncEvent);
if(!PCFrameTimeIsDefault || WaitForSingleObject(GUI.SoundSyncEvent,1000) != WAIT_OBJECT_0)
S9xClearSamples();
}
if(GUI.rewindBufferSize
#ifdef NETPLAY_SUPPORT
&&!Settings.NetPlay
@ -3758,8 +3739,6 @@ static void CheckMenuStates ()
if (Settings.SoundPlaybackRate == 0 || GUI.Mute)
mii.fState |= MFS_DISABLED;
SetMenuItemInfo (GUI.hMenu, ID_SOUND_16BIT, FALSE, &mii);
SetMenuItemInfo (GUI.hMenu, ID_SOUND_STEREO, FALSE, &mii);
SetMenuItemInfo (GUI.hMenu, ID_SOUND_SYNC, FALSE, &mii);
SetMenuItemInfo (GUI.hMenu, ID_SOUND_INTERPOLATED, FALSE, &mii);
@ -3774,13 +3753,8 @@ static void CheckMenuStates ()
SetMenuItemInfo (GUI.hMenu, ID_SOUND_144MS, FALSE, &mii);
SetMenuItemInfo (GUI.hMenu, ID_SOUND_160MS, FALSE, &mii);
SetMenuItemInfo (GUI.hMenu, ID_SOUND_176MS, FALSE, &mii);
SetMenuItemInfo (GUI.hMenu, ID_SOUND_194MS, FALSE, &mii);
SetMenuItemInfo (GUI.hMenu, ID_SOUND_210MS, FALSE, &mii);
if (!Settings.Stereo)
mii.fState |= MFS_DISABLED;
SetMenuItemInfo (GUI.hMenu, ID_SOUND_REVERSE_STEREO, FALSE, &mii);
SetMenuItemInfo (GUI.hMenu, ID_SOUND_192MS, FALSE, &mii);
SetMenuItemInfo (GUI.hMenu, ID_SOUND_208MS, FALSE, &mii);
mii.fState = MFS_CHECKED;
if (GUI.AVIOut)
@ -3816,23 +3790,14 @@ static void CheckMenuStates ()
case 144: id = ID_SOUND_144MS; break;
case 160: id = ID_SOUND_160MS; break;
case 176: id = ID_SOUND_176MS; break;
case 194: id = ID_SOUND_194MS; break;
case 210: id = ID_SOUND_210MS; break;
case 192: id = ID_SOUND_192MS; break;
case 208: id = ID_SOUND_208MS; break;
}
SetMenuItemInfo (GUI.hMenu, id, FALSE, &mii);
if (Settings.SixteenBitSound)
SetMenuItemInfo (GUI.hMenu, ID_SOUND_16BIT, FALSE, &mii);
if (Settings.Stereo)
SetMenuItemInfo (GUI.hMenu, ID_SOUND_STEREO, FALSE, &mii);
if (Settings.SoundSync)
SetMenuItemInfo (GUI.hMenu, ID_SOUND_SYNC, FALSE, &mii);
if (!Settings.Stereo)
mii.fState |= MFS_DISABLED;
if (Settings.ReverseStereo)
SetMenuItemInfo (GUI.hMenu, ID_SOUND_REVERSE_STEREO, FALSE, &mii);
#ifdef DEBUGGER
mii.fState = (CPU.Flags & TRACE_FLAG) ? MFS_CHECKED : MFS_UNCHECKED;
SetMenuItemInfo (GUI.hMenu, ID_DEBUG_TRACE, FALSE, &mii);
@ -3928,12 +3893,15 @@ static void ResetFrameTimer ()
{
QueryPerformanceCounter((LARGE_INTEGER*)&PCStart);
PCStartTicks = timeGetTime()*1000;
if (Settings.FrameTime == Settings.FrameTimeNTSC) PCFrameTime = PCFrameTimeNTSC;
else if (Settings.FrameTime == Settings.FrameTimePAL) PCFrameTime = PCFrameTimePAL;
else PCFrameTime = (__int64)((double)(PCBase * Settings.FrameTime) * .000001);
if (Settings.FrameTime == Settings.FrameTimeNTSC)
PCFrameTime = PCFrameTimeNTSC;
else if (Settings.FrameTime == Settings.FrameTimePAL)
PCFrameTime = PCFrameTimePAL;
else
PCFrameTime = (__int64)((double)(PCBase * Settings.FrameTime) * .000001);
// determines if we can do sound sync
PCFrameTimeIsDefault = Settings.PAL ? Settings.FrameTime == Settings.FrameTimePAL : Settings.FrameTime == Settings.FrameTimeNTSC;
GUI.AllowSoundSync = Settings.PAL ? Settings.FrameTime == Settings.FrameTimePAL : Settings.FrameTime == Settings.FrameTimeNTSC;
if (GUI.hFrameTimer)
timeKillEvent (GUI.hFrameTimer);
@ -4364,32 +4332,17 @@ INT_PTR CALLBACK DlgSoundConf(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
CreateToolTip(IDC_INRATEEDIT, hDlg, TEXT("For each 'Input rate' samples generated by the SNES, 'Playback rate' samples will produced. If you experience crackling you can try to lower this setting."));
CreateToolTip(IDC_INRATE, hDlg, TEXT("For each 'Input rate' samples generated by the SNES, 'Playback rate' samples will produced. If you experience crackling you can try to lower this setting."));
CreateToolTip(IDC_DYNRATECONTROL, hDlg, TEXT("Try to dynamically adjust the input rate to never overflow or underflow the sound buffer. Only works with XAudio2."));
CreateToolTip(IDC_DYNRATECONTROL, hDlg, TEXT("Try to dynamically adjust the input rate to never overflow or underflow the sound buffer."));
HWND output_dropdown = GetDlgItem(hDlg, IDC_OUTPUT_DEVICE);
UpdateAudioDeviceDropdown(output_dropdown);
ComboBox_SetCurSel(output_dropdown, FindAudioDeviceIndex(GUI.AudioDevice));
int pos;
pos = SendDlgItemMessage(hDlg, IDC_DRIVER, CB_INSERTSTRING, -1, (LPARAM)TEXT("Snes9x DirectSound"));
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_SETITEMDATA, pos, WIN_SNES9X_DIRECT_SOUND_DRIVER);
pos = SendDlgItemMessage(hDlg, IDC_DRIVER, CB_INSERTSTRING, -1, (LPARAM)TEXT("WaveOut"));
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_SETITEMDATA, pos, WIN_WAVEOUT_DRIVER);
pos = SendDlgItemMessage(hDlg, IDC_DRIVER, CB_INSERTSTRING, -1, (LPARAM)TEXT("XAudio2"));
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_SETITEMDATA, pos, WIN_XAUDIO2_SOUND_DRIVER);
#ifdef FMOD_SUPPORT
pos = SendDlgItemMessage(hDlg, IDC_DRIVER, CB_INSERTSTRING, -1, (LPARAM)TEXT("FMOD DirectSound"));
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_SETITEMDATA, pos, WIN_FMOD_DIRECT_SOUND_DRIVER);
pos = SendDlgItemMessage(hDlg, IDC_DRIVER, CB_INSERTSTRING, -1, (LPARAM)TEXT("FMOD Windows Multimedia"));
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_SETITEMDATA, pos, WIN_FMOD_WAVE_SOUND_DRIVER);
pos = SendDlgItemMessage(hDlg, IDC_DRIVER, CB_INSERTSTRING, -1, (LPARAM)TEXT("FMOD A3D"));
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_SETITEMDATA, pos, WIN_FMOD_A3D_SOUND_DRIVER);
#elif defined FMODEX_SUPPORT
pos = SendDlgItemMessage(hDlg, IDC_DRIVER, CB_INSERTSTRING, -1, (LPARAM)TEXT("FMOD Ex Default"));
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_SETITEMDATA, pos, WIN_FMODEX_DEFAULT_DRIVER);
pos = SendDlgItemMessage(hDlg, IDC_DRIVER, CB_INSERTSTRING, -1, (LPARAM)TEXT("FMOD Ex ASIO"));
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_SETITEMDATA, pos, WIN_FMODEX_ASIO_DRIVER);
pos = SendDlgItemMessage(hDlg, IDC_DRIVER, CB_INSERTSTRING, -1, (LPARAM)TEXT("FMOD Ex OpenAL"));
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_SETITEMDATA, pos, WIN_FMODEX_OPENAL_DRIVER);
#endif
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_SETCURSEL, 0, 0);
for (pos = 0; pos < SendDlgItemMessage(hDlg, IDC_DRIVER, CB_GETCOUNT, 0, 0); pos++) {
if (SendDlgItemMessage(hDlg, IDC_DRIVER, CB_GETITEMDATA, pos, 0) == GUI.SoundDriver) {
@ -4404,7 +4357,6 @@ INT_PTR CALLBACK DlgSoundConf(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
GUI.AutomaticInputRate = false;
}
EnableWindow(GetDlgItem(hDlg, IDC_DYNRATECONTROL), GUI.SoundDriver == WIN_XAUDIO2_SOUND_DRIVER);
EnableWindow(GetDlgItem(hDlg, IDC_INRATEEDIT), !GUI.AutomaticInputRate);
EnableWindow(GetDlgItem(hDlg, IDC_INRATE), !GUI.AutomaticInputRate);
@ -4468,18 +4420,13 @@ INT_PTR CALLBACK DlgSoundConf(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
SendDlgItemMessage(hDlg, IDC_BUFLEN, CB_INSERTSTRING, 8, (LPARAM)TEXT("144 ms"));
SendDlgItemMessage(hDlg, IDC_BUFLEN, CB_INSERTSTRING, 9, (LPARAM)TEXT("160 ms"));
SendDlgItemMessage(hDlg, IDC_BUFLEN, CB_INSERTSTRING, 10, (LPARAM)TEXT("176 ms"));
SendDlgItemMessage(hDlg, IDC_BUFLEN, CB_INSERTSTRING, 11, (LPARAM)TEXT("194 ms"));
SendDlgItemMessage(hDlg, IDC_BUFLEN, CB_INSERTSTRING, 12, (LPARAM)TEXT("210 ms"));
SendDlgItemMessage(hDlg, IDC_BUFLEN, CB_INSERTSTRING, 11, (LPARAM)TEXT("192 ms"));
SendDlgItemMessage(hDlg, IDC_BUFLEN, CB_INSERTSTRING, 12, (LPARAM)TEXT("208 ms"));
SendDlgItemMessage(hDlg, IDC_BUFLEN, CB_SETCURSEL, ((GUI.SoundBufferSize / 16) - 1), 0);
if (Settings.DynamicRateControl)
SendDlgItemMessage(hDlg, IDC_DYNRATECONTROL, BM_SETCHECK, BST_CHECKED, 0);
if (Settings.Stereo)
SendDlgItemMessage(hDlg, IDC_STEREO, BM_SETCHECK, BST_CHECKED, 0);
else EnableWindow(GetDlgItem(hDlg, IDC_REV_STEREO), FALSE);
if (Settings.ReverseStereo)
SendDlgItemMessage(hDlg, IDC_REV_STEREO, BM_SETCHECK, BST_CHECKED, 0);
if (GUI.Mute)
SendDlgItemMessage(hDlg, IDC_MUTE, BM_SETCHECK, BST_CHECKED, 0);
@ -4539,8 +4486,6 @@ INT_PTR CALLBACK DlgSoundConf(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_GETCURSEL, 0,0),0);
Settings.DynamicRateControl=IsDlgButtonChecked(hDlg, IDC_DYNRATECONTROL);
Settings.SoundSync=IsDlgButtonChecked(hDlg, IDC_SYNC_TO_SOUND_CPU);
Settings.Stereo=IsDlgButtonChecked(hDlg, IDC_STEREO);
Settings.ReverseStereo=IsDlgButtonChecked(hDlg, IDC_REV_STEREO);
GUI.Mute=IsDlgButtonChecked(hDlg, IDC_MUTE);
GUI.FAMute=IsDlgButtonChecked(hDlg, IDC_FAMT)!=0;
@ -4619,16 +4564,15 @@ INT_PTR CALLBACK DlgSoundConf(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
case IDC_DRIVER:
if(CBN_SELCHANGE==HIWORD(wParam))
{
int driver=SendDlgItemMessage(hDlg, IDC_DRIVER, CB_GETITEMDATA,
int driver = SendDlgItemMessage(hDlg, IDC_DRIVER, CB_GETITEMDATA,
SendDlgItemMessage(hDlg, IDC_DRIVER, CB_GETCURSEL, 0,0),0);
EnableWindow(GetDlgItem(hDlg, IDC_DYNRATECONTROL), FALSE);
switch(driver) {
case WIN_SNES9X_DIRECT_SOUND_DRIVER:
case WIN_WAVEOUT_DRIVER:
SendDlgItemMessage(hDlg,IDC_BUFLEN,CB_SETCURSEL,3,0);
break;
case WIN_XAUDIO2_SOUND_DRIVER:
SendDlgItemMessage(hDlg,IDC_BUFLEN,CB_SETCURSEL,3,0);
EnableWindow(GetDlgItem(hDlg, IDC_DYNRATECONTROL), TRUE);
break;
default:
SendDlgItemMessage(hDlg,IDC_BUFLEN,CB_SETCURSEL,7,0);
@ -4646,20 +4590,6 @@ INT_PTR CALLBACK DlgSoundConf(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
return true;
}
else return false;
case IDC_STEREO:
{
if(BN_CLICKED==HIWORD(wParam)||BN_DBLCLK==HIWORD(wParam))
{
if(IsDlgButtonChecked(hDlg,IDC_STEREO))
{
EnableWindow(GetDlgItem(hDlg, IDC_REV_STEREO), TRUE);
}
else EnableWindow(GetDlgItem(hDlg, IDC_REV_STEREO), FALSE);
return true;
}
else return false;
}
case IDC_INRATEEDIT:
if(HIWORD(wParam)==EN_UPDATE) {
Edit_GetText(GetDlgItem(hDlg,IDC_INRATEEDIT),valTxt,10);

View File

@ -217,6 +217,7 @@ struct sGUI {
CRITICAL_SECTION SoundCritSect;
HANDLE SoundSyncEvent;
TCHAR AudioDevice[MAX_AUDIO_NAME_LENGTH];
bool AllowSoundSync;
TCHAR RomDir [_MAX_PATH];
TCHAR ScreensDir [_MAX_PATH];
@ -422,7 +423,8 @@ enum
WIN_XAUDIO2_SOUND_DRIVER,
WIN_FMODEX_DEFAULT_DRIVER,
WIN_FMODEX_ASIO_DRIVER,
WIN_FMODEX_OPENAL_DRIVER
WIN_FMODEX_OPENAL_DRIVER,
WIN_WAVEOUT_DRIVER
};
#define S9X_REG_KEY_BASE MY_REG_KEY