mirror of https://github.com/snes9xgit/snes9x.git
commit
1ba69b0d9c
843
apu/apu.cpp
843
apu/apu.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#define __SNES_HPP
|
||||
|
||||
#include "../../../snes9x.h"
|
||||
#include "../../resampler.h"
|
||||
#include "../../../msu1.h"
|
||||
|
||||
#define SNES9X
|
||||
|
||||
|
|
|
@ -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 */
|
233
apu/resampler.h
233
apu/resampler.h
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
¶m,
|
||||
Settings.SoundPlaybackRate,
|
||||
0,
|
||||
paNoFlag,
|
||||
NULL,
|
||||
NULL);
|
||||
err = Pa_OpenStream(&audio_stream,
|
||||
NULL,
|
||||
¶m,
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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>
|
||||
|
|
30
msu1.cpp
30
msu1.cpp
|
@ -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
3
msu1.h
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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)");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue