Reformat all the things. Have fun with merge conflicts.

This commit is contained in:
Pierre Bourdon 2016-06-24 10:43:46 +02:00
parent 2115e8a4a6
commit 3570c7f03a
1116 changed files with 187405 additions and 180344 deletions

File diff suppressed because it is too large Load Diff

View File

@ -9,249 +9,249 @@
namespace ButtonManager namespace ButtonManager
{ {
enum ButtonType enum ButtonType
{ {
// GC // GC
BUTTON_A = 0, BUTTON_A = 0,
BUTTON_B = 1, BUTTON_B = 1,
BUTTON_START = 2, BUTTON_START = 2,
BUTTON_X = 3, BUTTON_X = 3,
BUTTON_Y = 4, BUTTON_Y = 4,
BUTTON_Z = 5, BUTTON_Z = 5,
BUTTON_UP = 6, BUTTON_UP = 6,
BUTTON_DOWN = 7, BUTTON_DOWN = 7,
BUTTON_LEFT = 8, BUTTON_LEFT = 8,
BUTTON_RIGHT = 9, BUTTON_RIGHT = 9,
STICK_MAIN = 10, // Used on Java Side STICK_MAIN = 10, // Used on Java Side
STICK_MAIN_UP = 11, STICK_MAIN_UP = 11,
STICK_MAIN_DOWN = 12, STICK_MAIN_DOWN = 12,
STICK_MAIN_LEFT = 13, STICK_MAIN_LEFT = 13,
STICK_MAIN_RIGHT = 14, STICK_MAIN_RIGHT = 14,
STICK_C = 15, // Used on Java Side STICK_C = 15, // Used on Java Side
STICK_C_UP = 16, STICK_C_UP = 16,
STICK_C_DOWN = 17, STICK_C_DOWN = 17,
STICK_C_LEFT = 18, STICK_C_LEFT = 18,
STICK_C_RIGHT = 19, STICK_C_RIGHT = 19,
TRIGGER_L = 20, TRIGGER_L = 20,
TRIGGER_R = 21, TRIGGER_R = 21,
// Wiimote // Wiimote
WIIMOTE_BUTTON_A = 100, WIIMOTE_BUTTON_A = 100,
WIIMOTE_BUTTON_B = 101, WIIMOTE_BUTTON_B = 101,
WIIMOTE_BUTTON_MINUS = 102, WIIMOTE_BUTTON_MINUS = 102,
WIIMOTE_BUTTON_PLUS = 103, WIIMOTE_BUTTON_PLUS = 103,
WIIMOTE_BUTTON_HOME = 104, WIIMOTE_BUTTON_HOME = 104,
WIIMOTE_BUTTON_1 = 105, WIIMOTE_BUTTON_1 = 105,
WIIMOTE_BUTTON_2 = 106, WIIMOTE_BUTTON_2 = 106,
WIIMOTE_UP = 107, WIIMOTE_UP = 107,
WIIMOTE_DOWN = 108, WIIMOTE_DOWN = 108,
WIIMOTE_LEFT = 109, WIIMOTE_LEFT = 109,
WIIMOTE_RIGHT = 110, WIIMOTE_RIGHT = 110,
WIIMOTE_IR = 111, // To Be Used on Java Side WIIMOTE_IR = 111, // To Be Used on Java Side
WIIMOTE_IR_UP = 112, WIIMOTE_IR_UP = 112,
WIIMOTE_IR_DOWN = 113, WIIMOTE_IR_DOWN = 113,
WIIMOTE_IR_LEFT = 114, WIIMOTE_IR_LEFT = 114,
WIIMOTE_IR_RIGHT = 115, WIIMOTE_IR_RIGHT = 115,
WIIMOTE_IR_FORWARD = 116, WIIMOTE_IR_FORWARD = 116,
WIIMOTE_IR_BACKWARD = 117, WIIMOTE_IR_BACKWARD = 117,
WIIMOTE_IR_HIDE = 118, WIIMOTE_IR_HIDE = 118,
WIIMOTE_SWING = 119, // To Be Used on Java Side WIIMOTE_SWING = 119, // To Be Used on Java Side
WIIMOTE_SWING_UP = 120, WIIMOTE_SWING_UP = 120,
WIIMOTE_SWING_DOWN = 121, WIIMOTE_SWING_DOWN = 121,
WIIMOTE_SWING_LEFT = 122, WIIMOTE_SWING_LEFT = 122,
WIIMOTE_SWING_RIGHT = 123, WIIMOTE_SWING_RIGHT = 123,
WIIMOTE_SWING_FORWARD = 124, WIIMOTE_SWING_FORWARD = 124,
WIIMOTE_SWING_BACKWARD = 125, WIIMOTE_SWING_BACKWARD = 125,
WIIMOTE_TILT = 126, // To Be Used on Java Side WIIMOTE_TILT = 126, // To Be Used on Java Side
WIIMOTE_TILT_FORWARD = 127, WIIMOTE_TILT_FORWARD = 127,
WIIMOTE_TILT_BACKWARD = 128, WIIMOTE_TILT_BACKWARD = 128,
WIIMOTE_TILT_LEFT = 129, WIIMOTE_TILT_LEFT = 129,
WIIMOTE_TILT_RIGHT = 130, WIIMOTE_TILT_RIGHT = 130,
WIIMOTE_TILT_MODIFIER = 131, WIIMOTE_TILT_MODIFIER = 131,
WIIMOTE_SHAKE_X = 132, WIIMOTE_SHAKE_X = 132,
WIIMOTE_SHAKE_Y = 133, WIIMOTE_SHAKE_Y = 133,
WIIMOTE_SHAKE_Z = 134, WIIMOTE_SHAKE_Z = 134,
//Nunchuk // Nunchuk
NUNCHUK_BUTTON_C = 200, NUNCHUK_BUTTON_C = 200,
NUNCHUK_BUTTON_Z = 201, NUNCHUK_BUTTON_Z = 201,
NUNCHUK_STICK = 202, // To Be Used on Java Side NUNCHUK_STICK = 202, // To Be Used on Java Side
NUNCHUK_STICK_UP = 203, NUNCHUK_STICK_UP = 203,
NUNCHUK_STICK_DOWN = 204, NUNCHUK_STICK_DOWN = 204,
NUNCHUK_STICK_LEFT = 205, NUNCHUK_STICK_LEFT = 205,
NUNCHUK_STICK_RIGHT = 206, NUNCHUK_STICK_RIGHT = 206,
NUNCHUK_SWING = 207, // To Be Used on Java Side NUNCHUK_SWING = 207, // To Be Used on Java Side
NUNCHUK_SWING_UP = 208, NUNCHUK_SWING_UP = 208,
NUNCHUK_SWING_DOWN = 209, NUNCHUK_SWING_DOWN = 209,
NUNCHUK_SWING_LEFT = 210, NUNCHUK_SWING_LEFT = 210,
NUNCHUK_SWING_RIGHT = 211, NUNCHUK_SWING_RIGHT = 211,
NUNCHUK_SWING_FORWARD = 212, NUNCHUK_SWING_FORWARD = 212,
NUNCHUK_SWING_BACKWARD = 213, NUNCHUK_SWING_BACKWARD = 213,
NUNCHUK_TILT = 214, // To Be Used on Java Side NUNCHUK_TILT = 214, // To Be Used on Java Side
NUNCHUK_TILT_FORWARD = 215, NUNCHUK_TILT_FORWARD = 215,
NUNCHUK_TILT_BACKWARD = 216, NUNCHUK_TILT_BACKWARD = 216,
NUNCHUK_TILT_LEFT = 217, NUNCHUK_TILT_LEFT = 217,
NUNCHUK_TILT_RIGHT = 218, NUNCHUK_TILT_RIGHT = 218,
NUNCHUK_TILT_MODIFIER = 219, NUNCHUK_TILT_MODIFIER = 219,
NUNCHUK_SHAKE_X = 220, NUNCHUK_SHAKE_X = 220,
NUNCHUK_SHAKE_Y = 221, NUNCHUK_SHAKE_Y = 221,
NUNCHUK_SHAKE_Z = 222, NUNCHUK_SHAKE_Z = 222,
//Classic // Classic
CLASSIC_BUTTON_A = 300, CLASSIC_BUTTON_A = 300,
CLASSIC_BUTTON_B = 301, CLASSIC_BUTTON_B = 301,
CLASSIC_BUTTON_X = 302, CLASSIC_BUTTON_X = 302,
CLASSIC_BUTTON_Y = 303, CLASSIC_BUTTON_Y = 303,
CLASSIC_BUTTON_MINUS = 304, CLASSIC_BUTTON_MINUS = 304,
CLASSIC_BUTTON_PLUS = 305, CLASSIC_BUTTON_PLUS = 305,
CLASSIC_BUTTON_HOME = 306, CLASSIC_BUTTON_HOME = 306,
CLASSIC_BUTTON_ZL = 307, CLASSIC_BUTTON_ZL = 307,
CLASSIC_BUTTON_ZR = 308, CLASSIC_BUTTON_ZR = 308,
CLASSIC_DPAD_UP = 309, CLASSIC_DPAD_UP = 309,
CLASSIC_DPAD_DOWN = 310, CLASSIC_DPAD_DOWN = 310,
CLASSIC_DPAD_LEFT = 311, CLASSIC_DPAD_LEFT = 311,
CLASSIC_DPAD_RIGHT = 312, CLASSIC_DPAD_RIGHT = 312,
CLASSIC_STICK_LEFT = 313, // To Be Used on Java Side CLASSIC_STICK_LEFT = 313, // To Be Used on Java Side
CLASSIC_STICK_LEFT_UP = 314, CLASSIC_STICK_LEFT_UP = 314,
CLASSIC_STICK_LEFT_DOWN = 315, CLASSIC_STICK_LEFT_DOWN = 315,
CLASSIC_STICK_LEFT_LEFT = 316, CLASSIC_STICK_LEFT_LEFT = 316,
CLASSIC_STICK_LEFT_RIGHT = 317, CLASSIC_STICK_LEFT_RIGHT = 317,
CLASSIC_STICK_RIGHT = 318, // To Be Used on Java Side CLASSIC_STICK_RIGHT = 318, // To Be Used on Java Side
CLASSIC_STICK_RIGHT_UP = 319, CLASSIC_STICK_RIGHT_UP = 319,
CLASSIC_STICK_RIGHT_DOWN = 320, CLASSIC_STICK_RIGHT_DOWN = 320,
CLASSIC_STICK_RIGHT_LEFT = 321, CLASSIC_STICK_RIGHT_LEFT = 321,
CLASSIC_STICK_RIGHT_RIGHT = 322, CLASSIC_STICK_RIGHT_RIGHT = 322,
CLASSIC_TRIGGER_L = 323, CLASSIC_TRIGGER_L = 323,
CLASSIC_TRIGGER_R = 324, CLASSIC_TRIGGER_R = 324,
//Guitar // Guitar
GUITAR_BUTTON_MINUS = 400, GUITAR_BUTTON_MINUS = 400,
GUITAR_BUTTON_PLUS = 401, GUITAR_BUTTON_PLUS = 401,
GUITAR_FRET_GREEN = 402, GUITAR_FRET_GREEN = 402,
GUITAR_FRET_RED = 403, GUITAR_FRET_RED = 403,
GUITAR_FRET_YELLOW = 404, GUITAR_FRET_YELLOW = 404,
GUITAR_FRET_BLUE = 405, GUITAR_FRET_BLUE = 405,
GUITAR_FRET_ORANGE = 406, GUITAR_FRET_ORANGE = 406,
GUITAR_STRUM_UP = 407, GUITAR_STRUM_UP = 407,
GUITAR_STRUM_DOWN = 408, GUITAR_STRUM_DOWN = 408,
GUITAR_STICK = 409, // To Be Used on Java Side GUITAR_STICK = 409, // To Be Used on Java Side
GUITAR_STICK_UP = 410, GUITAR_STICK_UP = 410,
GUITAR_STICK_DOWN = 411, GUITAR_STICK_DOWN = 411,
GUITAR_STICK_LEFT = 412, GUITAR_STICK_LEFT = 412,
GUITAR_STICK_RIGHT = 413, GUITAR_STICK_RIGHT = 413,
GUITAR_WHAMMY_BAR = 414, GUITAR_WHAMMY_BAR = 414,
//Drums // Drums
DRUMS_BUTTON_MINUS = 500, DRUMS_BUTTON_MINUS = 500,
DRUMS_BUTTON_PLUS = 501, DRUMS_BUTTON_PLUS = 501,
DRUMS_PAD_RED = 502, DRUMS_PAD_RED = 502,
DRUMS_PAD_YELLOW = 503, DRUMS_PAD_YELLOW = 503,
DRUMS_PAD_BLUE = 504, DRUMS_PAD_BLUE = 504,
DRUMS_PAD_GREEN = 505, DRUMS_PAD_GREEN = 505,
DRUMS_PAD_ORANGE = 506, DRUMS_PAD_ORANGE = 506,
DRUMS_PAD_BASS = 507, DRUMS_PAD_BASS = 507,
DRUMS_STICK = 508, // To Be Used on Java Side DRUMS_STICK = 508, // To Be Used on Java Side
DRUMS_STICK_UP = 509, DRUMS_STICK_UP = 509,
DRUMS_STICK_DOWN = 510, DRUMS_STICK_DOWN = 510,
DRUMS_STICK_LEFT = 511, DRUMS_STICK_LEFT = 511,
DRUMS_STICK_RIGHT = 512, DRUMS_STICK_RIGHT = 512,
//Turntable // Turntable
TURNTABLE_BUTTON_GREEN_LEFT = 600, TURNTABLE_BUTTON_GREEN_LEFT = 600,
TURNTABLE_BUTTON_RED_LEFT = 601, TURNTABLE_BUTTON_RED_LEFT = 601,
TURNTABLE_BUTTON_BLUE_LEFT = 602, TURNTABLE_BUTTON_BLUE_LEFT = 602,
TURNTABLE_BUTTON_GREEN_RIGHT = 603, TURNTABLE_BUTTON_GREEN_RIGHT = 603,
TURNTABLE_BUTTON_RED_RIGHT = 604, TURNTABLE_BUTTON_RED_RIGHT = 604,
TURNTABLE_BUTTON_BLUE_RIGHT = 605, TURNTABLE_BUTTON_BLUE_RIGHT = 605,
TURNTABLE_BUTTON_MINUS = 606, TURNTABLE_BUTTON_MINUS = 606,
TURNTABLE_BUTTON_PLUS = 607, TURNTABLE_BUTTON_PLUS = 607,
TURNTABLE_BUTTON_HOME = 608, TURNTABLE_BUTTON_HOME = 608,
TURNTABLE_BUTTON_EUPHORIA = 609, TURNTABLE_BUTTON_EUPHORIA = 609,
TURNTABLE_TABLE_LEFT = 610, // To Be Used on Java Side TURNTABLE_TABLE_LEFT = 610, // To Be Used on Java Side
TURNTABLE_TABLE_LEFT_LEFT = 611, TURNTABLE_TABLE_LEFT_LEFT = 611,
TURNTABLE_TABLE_LEFT_RIGHT = 612, TURNTABLE_TABLE_LEFT_RIGHT = 612,
TURNTABLE_TABLE_RIGHT = 613, // To Be Used on Java Side TURNTABLE_TABLE_RIGHT = 613, // To Be Used on Java Side
TURNTABLE_TABLE_RIGHT_LEFT = 614, TURNTABLE_TABLE_RIGHT_LEFT = 614,
TURNTABLE_TABLE_RIGHT_RIGHT = 615, TURNTABLE_TABLE_RIGHT_RIGHT = 615,
TURNTABLE_STICK = 616, // To Be Used on Java Side TURNTABLE_STICK = 616, // To Be Used on Java Side
TURNTABLE_STICK_UP = 617, TURNTABLE_STICK_UP = 617,
TURNTABLE_STICK_DOWN = 618, TURNTABLE_STICK_DOWN = 618,
TURNTABLE_STICK_LEFT = 619, TURNTABLE_STICK_LEFT = 619,
TURNTABLE_STICK_RIGHT = 620, TURNTABLE_STICK_RIGHT = 620,
TURNTABLE_EFFECT_DIAL = 621, TURNTABLE_EFFECT_DIAL = 621,
TURNTABLE_CROSSFADE = 622, // To Be Used on Java Side TURNTABLE_CROSSFADE = 622, // To Be Used on Java Side
TURNTABLE_CROSSFADE_LEFT = 623, TURNTABLE_CROSSFADE_LEFT = 623,
TURNTABLE_CROSSFADE_RIGHT = 624, TURNTABLE_CROSSFADE_RIGHT = 624,
}; };
enum ButtonState enum ButtonState
{ {
BUTTON_RELEASED = 0, BUTTON_RELEASED = 0,
BUTTON_PRESSED = 1 BUTTON_PRESSED = 1
}; };
enum BindType enum BindType
{ {
BIND_BUTTON = 0, BIND_BUTTON = 0,
BIND_AXIS BIND_AXIS
}; };
class Button class Button
{ {
private: private:
ButtonState m_state; ButtonState m_state;
public:
Button() : m_state(BUTTON_RELEASED) {}
void SetState(ButtonState state) { m_state = state; }
bool Pressed() { return m_state == BUTTON_PRESSED; }
~Button() {} public:
}; Button() : m_state(BUTTON_RELEASED) {}
class Axis void SetState(ButtonState state) { m_state = state; }
{ bool Pressed() { return m_state == BUTTON_PRESSED; }
private: ~Button() {}
float m_value; };
public: class Axis
Axis() : m_value(0.0f) {} {
void SetValue(float value) { m_value = value; } private:
float AxisValue() { return m_value; } float m_value;
~Axis() {} public:
}; Axis() : m_value(0.0f) {}
void SetValue(float value) { m_value = value; }
float AxisValue() { return m_value; }
~Axis() {}
};
struct sBind struct sBind
{ {
const int _padID; const int _padID;
const ButtonType _buttontype; const ButtonType _buttontype;
const BindType _bindtype; const BindType _bindtype;
const int _bind; const int _bind;
const float _neg; const float _neg;
sBind(int padID, ButtonType buttontype, BindType bindtype, int bind, float neg) sBind(int padID, ButtonType buttontype, BindType bindtype, int bind, float neg)
: _padID(padID), _buttontype(buttontype), _bindtype(bindtype), _bind(bind), _neg(neg) : _padID(padID), _buttontype(buttontype), _bindtype(bindtype), _bind(bind), _neg(neg)
{} {
}; }
};
class InputDevice
{
private:
const std::string _dev;
std::map<ButtonType, bool> _buttons;
std::map<ButtonType, float> _axises;
class InputDevice // Key is padID and ButtonType
{ std::map<std::pair<int, ButtonType>, sBind*> _inputbinds;
private:
const std::string _dev;
std::map<ButtonType, bool> _buttons;
std::map<ButtonType, float> _axises;
// Key is padID and ButtonType public:
std::map<std::pair<int, ButtonType>, sBind*> _inputbinds; InputDevice(std::string dev) : _dev(dev) {}
public: ~InputDevice()
InputDevice(std::string dev) {
: _dev(dev) {} for (const auto& bind : _inputbinds)
~InputDevice() delete bind.second;
{ _inputbinds.clear();
for (const auto& bind : _inputbinds) }
delete bind.second; void AddBind(sBind* bind) { _inputbinds[std::make_pair(bind->_padID, bind->_buttontype)] = bind; }
_inputbinds.clear(); bool PressEvent(int button, int action);
} void AxisEvent(int axis, float value);
void AddBind(sBind* bind) { _inputbinds[std::make_pair(bind->_padID, bind->_buttontype)] = bind; } bool ButtonValue(int padID, ButtonType button);
bool PressEvent(int button, int action); float AxisValue(int padID, ButtonType axis);
void AxisEvent(int axis, float value); };
bool ButtonValue(int padID, ButtonType button);
float AxisValue(int padID, ButtonType axis);
};
void Init(); void Init();
bool GetButtonPressed(int padID, ButtonType button); bool GetButtonPressed(int padID, ButtonType button);
float GetAxisValue(int padID, ButtonType axis); float GetAxisValue(int padID, ButtonType axis);
bool GamepadEvent(const std::string& dev, int button, int action); bool GamepadEvent(const std::string& dev, int button, int action);
void GamepadAxisEvent(const std::string& dev, int axis, float value); void GamepadAxisEvent(const std::string& dev, int axis, float value);
void Shutdown(); void Shutdown();
} }

File diff suppressed because it is too large Load Diff

View File

@ -6,77 +6,77 @@
#include "AudioCommon/AOSoundStream.h" #include "AudioCommon/AOSoundStream.h"
#include "AudioCommon/Mixer.h" #include "AudioCommon/Mixer.h"
#include "Common/MsgHandler.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#if defined(HAVE_AO) && HAVE_AO #if defined(HAVE_AO) && HAVE_AO
void AOSound::SoundLoop() void AOSound::SoundLoop()
{ {
Common::SetCurrentThreadName("Audio thread - ao"); Common::SetCurrentThreadName("Audio thread - ao");
uint_32 numBytesToRender = 256; uint_32 numBytesToRender = 256;
ao_initialize(); ao_initialize();
default_driver = ao_default_driver_id(); default_driver = ao_default_driver_id();
format.bits = 16; format.bits = 16;
format.channels = 2; format.channels = 2;
format.rate = m_mixer->GetSampleRate(); format.rate = m_mixer->GetSampleRate();
format.byte_format = AO_FMT_LITTLE; format.byte_format = AO_FMT_LITTLE;
device = ao_open_live(default_driver, &format, nullptr /* no options */); device = ao_open_live(default_driver, &format, nullptr /* no options */);
if (!device) if (!device)
{ {
PanicAlertT("AudioCommon: Error opening AO device.\n"); PanicAlertT("AudioCommon: Error opening AO device.\n");
ao_shutdown(); ao_shutdown();
Stop(); Stop();
return; return;
} }
buf_size = format.bits/8 * format.channels * format.rate; buf_size = format.bits / 8 * format.channels * format.rate;
while (m_run_thread.load()) while (m_run_thread.load())
{ {
m_mixer->Mix(realtimeBuffer, numBytesToRender >> 2); m_mixer->Mix(realtimeBuffer, numBytesToRender >> 2);
{ {
std::lock_guard<std::mutex> lk(soundCriticalSection); std::lock_guard<std::mutex> lk(soundCriticalSection);
ao_play(device, (char*)realtimeBuffer, numBytesToRender); ao_play(device, (char*)realtimeBuffer, numBytesToRender);
} }
soundSyncEvent.Wait(); soundSyncEvent.Wait();
} }
} }
bool AOSound::Start() bool AOSound::Start()
{ {
m_run_thread.store(true); m_run_thread.store(true);
memset(realtimeBuffer, 0, sizeof(realtimeBuffer)); memset(realtimeBuffer, 0, sizeof(realtimeBuffer));
thread = std::thread(&AOSound::SoundLoop, this); thread = std::thread(&AOSound::SoundLoop, this);
return true; return true;
} }
void AOSound::Update() void AOSound::Update()
{ {
soundSyncEvent.Set(); soundSyncEvent.Set();
} }
void AOSound::Stop() void AOSound::Stop()
{ {
m_run_thread.store(false); m_run_thread.store(false);
soundSyncEvent.Set(); soundSyncEvent.Set();
{ {
std::lock_guard<std::mutex> lk(soundCriticalSection); std::lock_guard<std::mutex> lk(soundCriticalSection);
thread.join(); thread.join();
if (device) if (device)
ao_close(device); ao_close(device);
ao_shutdown(); ao_shutdown();
device = nullptr; device = nullptr;
} }
} }
#endif #endif

View File

@ -19,29 +19,25 @@
class AOSound final : public SoundStream class AOSound final : public SoundStream
{ {
#if defined(HAVE_AO) && HAVE_AO #if defined(HAVE_AO) && HAVE_AO
std::thread thread; std::thread thread;
std::atomic<bool> m_run_thread; std::atomic<bool> m_run_thread;
std::mutex soundCriticalSection; std::mutex soundCriticalSection;
Common::Event soundSyncEvent; Common::Event soundSyncEvent;
int buf_size; int buf_size;
ao_device *device; ao_device* device;
ao_sample_format format; ao_sample_format format;
int default_driver; int default_driver;
short realtimeBuffer[1024 * 1024]; short realtimeBuffer[1024 * 1024];
public: public:
bool Start() override; bool Start() override;
void SoundLoop() override; void SoundLoop() override;
void Stop() override; void Stop() override;
void Update() override; void Update() override;
static bool isValid()
{
return true;
}
static bool isValid() { return true; }
#endif #endif
}; };

View File

@ -6,230 +6,229 @@
#include "AudioCommon/AlsaSoundStream.h" #include "AudioCommon/AlsaSoundStream.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Thread.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/Thread.h"
AlsaSound::AlsaSound() AlsaSound::AlsaSound()
: m_thread_status(ALSAThreadStatus::STOPPED) : m_thread_status(ALSAThreadStatus::STOPPED), handle(nullptr),
, handle(nullptr) frames_to_deliver(FRAME_COUNT_MIN)
, frames_to_deliver(FRAME_COUNT_MIN)
{ {
} }
bool AlsaSound::Start() bool AlsaSound::Start()
{ {
m_thread_status.store(ALSAThreadStatus::RUNNING); m_thread_status.store(ALSAThreadStatus::RUNNING);
if (!AlsaInit()) if (!AlsaInit())
{ {
m_thread_status.store(ALSAThreadStatus::STOPPED); m_thread_status.store(ALSAThreadStatus::STOPPED);
return false; return false;
} }
thread = std::thread(&AlsaSound::SoundLoop, this); thread = std::thread(&AlsaSound::SoundLoop, this);
return true; return true;
} }
void AlsaSound::Stop() void AlsaSound::Stop()
{ {
m_thread_status.store(ALSAThreadStatus::STOPPING); m_thread_status.store(ALSAThreadStatus::STOPPING);
//Give the opportunity to the audio thread // Give the opportunity to the audio thread
//to realize we are stopping the emulation // to realize we are stopping the emulation
cv.notify_one(); cv.notify_one();
thread.join(); thread.join();
} }
void AlsaSound::Update() void AlsaSound::Update()
{ {
// don't need to do anything here. // don't need to do anything here.
} }
// Called on audio thread. // Called on audio thread.
void AlsaSound::SoundLoop() void AlsaSound::SoundLoop()
{ {
Common::SetCurrentThreadName("Audio thread - alsa"); Common::SetCurrentThreadName("Audio thread - alsa");
while (m_thread_status.load() != ALSAThreadStatus::STOPPING) while (m_thread_status.load() != ALSAThreadStatus::STOPPING)
{ {
while (m_thread_status.load() == ALSAThreadStatus::RUNNING) while (m_thread_status.load() == ALSAThreadStatus::RUNNING)
{ {
m_mixer->Mix(mix_buffer, frames_to_deliver); m_mixer->Mix(mix_buffer, frames_to_deliver);
int rc = snd_pcm_writei(handle, mix_buffer, frames_to_deliver); int rc = snd_pcm_writei(handle, mix_buffer, frames_to_deliver);
if (rc == -EPIPE) if (rc == -EPIPE)
{ {
// Underrun // Underrun
snd_pcm_prepare(handle); snd_pcm_prepare(handle);
} }
else if (rc < 0) else if (rc < 0)
{ {
ERROR_LOG(AUDIO, "writei fail: %s", snd_strerror(rc)); ERROR_LOG(AUDIO, "writei fail: %s", snd_strerror(rc));
} }
} }
if (m_thread_status.load() == ALSAThreadStatus::PAUSED) if (m_thread_status.load() == ALSAThreadStatus::PAUSED)
{ {
snd_pcm_drop(handle); // Stop sound output snd_pcm_drop(handle); // Stop sound output
// Block until thread status changes. // Block until thread status changes.
std::unique_lock<std::mutex> lock(cv_m); std::unique_lock<std::mutex> lock(cv_m);
cv.wait(lock, [this]{ return m_thread_status.load() != ALSAThreadStatus::PAUSED; }); cv.wait(lock, [this] { return m_thread_status.load() != ALSAThreadStatus::PAUSED; });
snd_pcm_prepare(handle); // resume sound output snd_pcm_prepare(handle); // resume sound output
} }
} }
AlsaShutdown(); AlsaShutdown();
m_thread_status.store(ALSAThreadStatus::STOPPED); m_thread_status.store(ALSAThreadStatus::STOPPED);
} }
void AlsaSound::Clear(bool muted) void AlsaSound::Clear(bool muted)
{ {
m_muted = muted; m_muted = muted;
m_thread_status.store(muted ? ALSAThreadStatus::PAUSED : ALSAThreadStatus::RUNNING); m_thread_status.store(muted ? ALSAThreadStatus::PAUSED : ALSAThreadStatus::RUNNING);
cv.notify_one(); // Notify thread that status has changed cv.notify_one(); // Notify thread that status has changed
} }
bool AlsaSound::AlsaInit() bool AlsaSound::AlsaInit()
{ {
unsigned int sample_rate = m_mixer->GetSampleRate(); unsigned int sample_rate = m_mixer->GetSampleRate();
int err; int err;
int dir; int dir;
snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_t* swparams;
snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_t* hwparams;
snd_pcm_uframes_t buffer_size,buffer_size_max; snd_pcm_uframes_t buffer_size, buffer_size_max;
unsigned int periods; unsigned int periods;
err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Audio open error: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Audio open error: %s\n", snd_strerror(err));
return false; return false;
} }
snd_pcm_hw_params_alloca(&hwparams); snd_pcm_hw_params_alloca(&hwparams);
err = snd_pcm_hw_params_any(handle, hwparams); err = snd_pcm_hw_params_any(handle, hwparams);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Broken configuration for this PCM: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Broken configuration for this PCM: %s\n", snd_strerror(err));
return false; return false;
} }
err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Access type not available: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Access type not available: %s\n", snd_strerror(err));
return false; return false;
} }
err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE); err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Sample format not available: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Sample format not available: %s\n", snd_strerror(err));
return false; return false;
} }
dir = 0; dir = 0;
err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &sample_rate, &dir); err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &sample_rate, &dir);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Rate not available: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Rate not available: %s\n", snd_strerror(err));
return false; return false;
} }
err = snd_pcm_hw_params_set_channels(handle, hwparams, CHANNEL_COUNT); err = snd_pcm_hw_params_set_channels(handle, hwparams, CHANNEL_COUNT);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Channels count not available: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Channels count not available: %s\n", snd_strerror(err));
return false; return false;
} }
periods = BUFFER_SIZE_MAX / FRAME_COUNT_MIN; periods = BUFFER_SIZE_MAX / FRAME_COUNT_MIN;
err = snd_pcm_hw_params_set_periods_max(handle, hwparams, &periods, &dir); err = snd_pcm_hw_params_set_periods_max(handle, hwparams, &periods, &dir);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Cannot set maximum periods per buffer: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Cannot set maximum periods per buffer: %s\n", snd_strerror(err));
return false; return false;
} }
buffer_size_max = BUFFER_SIZE_MAX; buffer_size_max = BUFFER_SIZE_MAX;
err = snd_pcm_hw_params_set_buffer_size_max(handle, hwparams, &buffer_size_max); err = snd_pcm_hw_params_set_buffer_size_max(handle, hwparams, &buffer_size_max);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Cannot set maximum buffer size: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Cannot set maximum buffer size: %s\n", snd_strerror(err));
return false; return false;
} }
err = snd_pcm_hw_params(handle, hwparams); err = snd_pcm_hw_params(handle, hwparams);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Unable to install hw params: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Unable to install hw params: %s\n", snd_strerror(err));
return false; return false;
} }
err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Cannot get buffer size: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Cannot get buffer size: %s\n", snd_strerror(err));
return false; return false;
} }
err = snd_pcm_hw_params_get_periods_max(hwparams, &periods, &dir); err = snd_pcm_hw_params_get_periods_max(hwparams, &periods, &dir);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Cannot get periods: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Cannot get periods: %s\n", snd_strerror(err));
return false; return false;
} }
//periods is the number of fragments alsa can wait for during one // periods is the number of fragments alsa can wait for during one
//buffer_size // buffer_size
frames_to_deliver = buffer_size / periods; frames_to_deliver = buffer_size / periods;
//limit the minimum size. pulseaudio advertises a minimum of 32 samples. // limit the minimum size. pulseaudio advertises a minimum of 32 samples.
if (frames_to_deliver < FRAME_COUNT_MIN) if (frames_to_deliver < FRAME_COUNT_MIN)
frames_to_deliver = FRAME_COUNT_MIN; frames_to_deliver = FRAME_COUNT_MIN;
//it is probably a bad idea to try to send more than one buffer of data // it is probably a bad idea to try to send more than one buffer of data
if ((unsigned int)frames_to_deliver > buffer_size) if ((unsigned int)frames_to_deliver > buffer_size)
frames_to_deliver = buffer_size; frames_to_deliver = buffer_size;
NOTICE_LOG(AUDIO, "ALSA gave us a %ld sample \"hardware\" buffer with %d periods. Will send %d samples per fragments.\n", buffer_size, periods, frames_to_deliver); NOTICE_LOG(AUDIO, "ALSA gave us a %ld sample \"hardware\" buffer with %d periods. Will send %d "
"samples per fragments.\n",
buffer_size, periods, frames_to_deliver);
snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_alloca(&swparams);
err = snd_pcm_sw_params_current(handle, swparams); err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "cannot init sw params: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "cannot init sw params: %s\n", snd_strerror(err));
return false; return false;
} }
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U); err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "cannot set start thresh: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "cannot set start thresh: %s\n", snd_strerror(err));
return false; return false;
} }
err = snd_pcm_sw_params(handle, swparams); err = snd_pcm_sw_params(handle, swparams);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "cannot set sw params: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "cannot set sw params: %s\n", snd_strerror(err));
return false; return false;
} }
err = snd_pcm_prepare(handle); err = snd_pcm_prepare(handle);
if (err < 0) if (err < 0)
{ {
ERROR_LOG(AUDIO, "Unable to prepare: %s\n", snd_strerror(err)); ERROR_LOG(AUDIO, "Unable to prepare: %s\n", snd_strerror(err));
return false; return false;
} }
NOTICE_LOG(AUDIO, "ALSA successfully initialized.\n"); NOTICE_LOG(AUDIO, "ALSA successfully initialized.\n");
return true; return true;
} }
void AlsaSound::AlsaShutdown() void AlsaSound::AlsaShutdown()
{ {
if (handle != nullptr) if (handle != nullptr)
{ {
snd_pcm_drop(handle); snd_pcm_drop(handle);
snd_pcm_close(handle); snd_pcm_close(handle);
handle = nullptr; handle = nullptr;
} }
} }

View File

@ -20,47 +20,43 @@ class AlsaSound final : public SoundStream
{ {
#if defined(HAVE_ALSA) && HAVE_ALSA #if defined(HAVE_ALSA) && HAVE_ALSA
public: public:
AlsaSound(); AlsaSound();
bool Start() override; bool Start() override;
void SoundLoop() override; void SoundLoop() override;
void Stop() override; void Stop() override;
void Update() override; void Update() override;
void Clear(bool) override; void Clear(bool) override;
static bool isValid()
{
return true;
}
static bool isValid() { return true; }
private: private:
// maximum number of frames the buffer can hold // maximum number of frames the buffer can hold
static constexpr size_t BUFFER_SIZE_MAX = 8192; static constexpr size_t BUFFER_SIZE_MAX = 8192;
// minimum number of frames to deliver in one transfer // minimum number of frames to deliver in one transfer
static constexpr u32 FRAME_COUNT_MIN = 256; static constexpr u32 FRAME_COUNT_MIN = 256;
// number of channels per frame // number of channels per frame
static constexpr u32 CHANNEL_COUNT = 2; static constexpr u32 CHANNEL_COUNT = 2;
enum class ALSAThreadStatus enum class ALSAThreadStatus
{ {
RUNNING, RUNNING,
PAUSED, PAUSED,
STOPPING, STOPPING,
STOPPED, STOPPED,
}; };
bool AlsaInit(); bool AlsaInit();
void AlsaShutdown(); void AlsaShutdown();
s16 mix_buffer[BUFFER_SIZE_MAX * CHANNEL_COUNT]; s16 mix_buffer[BUFFER_SIZE_MAX * CHANNEL_COUNT];
std::thread thread; std::thread thread;
std::atomic<ALSAThreadStatus> m_thread_status; std::atomic<ALSAThreadStatus> m_thread_status;
std::condition_variable cv; std::condition_variable cv;
std::mutex cv_m; std::mutex cv_m;
snd_pcm_t *handle; snd_pcm_t* handle;
unsigned int frames_to_deliver; unsigned int frames_to_deliver;
#endif #endif
}; };

View File

@ -2,22 +2,21 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "AudioCommon/AlsaSoundStream.h"
#include "AudioCommon/AOSoundStream.h"
#include "AudioCommon/AudioCommon.h" #include "AudioCommon/AudioCommon.h"
#include "AudioCommon/AOSoundStream.h"
#include "AudioCommon/AlsaSoundStream.h"
#include "AudioCommon/CoreAudioSoundStream.h" #include "AudioCommon/CoreAudioSoundStream.h"
#include "AudioCommon/Mixer.h" #include "AudioCommon/Mixer.h"
#include "AudioCommon/NullSoundStream.h" #include "AudioCommon/NullSoundStream.h"
#include "AudioCommon/OpenALStream.h" #include "AudioCommon/OpenALStream.h"
#include "AudioCommon/OpenSLESStream.h" #include "AudioCommon/OpenSLESStream.h"
#include "AudioCommon/PulseAudioStream.h" #include "AudioCommon/PulseAudioStream.h"
#include "AudioCommon/XAudio2_7Stream.h"
#include "AudioCommon/XAudio2Stream.h" #include "AudioCommon/XAudio2Stream.h"
#include "AudioCommon/XAudio2_7Stream.h"
#include "Common/Common.h" #include "Common/Common.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/MsgHandler.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/Movie.h" #include "Core/Movie.h"
@ -28,182 +27,182 @@ static bool s_audio_dump_start = false;
namespace AudioCommon namespace AudioCommon
{ {
static const int AUDIO_VOLUME_MIN = 0; static const int AUDIO_VOLUME_MIN = 0;
static const int AUDIO_VOLUME_MAX = 100; static const int AUDIO_VOLUME_MAX = 100;
SoundStream* InitSoundStream() SoundStream* InitSoundStream()
{ {
std::string backend = SConfig::GetInstance().sBackend; std::string backend = SConfig::GetInstance().sBackend;
if (backend == BACKEND_OPENAL && OpenALStream::isValid()) if (backend == BACKEND_OPENAL && OpenALStream::isValid())
g_sound_stream = new OpenALStream(); g_sound_stream = new OpenALStream();
else if (backend == BACKEND_NULLSOUND && NullSound::isValid()) else if (backend == BACKEND_NULLSOUND && NullSound::isValid())
g_sound_stream = new NullSound(); g_sound_stream = new NullSound();
else if (backend == BACKEND_XAUDIO2) else if (backend == BACKEND_XAUDIO2)
{ {
if (XAudio2::isValid()) if (XAudio2::isValid())
g_sound_stream = new XAudio2(); g_sound_stream = new XAudio2();
else if (XAudio2_7::isValid()) else if (XAudio2_7::isValid())
g_sound_stream = new XAudio2_7(); g_sound_stream = new XAudio2_7();
} }
else if (backend == BACKEND_AOSOUND && AOSound::isValid()) else if (backend == BACKEND_AOSOUND && AOSound::isValid())
g_sound_stream = new AOSound(); g_sound_stream = new AOSound();
else if (backend == BACKEND_ALSA && AlsaSound::isValid()) else if (backend == BACKEND_ALSA && AlsaSound::isValid())
g_sound_stream = new AlsaSound(); g_sound_stream = new AlsaSound();
else if (backend == BACKEND_COREAUDIO && CoreAudioSound::isValid()) else if (backend == BACKEND_COREAUDIO && CoreAudioSound::isValid())
g_sound_stream = new CoreAudioSound(); g_sound_stream = new CoreAudioSound();
else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid()) else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid())
g_sound_stream = new PulseAudio(); g_sound_stream = new PulseAudio();
else if (backend == BACKEND_OPENSLES && OpenSLESStream::isValid()) else if (backend == BACKEND_OPENSLES && OpenSLESStream::isValid())
g_sound_stream = new OpenSLESStream(); g_sound_stream = new OpenSLESStream();
if (!g_sound_stream && NullSound::isValid()) if (!g_sound_stream && NullSound::isValid())
{ {
WARN_LOG(AUDIO, "Could not initialize backend %s, using %s instead.", WARN_LOG(AUDIO, "Could not initialize backend %s, using %s instead.", backend.c_str(),
backend.c_str(), BACKEND_NULLSOUND); BACKEND_NULLSOUND);
g_sound_stream = new NullSound(); g_sound_stream = new NullSound();
} }
if (g_sound_stream) if (g_sound_stream)
{ {
UpdateSoundStream(); UpdateSoundStream();
if (!g_sound_stream->Start()) if (!g_sound_stream->Start())
{ {
ERROR_LOG(AUDIO, "Could not start backend %s, using %s instead", ERROR_LOG(AUDIO, "Could not start backend %s, using %s instead", backend.c_str(),
backend.c_str(), BACKEND_NULLSOUND); BACKEND_NULLSOUND);
delete g_sound_stream; delete g_sound_stream;
g_sound_stream = new NullSound(); g_sound_stream = new NullSound();
g_sound_stream->Start(); g_sound_stream->Start();
} }
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start) if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
StartAudioDump(); StartAudioDump();
return g_sound_stream; return g_sound_stream;
} }
PanicAlertT("Sound backend %s is not valid.", backend.c_str()); PanicAlertT("Sound backend %s is not valid.", backend.c_str());
delete g_sound_stream; delete g_sound_stream;
g_sound_stream = nullptr; g_sound_stream = nullptr;
return nullptr; return nullptr;
} }
void ShutdownSoundStream() void ShutdownSoundStream()
{ {
INFO_LOG(AUDIO, "Shutting down sound stream"); INFO_LOG(AUDIO, "Shutting down sound stream");
if (g_sound_stream) if (g_sound_stream)
{ {
g_sound_stream->Stop(); g_sound_stream->Stop();
if (SConfig::GetInstance().m_DumpAudio && s_audio_dump_start) if (SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
StopAudioDump(); StopAudioDump();
delete g_sound_stream; delete g_sound_stream;
g_sound_stream = nullptr; g_sound_stream = nullptr;
} }
INFO_LOG(AUDIO, "Done shutting down sound stream"); INFO_LOG(AUDIO, "Done shutting down sound stream");
} }
std::vector<std::string> GetSoundBackends() std::vector<std::string> GetSoundBackends()
{ {
std::vector<std::string> backends; std::vector<std::string> backends;
if (NullSound::isValid()) if (NullSound::isValid())
backends.push_back(BACKEND_NULLSOUND); backends.push_back(BACKEND_NULLSOUND);
if (XAudio2_7::isValid() || XAudio2::isValid()) if (XAudio2_7::isValid() || XAudio2::isValid())
backends.push_back(BACKEND_XAUDIO2); backends.push_back(BACKEND_XAUDIO2);
if (AOSound::isValid()) if (AOSound::isValid())
backends.push_back(BACKEND_AOSOUND); backends.push_back(BACKEND_AOSOUND);
if (AlsaSound::isValid()) if (AlsaSound::isValid())
backends.push_back(BACKEND_ALSA); backends.push_back(BACKEND_ALSA);
if (CoreAudioSound::isValid()) if (CoreAudioSound::isValid())
backends.push_back(BACKEND_COREAUDIO); backends.push_back(BACKEND_COREAUDIO);
if (PulseAudio::isValid()) if (PulseAudio::isValid())
backends.push_back(BACKEND_PULSEAUDIO); backends.push_back(BACKEND_PULSEAUDIO);
if (OpenALStream::isValid()) if (OpenALStream::isValid())
backends.push_back(BACKEND_OPENAL); backends.push_back(BACKEND_OPENAL);
if (OpenSLESStream::isValid()) if (OpenSLESStream::isValid())
backends.push_back(BACKEND_OPENSLES); backends.push_back(BACKEND_OPENSLES);
return backends; return backends;
} }
void UpdateSoundStream() void UpdateSoundStream()
{ {
if (g_sound_stream) if (g_sound_stream)
{ {
int volume = SConfig::GetInstance().m_IsMuted ? 0 : SConfig::GetInstance().m_Volume; int volume = SConfig::GetInstance().m_IsMuted ? 0 : SConfig::GetInstance().m_Volume;
g_sound_stream->SetVolume(volume); g_sound_stream->SetVolume(volume);
} }
} }
void ClearAudioBuffer(bool mute) void ClearAudioBuffer(bool mute)
{ {
if (g_sound_stream) if (g_sound_stream)
g_sound_stream->Clear(mute); g_sound_stream->Clear(mute);
} }
void SendAIBuffer(short *samples, unsigned int num_samples) void SendAIBuffer(short* samples, unsigned int num_samples)
{ {
if (!g_sound_stream) if (!g_sound_stream)
return; return;
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start) if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
StartAudioDump(); StartAudioDump();
else if (!SConfig::GetInstance().m_DumpAudio && s_audio_dump_start) else if (!SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
StopAudioDump(); StopAudioDump();
CMixer* pMixer = g_sound_stream->GetMixer(); CMixer* pMixer = g_sound_stream->GetMixer();
if (pMixer && samples) if (pMixer && samples)
{ {
pMixer->PushSamples(samples, num_samples); pMixer->PushSamples(samples, num_samples);
} }
g_sound_stream->Update(); g_sound_stream->Update();
} }
void StartAudioDump() void StartAudioDump()
{ {
std::string audio_file_name_dtk = File::GetUserPath(D_DUMPAUDIO_IDX) + "dtkdump.wav"; std::string audio_file_name_dtk = File::GetUserPath(D_DUMPAUDIO_IDX) + "dtkdump.wav";
std::string audio_file_name_dsp = File::GetUserPath(D_DUMPAUDIO_IDX) + "dspdump.wav"; std::string audio_file_name_dsp = File::GetUserPath(D_DUMPAUDIO_IDX) + "dspdump.wav";
File::CreateFullPath(audio_file_name_dtk); File::CreateFullPath(audio_file_name_dtk);
File::CreateFullPath(audio_file_name_dsp); File::CreateFullPath(audio_file_name_dsp);
g_sound_stream->GetMixer()->StartLogDTKAudio(audio_file_name_dtk); g_sound_stream->GetMixer()->StartLogDTKAudio(audio_file_name_dtk);
g_sound_stream->GetMixer()->StartLogDSPAudio(audio_file_name_dsp); g_sound_stream->GetMixer()->StartLogDSPAudio(audio_file_name_dsp);
s_audio_dump_start = true; s_audio_dump_start = true;
} }
void StopAudioDump() void StopAudioDump()
{ {
g_sound_stream->GetMixer()->StopLogDTKAudio(); g_sound_stream->GetMixer()->StopLogDTKAudio();
g_sound_stream->GetMixer()->StopLogDSPAudio(); g_sound_stream->GetMixer()->StopLogDSPAudio();
s_audio_dump_start = false; s_audio_dump_start = false;
} }
void IncreaseVolume(unsigned short offset) void IncreaseVolume(unsigned short offset)
{ {
SConfig::GetInstance().m_IsMuted = false; SConfig::GetInstance().m_IsMuted = false;
int& currentVolume = SConfig::GetInstance().m_Volume; int& currentVolume = SConfig::GetInstance().m_Volume;
currentVolume += offset; currentVolume += offset;
if (currentVolume > AUDIO_VOLUME_MAX) if (currentVolume > AUDIO_VOLUME_MAX)
currentVolume = AUDIO_VOLUME_MAX; currentVolume = AUDIO_VOLUME_MAX;
UpdateSoundStream(); UpdateSoundStream();
} }
void DecreaseVolume(unsigned short offset) void DecreaseVolume(unsigned short offset)
{ {
SConfig::GetInstance().m_IsMuted = false; SConfig::GetInstance().m_IsMuted = false;
int& currentVolume = SConfig::GetInstance().m_Volume; int& currentVolume = SConfig::GetInstance().m_Volume;
currentVolume -= offset; currentVolume -= offset;
if (currentVolume < AUDIO_VOLUME_MIN) if (currentVolume < AUDIO_VOLUME_MIN)
currentVolume = AUDIO_VOLUME_MIN; currentVolume = AUDIO_VOLUME_MIN;
UpdateSoundStream(); UpdateSoundStream();
} }
void ToggleMuteVolume() void ToggleMuteVolume()
{ {
bool& isMuted = SConfig::GetInstance().m_IsMuted; bool& isMuted = SConfig::GetInstance().m_IsMuted;
isMuted = !isMuted; isMuted = !isMuted;
UpdateSoundStream(); UpdateSoundStream();
} }
} }

View File

@ -7,22 +7,21 @@
#include "AudioCommon/SoundStream.h" #include "AudioCommon/SoundStream.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
class CMixer; class CMixer;
extern SoundStream *g_sound_stream; extern SoundStream* g_sound_stream;
namespace AudioCommon namespace AudioCommon
{ {
SoundStream* InitSoundStream(); SoundStream* InitSoundStream();
void ShutdownSoundStream(); void ShutdownSoundStream();
std::vector<std::string> GetSoundBackends(); std::vector<std::string> GetSoundBackends();
void UpdateSoundStream(); void UpdateSoundStream();
void ClearAudioBuffer(bool mute); void ClearAudioBuffer(bool mute);
void SendAIBuffer(short* samples, unsigned int num_samples); void SendAIBuffer(short* samples, unsigned int num_samples);
void StartAudioDump(); void StartAudioDump();
void StopAudioDump(); void StopAudioDump();
void IncreaseVolume(unsigned short offset); void IncreaseVolume(unsigned short offset);
void DecreaseVolume(unsigned short offset); void DecreaseVolume(unsigned short offset);
void ToggleMuteVolume(); void ToggleMuteVolume();
} }

View File

@ -7,105 +7,94 @@
#include "AudioCommon/CoreAudioSoundStream.h" #include "AudioCommon/CoreAudioSoundStream.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
OSStatus CoreAudioSound::callback(void *inRefCon, OSStatus CoreAudioSound::callback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
UInt32 inNumberFrames, AudioBufferList *ioData)
{ {
for (UInt32 i = 0; i < ioData->mNumberBuffers; i++) for (UInt32 i = 0; i < ioData->mNumberBuffers; i++)
((CoreAudioSound *)inRefCon)->m_mixer-> ((CoreAudioSound*)inRefCon)
Mix((short *)ioData->mBuffers[i].mData, ->m_mixer->Mix((short*)ioData->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize / 4);
ioData->mBuffers[i].mDataByteSize / 4);
return noErr; return noErr;
} }
bool CoreAudioSound::Start() bool CoreAudioSound::Start()
{ {
OSStatus err; OSStatus err;
AURenderCallbackStruct callback_struct; AURenderCallbackStruct callback_struct;
AudioStreamBasicDescription format; AudioStreamBasicDescription format;
AudioComponentDescription desc; AudioComponentDescription desc;
AudioComponent component; AudioComponent component;
desc.componentType = kAudioUnitType_Output; desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput; desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentFlags = 0; desc.componentFlags = 0;
desc.componentFlagsMask = 0; desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentManufacturer = kAudioUnitManufacturer_Apple;
component = AudioComponentFindNext(nullptr, &desc); component = AudioComponentFindNext(nullptr, &desc);
if (component == nullptr) if (component == nullptr)
{ {
ERROR_LOG(AUDIO, "error finding audio component"); ERROR_LOG(AUDIO, "error finding audio component");
return false; return false;
} }
err = AudioComponentInstanceNew(component, &audioUnit); err = AudioComponentInstanceNew(component, &audioUnit);
if (err != noErr) if (err != noErr)
{ {
ERROR_LOG(AUDIO, "error opening audio component"); ERROR_LOG(AUDIO, "error opening audio component");
return false; return false;
} }
FillOutASBDForLPCM(format, m_mixer->GetSampleRate(), FillOutASBDForLPCM(format, m_mixer->GetSampleRate(), 2, 16, 16, false, false, false);
2, 16, 16, false, false, false); err = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0,
err = AudioUnitSetProperty(audioUnit, &format, sizeof(AudioStreamBasicDescription));
kAudioUnitProperty_StreamFormat, if (err != noErr)
kAudioUnitScope_Input, 0, &format, {
sizeof(AudioStreamBasicDescription)); ERROR_LOG(AUDIO, "error setting audio format");
if (err != noErr) return false;
{ }
ERROR_LOG(AUDIO, "error setting audio format");
return false;
}
callback_struct.inputProc = callback; callback_struct.inputProc = callback;
callback_struct.inputProcRefCon = this; callback_struct.inputProcRefCon = this;
err = AudioUnitSetProperty(audioUnit, err = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
kAudioUnitProperty_SetRenderCallback, 0, &callback_struct, sizeof callback_struct);
kAudioUnitScope_Input, 0, &callback_struct, if (err != noErr)
sizeof callback_struct); {
if (err != noErr) ERROR_LOG(AUDIO, "error setting audio callback");
{ return false;
ERROR_LOG(AUDIO, "error setting audio callback"); }
return false;
}
err = AudioUnitSetParameter(audioUnit, err = AudioUnitSetParameter(audioUnit, kHALOutputParam_Volume, kAudioUnitScope_Output, 0,
kHALOutputParam_Volume, m_volume / 100., 0);
kAudioUnitScope_Output, 0, if (err != noErr)
m_volume / 100., 0); ERROR_LOG(AUDIO, "error setting volume");
if (err != noErr)
ERROR_LOG(AUDIO, "error setting volume");
err = AudioUnitInitialize(audioUnit); err = AudioUnitInitialize(audioUnit);
if (err != noErr) if (err != noErr)
{ {
ERROR_LOG(AUDIO, "error initializing audiounit"); ERROR_LOG(AUDIO, "error initializing audiounit");
return false; return false;
} }
err = AudioOutputUnitStart(audioUnit); err = AudioOutputUnitStart(audioUnit);
if (err != noErr) if (err != noErr)
{ {
ERROR_LOG(AUDIO, "error starting audiounit"); ERROR_LOG(AUDIO, "error starting audiounit");
return false; return false;
} }
return true; return true;
} }
void CoreAudioSound::SetVolume(int volume) void CoreAudioSound::SetVolume(int volume)
{ {
OSStatus err; OSStatus err;
m_volume = volume; m_volume = volume;
err = AudioUnitSetParameter(audioUnit, err = AudioUnitSetParameter(audioUnit, kHALOutputParam_Volume, kAudioUnitScope_Output, 0,
kHALOutputParam_Volume, volume / 100., 0);
kAudioUnitScope_Output, 0, if (err != noErr)
volume / 100., 0); ERROR_LOG(AUDIO, "error setting volume");
if (err != noErr)
ERROR_LOG(AUDIO, "error setting volume");
} }
void CoreAudioSound::SoundLoop() void CoreAudioSound::SoundLoop()
@ -114,19 +103,19 @@ void CoreAudioSound::SoundLoop()
void CoreAudioSound::Stop() void CoreAudioSound::Stop()
{ {
OSStatus err; OSStatus err;
err = AudioOutputUnitStop(audioUnit); err = AudioOutputUnitStop(audioUnit);
if (err != noErr) if (err != noErr)
ERROR_LOG(AUDIO, "error stopping audiounit"); ERROR_LOG(AUDIO, "error stopping audiounit");
err = AudioUnitUninitialize(audioUnit); err = AudioUnitUninitialize(audioUnit);
if (err != noErr) if (err != noErr)
ERROR_LOG(AUDIO, "error uninitializing audiounit"); ERROR_LOG(AUDIO, "error uninitializing audiounit");
err = AudioComponentInstanceDispose(audioUnit); err = AudioComponentInstanceDispose(audioUnit);
if (err != noErr) if (err != noErr)
ERROR_LOG(AUDIO, "error closing audio component"); ERROR_LOG(AUDIO, "error closing audio component");
} }
void CoreAudioSound::Update() void CoreAudioSound::Update()

View File

@ -14,25 +14,19 @@ class CoreAudioSound final : public SoundStream
{ {
#ifdef __APPLE__ #ifdef __APPLE__
public: public:
bool Start() override; bool Start() override;
void SetVolume(int volume) override; void SetVolume(int volume) override;
void SoundLoop() override; void SoundLoop() override;
void Stop() override; void Stop() override;
void Update() override; void Update() override;
static bool isValid()
{
return true;
}
static bool isValid() { return true; }
private: private:
AudioUnit audioUnit; AudioUnit audioUnit;
int m_volume; int m_volume;
static OSStatus callback(void *inRefCon, static OSStatus callback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList* ioData);
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList *ioData);
#endif #endif
}; };

View File

@ -35,54 +35,56 @@ static float adapt_l_gain, adapt_r_gain, adapt_lpr_gain, adapt_lmr_gain;
static std::vector<float> lf, rf, lr, rr, cf, cr; static std::vector<float> lf, rf, lr, rr, cf, cr;
static float LFE_buf[256]; static float LFE_buf[256];
static unsigned int lfe_pos; static unsigned int lfe_pos;
static float *filter_coefs_lfe; static float* filter_coefs_lfe;
static unsigned int len125; static unsigned int len125;
template<class T, class _ftype_t> template <class T, class _ftype_t>
static _ftype_t DotProduct(int count, const T *buf, const _ftype_t *coefficients) static _ftype_t DotProduct(int count, const T* buf, const _ftype_t* coefficients)
{ {
int i; int i;
float sum0 = 0.0f, sum1 = 0.0f, sum2 = 0.0f, sum3 = 0.0f; float sum0 = 0.0f, sum1 = 0.0f, sum2 = 0.0f, sum3 = 0.0f;
// Unrolled loop // Unrolled loop
for (i = 0; (i + 3) < count; i += 4) for (i = 0; (i + 3) < count; i += 4)
{ {
sum0 += buf[i + 0] * coefficients[i + 0]; sum0 += buf[i + 0] * coefficients[i + 0];
sum1 += buf[i + 1] * coefficients[i + 1]; sum1 += buf[i + 1] * coefficients[i + 1];
sum2 += buf[i + 2] * coefficients[i + 2]; sum2 += buf[i + 2] * coefficients[i + 2];
sum3 += buf[i + 3] * coefficients[i + 3]; sum3 += buf[i + 3] * coefficients[i + 3];
} }
// Epilogue of unrolled loop // Epilogue of unrolled loop
for (; i < count; i++) for (; i < count; i++)
sum0 += buf[i] * coefficients[i]; sum0 += buf[i] * coefficients[i];
return sum0 + sum1 + sum2 + sum3; return sum0 + sum1 + sum2 + sum3;
} }
template<class T> template <class T>
static T FIRFilter(const T *buf, int pos, int len, int count, const float *coefficients) static T FIRFilter(const T* buf, int pos, int len, int count, const float* coefficients)
{ {
int count1, count2; int count1, count2;
if (pos >= count) if (pos >= count)
{ {
pos -= count; pos -= count;
count1 = count; count2 = 0; count1 = count;
} count2 = 0;
else }
{ else
count2 = pos; {
count1 = count - pos; count2 = pos;
pos = len - count1; count1 = count - pos;
} pos = len - count1;
}
// high part of window // high part of window
const T *ptr = &buf[pos]; const T* ptr = &buf[pos];
float r1 = DotProduct(count1, ptr, coefficients); coefficients += count1; float r1 = DotProduct(count1, ptr, coefficients);
float r2 = DotProduct(count2, buf, coefficients); coefficients += count1;
return T(r1 + r2); float r2 = DotProduct(count2, buf, coefficients);
return T(r1 + r2);
} }
/* /*
@ -96,11 +98,11 @@ static T FIRFilter(const T *buf, int pos, int len, int count, const float *coeff
*/ */
static void Hamming(int n, float* w) static void Hamming(int n, float* w)
{ {
float k = float(2*M_PI/((float)(n - 1))); // 2*pi/(N-1) float k = float(2 * M_PI / ((float)(n - 1))); // 2*pi/(N-1)
// Calculate window coefficients // Calculate window coefficients
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
*w++ = float(0.54 - 0.46*cos(k*(float)i)); *w++ = float(0.54 - 0.46 * cos(k * (float)i));
} }
/****************************************************************************** /******************************************************************************
@ -120,276 +122,270 @@ opt beta constant used only when designing using kaiser windows
returns 0 if OK, -1 if fail returns 0 if OK, -1 if fail
*/ */
static float* DesignFIR(unsigned int *n, float* fc, float opt) static float* DesignFIR(unsigned int* n, float* fc, float opt)
{ {
unsigned int o = *n & 1; // Indicator for odd filter length unsigned int o = *n & 1; // Indicator for odd filter length
unsigned int end = ((*n + 1) >> 1) - o; // Loop end unsigned int end = ((*n + 1) >> 1) - o; // Loop end
float k1 = 2 * float(M_PI); // 2*pi*fc1 float k1 = 2 * float(M_PI); // 2*pi*fc1
float k2 = 0.5f * (float)(1 - o); // Constant used if the filter has even length float k2 = 0.5f * (float)(1 - o); // Constant used if the filter has even length
float g = 0.0f; // Gain float g = 0.0f; // Gain
float t1; // Temporary variables float t1; // Temporary variables
float fc1; // Cutoff frequencies float fc1; // Cutoff frequencies
// Sanity check // Sanity check
if (*n == 0) if (*n == 0)
return nullptr; return nullptr;
fc[0] = MathUtil::Clamp(fc[0], 0.001f, 1.0f); fc[0] = MathUtil::Clamp(fc[0], 0.001f, 1.0f);
float *w = (float*)calloc(sizeof(float), *n); float* w = (float*)calloc(sizeof(float), *n);
// Get window coefficients // Get window coefficients
Hamming(*n, w); Hamming(*n, w);
fc1 = *fc; fc1 = *fc;
// Cutoff frequency must be < 0.5 where 0.5 <=> Fs/2 // Cutoff frequency must be < 0.5 where 0.5 <=> Fs/2
fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1 / 2 : 0.25f; fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1 / 2 : 0.25f;
k1 *= fc1; k1 *= fc1;
// Low pass filter // Low pass filter
// If the filter length is odd, there is one point which is exactly // If the filter length is odd, there is one point which is exactly
// in the middle. The value at this point is 2*fCutoff*sin(x)/x, // in the middle. The value at this point is 2*fCutoff*sin(x)/x,
// where x is zero. To make sure nothing strange happens, we set this // where x is zero. To make sure nothing strange happens, we set this
// value separately. // value separately.
if (o) if (o)
{ {
w[end] = fc1 * w[end] * 2.0f; w[end] = fc1 * w[end] * 2.0f;
g = w[end]; g = w[end];
} }
// Create filter // Create filter
for (u32 i = 0; i < end; i++) for (u32 i = 0; i < end; i++)
{ {
t1 = (float)(i + 1) - k2; t1 = (float)(i + 1) - k2;
w[end - i - 1] = w[*n - end + i] = float(w[end - i - 1] * sin(k1 * t1)/(M_PI * t1)); // Sinc w[end - i - 1] = w[*n - end + i] = float(w[end - i - 1] * sin(k1 * t1) / (M_PI * t1)); // Sinc
g += 2*w[end - i - 1]; // Total gain in filter g += 2 * w[end - i - 1]; // Total gain in filter
} }
// Normalize gain
g = 1 / g;
for (u32 i = 0; i < *n; i++)
w[i] *= g;
// Normalize gain return w;
g = 1/g;
for (u32 i = 0; i < *n; i++)
w[i] *= g;
return w;
} }
static void OnSeek() static void OnSeek()
{ {
l_fwr = r_fwr = lpr_fwr = lmr_fwr = 0; l_fwr = r_fwr = lpr_fwr = lmr_fwr = 0;
std::fill(fwrbuf_l.begin(), fwrbuf_l.end(), 0.0f); std::fill(fwrbuf_l.begin(), fwrbuf_l.end(), 0.0f);
std::fill(fwrbuf_r.begin(), fwrbuf_r.end(), 0.0f); std::fill(fwrbuf_r.begin(), fwrbuf_r.end(), 0.0f);
adapt_l_gain = adapt_r_gain = adapt_lpr_gain = adapt_lmr_gain = 0; adapt_l_gain = adapt_r_gain = adapt_lpr_gain = adapt_lmr_gain = 0;
std::fill(lf.begin(), lf.end(), 0.0f); std::fill(lf.begin(), lf.end(), 0.0f);
std::fill(rf.begin(), rf.end(), 0.0f); std::fill(rf.begin(), rf.end(), 0.0f);
std::fill(lr.begin(), lr.end(), 0.0f); std::fill(lr.begin(), lr.end(), 0.0f);
std::fill(rr.begin(), rr.end(), 0.0f); std::fill(rr.begin(), rr.end(), 0.0f);
std::fill(cf.begin(), cf.end(), 0.0f); std::fill(cf.begin(), cf.end(), 0.0f);
std::fill(cr.begin(), cr.end(), 0.0f); std::fill(cr.begin(), cr.end(), 0.0f);
lfe_pos = 0; lfe_pos = 0;
memset(LFE_buf, 0, sizeof(LFE_buf)); memset(LFE_buf, 0, sizeof(LFE_buf));
} }
static void Done() static void Done()
{ {
OnSeek(); OnSeek();
if (filter_coefs_lfe) if (filter_coefs_lfe)
{ {
free(filter_coefs_lfe); free(filter_coefs_lfe);
} }
filter_coefs_lfe = nullptr; filter_coefs_lfe = nullptr;
} }
static float* CalculateCoefficients125HzLowpass(int rate) static float* CalculateCoefficients125HzLowpass(int rate)
{ {
len125 = 256; len125 = 256;
float f = 125.0f / (rate / 2); float f = 125.0f / (rate / 2);
float *coeffs = DesignFIR(&len125, &f, 0); float* coeffs = DesignFIR(&len125, &f, 0);
static const float M3_01DB = 0.7071067812f; static const float M3_01DB = 0.7071067812f;
for (unsigned int i = 0; i < len125; i++) for (unsigned int i = 0; i < len125; i++)
{ {
coeffs[i] *= M3_01DB; coeffs[i] *= M3_01DB;
} }
return coeffs; return coeffs;
} }
static float PassiveLock(float x) static float PassiveLock(float x)
{ {
static const float MATAGCLOCK = 0.2f; /* AGC range (around 1) where the matrix behaves passively */ static const float MATAGCLOCK =
const float x1 = x - 1; 0.2f; /* AGC range (around 1) where the matrix behaves passively */
const float ax1s = fabs(x - 1) * (1.0f / MATAGCLOCK); const float x1 = x - 1;
return x1 - x1 / (1 + ax1s * ax1s) + 1; const float ax1s = fabs(x - 1) * (1.0f / MATAGCLOCK);
return x1 - x1 / (1 + ax1s * ax1s) + 1;
} }
static void MatrixDecode(const float *in, const int k, const int il, static void MatrixDecode(const float* in, const int k, const int il, const int ir, bool decode_rear,
const int ir, bool decode_rear, const int _dlbuflen, float _l_fwr, float _r_fwr, float _lpr_fwr,
const int _dlbuflen, float _lmr_fwr, float* _adapt_l_gain, float* _adapt_r_gain,
float _l_fwr, float _r_fwr, float* _adapt_lpr_gain, float* _adapt_lmr_gain, float* _lf, float* _rf,
float _lpr_fwr, float _lmr_fwr, float* _lr, float* _rr, float* _cf)
float *_adapt_l_gain, float *_adapt_r_gain,
float *_adapt_lpr_gain, float *_adapt_lmr_gain,
float *_lf, float *_rf, float *_lr,
float *_rr, float *_cf)
{ {
static const float M9_03DB = 0.3535533906f; static const float M9_03DB = 0.3535533906f;
static const float MATAGCTRIG = 8.0f; /* (Fuzzy) AGC trigger */ static const float MATAGCTRIG = 8.0f; /* (Fuzzy) AGC trigger */
static const float MATAGCDECAY = 1.0f; /* AGC baseline decay rate (1/samp.) */ static const float MATAGCDECAY = 1.0f; /* AGC baseline decay rate (1/samp.) */
static const float MATCOMPGAIN = 0.37f; /* Cross talk compensation gain, 0.50 - 0.55 is full cancellation. */ static const float MATCOMPGAIN =
0.37f; /* Cross talk compensation gain, 0.50 - 0.55 is full cancellation. */
const int kr = (k + olddelay) % _dlbuflen; const int kr = (k + olddelay) % _dlbuflen;
float l_gain = (_l_fwr + _r_fwr) / (1 + _l_fwr + _l_fwr); float l_gain = (_l_fwr + _r_fwr) / (1 + _l_fwr + _l_fwr);
float r_gain = (_l_fwr + _r_fwr) / (1 + _r_fwr + _r_fwr); float r_gain = (_l_fwr + _r_fwr) / (1 + _r_fwr + _r_fwr);
// The 2nd axis has strong gain fluctuations, and therefore require // The 2nd axis has strong gain fluctuations, and therefore require
// limits. The factor corresponds to the 1 / amplification of (Lt // limits. The factor corresponds to the 1 / amplification of (Lt
// - Rt) when (Lt, Rt) is strongly correlated. (e.g. during // - Rt) when (Lt, Rt) is strongly correlated. (e.g. during
// dialogues). It should be bigger than -12 dB to prevent // dialogues). It should be bigger than -12 dB to prevent
// distortion. // distortion.
float lmr_lim_fwr = _lmr_fwr > M9_03DB * _lpr_fwr ? _lmr_fwr : M9_03DB * _lpr_fwr; float lmr_lim_fwr = _lmr_fwr > M9_03DB * _lpr_fwr ? _lmr_fwr : M9_03DB * _lpr_fwr;
float lpr_gain = (_lpr_fwr + lmr_lim_fwr) / (1 + _lpr_fwr + _lpr_fwr); float lpr_gain = (_lpr_fwr + lmr_lim_fwr) / (1 + _lpr_fwr + _lpr_fwr);
float lmr_gain = (_lpr_fwr + lmr_lim_fwr) / (1 + lmr_lim_fwr + lmr_lim_fwr); float lmr_gain = (_lpr_fwr + lmr_lim_fwr) / (1 + lmr_lim_fwr + lmr_lim_fwr);
float lmr_unlim_gain = (_lpr_fwr + _lmr_fwr) / (1 + _lmr_fwr + _lmr_fwr); float lmr_unlim_gain = (_lpr_fwr + _lmr_fwr) / (1 + _lmr_fwr + _lmr_fwr);
float lpr, lmr; float lpr, lmr;
float l_agc, r_agc, lpr_agc, lmr_agc; float l_agc, r_agc, lpr_agc, lmr_agc;
float f, d_gain, c_gain, c_agc_cfk; float f, d_gain, c_gain, c_agc_cfk;
/*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/ /*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/
/* AGC adaption */ /* AGC adaption */
d_gain = (fabs(l_gain - *_adapt_l_gain) + fabs(r_gain - *_adapt_r_gain)) * 0.5f; d_gain = (fabs(l_gain - *_adapt_l_gain) + fabs(r_gain - *_adapt_r_gain)) * 0.5f;
f = d_gain * (1.0f / MATAGCTRIG); f = d_gain * (1.0f / MATAGCTRIG);
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f); f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
*_adapt_l_gain = (1 - f) * *_adapt_l_gain + f * l_gain; *_adapt_l_gain = (1 - f) * *_adapt_l_gain + f * l_gain;
*_adapt_r_gain = (1 - f) * *_adapt_r_gain + f * r_gain; *_adapt_r_gain = (1 - f) * *_adapt_r_gain + f * r_gain;
/* Matrix */ /* Matrix */
l_agc = in[il] * PassiveLock(*_adapt_l_gain); l_agc = in[il] * PassiveLock(*_adapt_l_gain);
r_agc = in[ir] * PassiveLock(*_adapt_r_gain); r_agc = in[ir] * PassiveLock(*_adapt_r_gain);
_cf[k] = (l_agc + r_agc) * (float)M_SQRT1_2; _cf[k] = (l_agc + r_agc) * (float)M_SQRT1_2;
if (decode_rear) if (decode_rear)
{ {
_lr[kr] = _rr[kr] = (l_agc - r_agc) * (float)M_SQRT1_2; _lr[kr] = _rr[kr] = (l_agc - r_agc) * (float)M_SQRT1_2;
// Stereo rear channel is steered with the same AGC steering as // Stereo rear channel is steered with the same AGC steering as
// the decoding matrix. Note this requires a fast updating AGC // the decoding matrix. Note this requires a fast updating AGC
// at the order of 20 ms (which is the case here). // at the order of 20 ms (which is the case here).
_lr[kr] *= (_l_fwr + _l_fwr) / (1 + _l_fwr + _r_fwr); _lr[kr] *= (_l_fwr + _l_fwr) / (1 + _l_fwr + _r_fwr);
_rr[kr] *= (_r_fwr + _r_fwr) / (1 + _l_fwr + _r_fwr); _rr[kr] *= (_r_fwr + _r_fwr) / (1 + _l_fwr + _r_fwr);
} }
/*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/ /*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/
lpr = (in[il] + in[ir]) * (float)M_SQRT1_2; lpr = (in[il] + in[ir]) * (float)M_SQRT1_2;
lmr = (in[il] - in[ir]) * (float)M_SQRT1_2; lmr = (in[il] - in[ir]) * (float)M_SQRT1_2;
/* AGC adaption */ /* AGC adaption */
d_gain = fabs(lmr_unlim_gain - *_adapt_lmr_gain); d_gain = fabs(lmr_unlim_gain - *_adapt_lmr_gain);
f = d_gain * (1.0f / MATAGCTRIG); f = d_gain * (1.0f / MATAGCTRIG);
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f); f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
*_adapt_lpr_gain = (1 - f) * *_adapt_lpr_gain + f * lpr_gain; *_adapt_lpr_gain = (1 - f) * *_adapt_lpr_gain + f * lpr_gain;
*_adapt_lmr_gain = (1 - f) * *_adapt_lmr_gain + f * lmr_gain; *_adapt_lmr_gain = (1 - f) * *_adapt_lmr_gain + f * lmr_gain;
/* Matrix */ /* Matrix */
lpr_agc = lpr * PassiveLock(*_adapt_lpr_gain); lpr_agc = lpr * PassiveLock(*_adapt_lpr_gain);
lmr_agc = lmr * PassiveLock(*_adapt_lmr_gain); lmr_agc = lmr * PassiveLock(*_adapt_lmr_gain);
_lf[k] = (lpr_agc + lmr_agc) * (float)M_SQRT1_2; _lf[k] = (lpr_agc + lmr_agc) * (float)M_SQRT1_2;
_rf[k] = (lpr_agc - lmr_agc) * (float)M_SQRT1_2; _rf[k] = (lpr_agc - lmr_agc) * (float)M_SQRT1_2;
/*** CENTER FRONT CANCELLATION ***/ /*** CENTER FRONT CANCELLATION ***/
// A heuristic approach exploits that Lt + Rt gain contains the // A heuristic approach exploits that Lt + Rt gain contains the
// information about Lt, Rt correlation. This effectively reshapes // information about Lt, Rt correlation. This effectively reshapes
// the front and rear "cones" to concentrate Lt + Rt to C and // the front and rear "cones" to concentrate Lt + Rt to C and
// introduce Lt - Rt in L, R. // introduce Lt - Rt in L, R.
/* 0.67677 is the empirical lower bound for lpr_gain. */ /* 0.67677 is the empirical lower bound for lpr_gain. */
c_gain = 8 * (*_adapt_lpr_gain - 0.67677f); c_gain = 8 * (*_adapt_lpr_gain - 0.67677f);
c_gain = c_gain > 0 ? c_gain : 0; c_gain = c_gain > 0 ? c_gain : 0;
// c_gain should not be too high, not even reaching full // c_gain should not be too high, not even reaching full
// cancellation (~ 0.50 - 0.55 at current AGC implementation), or // cancellation (~ 0.50 - 0.55 at current AGC implementation), or
// the center will sound too narrow. */ // the center will sound too narrow. */
c_gain = MATCOMPGAIN / (1 + c_gain * c_gain); c_gain = MATCOMPGAIN / (1 + c_gain * c_gain);
c_agc_cfk = c_gain * _cf[k]; c_agc_cfk = c_gain * _cf[k];
_lf[k] -= c_agc_cfk; _lf[k] -= c_agc_cfk;
_rf[k] -= c_agc_cfk; _rf[k] -= c_agc_cfk;
_cf[k] += c_agc_cfk + c_agc_cfk; _cf[k] += c_agc_cfk + c_agc_cfk;
} }
void DPL2Decode(float *samples, int numsamples, float *out) void DPL2Decode(float* samples, int numsamples, float* out)
{ {
static const unsigned int FWRDURATION = 240; // FWR average duration (samples) static const unsigned int FWRDURATION = 240; // FWR average duration (samples)
static const int cfg_delay = 0; static const int cfg_delay = 0;
static const unsigned int fmt_freq = 48000; static const unsigned int fmt_freq = 48000;
static const unsigned int fmt_nchannels = 2; // input channels static const unsigned int fmt_nchannels = 2; // input channels
int cur = 0; int cur = 0;
if (olddelay != cfg_delay || oldfreq != fmt_freq) if (olddelay != cfg_delay || oldfreq != fmt_freq)
{ {
Done(); Done();
olddelay = cfg_delay; olddelay = cfg_delay;
oldfreq = fmt_freq; oldfreq = fmt_freq;
dlbuflen = std::max(FWRDURATION, (fmt_freq * cfg_delay / 1000)); //+(len7000-1); dlbuflen = std::max(FWRDURATION, (fmt_freq * cfg_delay / 1000)); //+(len7000-1);
cyc_pos = dlbuflen - 1; cyc_pos = dlbuflen - 1;
fwrbuf_l.resize(dlbuflen); fwrbuf_l.resize(dlbuflen);
fwrbuf_r.resize(dlbuflen); fwrbuf_r.resize(dlbuflen);
lf.resize(dlbuflen); lf.resize(dlbuflen);
rf.resize(dlbuflen); rf.resize(dlbuflen);
lr.resize(dlbuflen); lr.resize(dlbuflen);
rr.resize(dlbuflen); rr.resize(dlbuflen);
cf.resize(dlbuflen); cf.resize(dlbuflen);
cr.resize(dlbuflen); cr.resize(dlbuflen);
filter_coefs_lfe = CalculateCoefficients125HzLowpass(fmt_freq); filter_coefs_lfe = CalculateCoefficients125HzLowpass(fmt_freq);
lfe_pos = 0; lfe_pos = 0;
memset(LFE_buf, 0, sizeof(LFE_buf)); memset(LFE_buf, 0, sizeof(LFE_buf));
} }
float *in = samples; // Input audio data float* in = samples; // Input audio data
float *end = in + numsamples * fmt_nchannels; // Loop end float* end = in + numsamples * fmt_nchannels; // Loop end
while (in < end) while (in < end)
{ {
const int k = cyc_pos; const int k = cyc_pos;
const int fwr_pos = (k + FWRDURATION) % dlbuflen; const int fwr_pos = (k + FWRDURATION) % dlbuflen;
/* Update the full wave rectified total amplitude */ /* Update the full wave rectified total amplitude */
/* Input matrix decoder */ /* Input matrix decoder */
l_fwr += fabs(in[0]) - fabs(fwrbuf_l[fwr_pos]); l_fwr += fabs(in[0]) - fabs(fwrbuf_l[fwr_pos]);
r_fwr += fabs(in[1]) - fabs(fwrbuf_r[fwr_pos]); r_fwr += fabs(in[1]) - fabs(fwrbuf_r[fwr_pos]);
lpr_fwr += fabs(in[0] + in[1]) - fabs(fwrbuf_l[fwr_pos] + fwrbuf_r[fwr_pos]); lpr_fwr += fabs(in[0] + in[1]) - fabs(fwrbuf_l[fwr_pos] + fwrbuf_r[fwr_pos]);
lmr_fwr += fabs(in[0] - in[1]) - fabs(fwrbuf_l[fwr_pos] - fwrbuf_r[fwr_pos]); lmr_fwr += fabs(in[0] - in[1]) - fabs(fwrbuf_l[fwr_pos] - fwrbuf_r[fwr_pos]);
/* Matrix encoded 2 channel sources */ /* Matrix encoded 2 channel sources */
fwrbuf_l[k] = in[0]; fwrbuf_l[k] = in[0];
fwrbuf_r[k] = in[1]; fwrbuf_r[k] = in[1];
MatrixDecode(in, k, 0, 1, true, dlbuflen, MatrixDecode(in, k, 0, 1, true, dlbuflen, l_fwr, r_fwr, lpr_fwr, lmr_fwr, &adapt_l_gain,
l_fwr, r_fwr, &adapt_r_gain, &adapt_lpr_gain, &adapt_lmr_gain, &lf[0], &rf[0], &lr[0], &rr[0],
lpr_fwr, lmr_fwr, &cf[0]);
&adapt_l_gain, &adapt_r_gain,
&adapt_lpr_gain, &adapt_lmr_gain,
&lf[0], &rf[0], &lr[0], &rr[0], &cf[0]);
out[cur + 0] = lf[k]; out[cur + 0] = lf[k];
out[cur + 1] = rf[k]; out[cur + 1] = rf[k];
out[cur + 2] = cf[k]; out[cur + 2] = cf[k];
LFE_buf[lfe_pos] = (lf[k] + rf[k] + 2.0f * cf[k] + lr[k] + rr[k]) / 2.0f; LFE_buf[lfe_pos] = (lf[k] + rf[k] + 2.0f * cf[k] + lr[k] + rr[k]) / 2.0f;
out[cur + 3] = FIRFilter(LFE_buf, lfe_pos, len125, len125, filter_coefs_lfe); out[cur + 3] = FIRFilter(LFE_buf, lfe_pos, len125, len125, filter_coefs_lfe);
lfe_pos++; lfe_pos++;
if (lfe_pos == len125) if (lfe_pos == len125)
{ {
lfe_pos = 0; lfe_pos = 0;
} }
out[cur + 4] = lr[k]; out[cur + 4] = lr[k];
out[cur + 5] = rr[k]; out[cur + 5] = rr[k];
// Next sample... // Next sample...
in += 2; in += 2;
cur += 6; cur += 6;
cyc_pos--; cyc_pos--;
if (cyc_pos < 0) if (cyc_pos < 0)
{ {
cyc_pos += dlbuflen; cyc_pos += dlbuflen;
} }
} }
} }
void DPL2Reset() void DPL2Reset()
{ {
olddelay = -1; olddelay = -1;
oldfreq = 0; oldfreq = 0;
filter_coefs_lfe = nullptr; filter_coefs_lfe = nullptr;
} }

View File

@ -4,5 +4,5 @@
#pragma once #pragma once
void DPL2Decode(float *samples, int numsamples, float *out); void DPL2Decode(float* samples, int numsamples, float* out);
void DPL2Reset(); void DPL2Reset();

View File

@ -8,18 +8,17 @@
#include "AudioCommon/Mixer.h" #include "AudioCommon/Mixer.h"
#include "Common/CommonFuncs.h" #include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/MathUtil.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#if _M_SSE >= 0x301 && !(defined __GNUC__ && !defined __SSSE3__) #if _M_SSE >= 0x301 && !(defined __GNUC__ && !defined __SSSE3__)
#include <tmmintrin.h> #include <tmmintrin.h>
#endif #endif
CMixer::CMixer(unsigned int BackendSampleRate) CMixer::CMixer(unsigned int BackendSampleRate) : m_sampleRate(BackendSampleRate)
: m_sampleRate(BackendSampleRate)
{ {
INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized"); INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized");
} }
CMixer::~CMixer() CMixer::~CMixer()
@ -27,249 +26,253 @@ CMixer::~CMixer()
} }
// Executed from sound stream thread // Executed from sound stream thread
unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, bool consider_framelimit) unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples,
bool consider_framelimit)
{ {
unsigned int currentSample = 0; unsigned int currentSample = 0;
// Cache access in non-volatile variable // Cache access in non-volatile variable
// This is the only function changing the read value, so it's safe to // This is the only function changing the read value, so it's safe to
// cache it locally although it's written here. // cache it locally although it's written here.
// The writing pointer will be modified outside, but it will only increase, // The writing pointer will be modified outside, but it will only increase,
// so we will just ignore new written data while interpolating. // so we will just ignore new written data while interpolating.
// Without this cache, the compiler wouldn't be allowed to optimize the // Without this cache, the compiler wouldn't be allowed to optimize the
// interpolation loop. // interpolation loop.
u32 indexR = m_indexR.load(); u32 indexR = m_indexR.load();
u32 indexW = m_indexW.load(); u32 indexW = m_indexW.load();
u32 low_waterwark = m_input_sample_rate * SConfig::GetInstance().iTimingVariance / 1000; u32 low_waterwark = m_input_sample_rate * SConfig::GetInstance().iTimingVariance / 1000;
low_waterwark = std::min(low_waterwark, MAX_SAMPLES / 2); low_waterwark = std::min(low_waterwark, MAX_SAMPLES / 2);
float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2); float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2);
m_numLeftI = (numLeft + m_numLeftI*(CONTROL_AVG-1)) / CONTROL_AVG; m_numLeftI = (numLeft + m_numLeftI * (CONTROL_AVG - 1)) / CONTROL_AVG;
float offset = (m_numLeftI - low_waterwark) * CONTROL_FACTOR; float offset = (m_numLeftI - low_waterwark) * CONTROL_FACTOR;
if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT; if (offset > MAX_FREQ_SHIFT)
if (offset < -MAX_FREQ_SHIFT) offset = -MAX_FREQ_SHIFT; offset = MAX_FREQ_SHIFT;
if (offset < -MAX_FREQ_SHIFT)
offset = -MAX_FREQ_SHIFT;
//render numleft sample pairs to samples[] // render numleft sample pairs to samples[]
//advance indexR with sample position // advance indexR with sample position
//remember fractional offset // remember fractional offset
float emulationspeed = SConfig::GetInstance().m_EmulationSpeed; float emulationspeed = SConfig::GetInstance().m_EmulationSpeed;
float aid_sample_rate = m_input_sample_rate + offset; float aid_sample_rate = m_input_sample_rate + offset;
if (consider_framelimit && emulationspeed > 0.0f) if (consider_framelimit && emulationspeed > 0.0f)
{ {
aid_sample_rate = aid_sample_rate * emulationspeed; aid_sample_rate = aid_sample_rate * emulationspeed;
} }
const u32 ratio = (u32)(65536.0f * aid_sample_rate / (float)m_mixer->m_sampleRate); const u32 ratio = (u32)(65536.0f * aid_sample_rate / (float)m_mixer->m_sampleRate);
s32 lvolume = m_LVolume.load(); s32 lvolume = m_LVolume.load();
s32 rvolume = m_RVolume.load(); s32 rvolume = m_RVolume.load();
// TODO: consider a higher-quality resampling algorithm. // TODO: consider a higher-quality resampling algorithm.
for (; currentSample < numSamples * 2 && ((indexW-indexR) & INDEX_MASK) > 2; currentSample += 2) for (; currentSample < numSamples * 2 && ((indexW - indexR) & INDEX_MASK) > 2; currentSample += 2)
{ {
u32 indexR2 = indexR + 2; //next sample u32 indexR2 = indexR + 2; // next sample
s16 l1 = Common::swap16(m_buffer[indexR & INDEX_MASK]); //current s16 l1 = Common::swap16(m_buffer[indexR & INDEX_MASK]); // current
s16 l2 = Common::swap16(m_buffer[indexR2 & INDEX_MASK]); //next s16 l2 = Common::swap16(m_buffer[indexR2 & INDEX_MASK]); // next
int sampleL = ((l1 << 16) + (l2 - l1) * (u16)m_frac) >> 16; int sampleL = ((l1 << 16) + (l2 - l1) * (u16)m_frac) >> 16;
sampleL = (sampleL * lvolume) >> 8; sampleL = (sampleL * lvolume) >> 8;
sampleL += samples[currentSample + 1]; sampleL += samples[currentSample + 1];
samples[currentSample + 1] = MathUtil::Clamp(sampleL, -32767, 32767); samples[currentSample + 1] = MathUtil::Clamp(sampleL, -32767, 32767);
s16 r1 = Common::swap16(m_buffer[(indexR + 1) & INDEX_MASK]); //current s16 r1 = Common::swap16(m_buffer[(indexR + 1) & INDEX_MASK]); // current
s16 r2 = Common::swap16(m_buffer[(indexR2 + 1) & INDEX_MASK]); //next s16 r2 = Common::swap16(m_buffer[(indexR2 + 1) & INDEX_MASK]); // next
int sampleR = ((r1 << 16) + (r2 - r1) * (u16)m_frac) >> 16; int sampleR = ((r1 << 16) + (r2 - r1) * (u16)m_frac) >> 16;
sampleR = (sampleR * rvolume) >> 8; sampleR = (sampleR * rvolume) >> 8;
sampleR += samples[currentSample]; sampleR += samples[currentSample];
samples[currentSample] = MathUtil::Clamp(sampleR, -32767, 32767); samples[currentSample] = MathUtil::Clamp(sampleR, -32767, 32767);
m_frac += ratio; m_frac += ratio;
indexR += 2 * (u16)(m_frac >> 16); indexR += 2 * (u16)(m_frac >> 16);
m_frac &= 0xffff; m_frac &= 0xffff;
} }
// Padding // Padding
short s[2]; short s[2];
s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]); s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]);
s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]); s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]);
s[0] = (s[0] * rvolume) >> 8; s[0] = (s[0] * rvolume) >> 8;
s[1] = (s[1] * lvolume) >> 8; s[1] = (s[1] * lvolume) >> 8;
for (; currentSample < numSamples * 2; currentSample += 2) for (; currentSample < numSamples * 2; currentSample += 2)
{ {
int sampleR = MathUtil::Clamp(s[0] + samples[currentSample + 0], -32767, 32767); int sampleR = MathUtil::Clamp(s[0] + samples[currentSample + 0], -32767, 32767);
int sampleL = MathUtil::Clamp(s[1] + samples[currentSample + 1], -32767, 32767); int sampleL = MathUtil::Clamp(s[1] + samples[currentSample + 1], -32767, 32767);
samples[currentSample + 0] = sampleR; samples[currentSample + 0] = sampleR;
samples[currentSample + 1] = sampleL; samples[currentSample + 1] = sampleL;
} }
// Flush cached variable // Flush cached variable
m_indexR.store(indexR); m_indexR.store(indexR);
return numSamples; return numSamples;
} }
unsigned int CMixer::Mix(short* samples, unsigned int num_samples, bool consider_framelimit) unsigned int CMixer::Mix(short* samples, unsigned int num_samples, bool consider_framelimit)
{ {
if (!samples) if (!samples)
return 0; return 0;
memset(samples, 0, num_samples * 2 * sizeof(short)); memset(samples, 0, num_samples * 2 * sizeof(short));
m_dma_mixer.Mix(samples, num_samples, consider_framelimit); m_dma_mixer.Mix(samples, num_samples, consider_framelimit);
m_streaming_mixer.Mix(samples, num_samples, consider_framelimit); m_streaming_mixer.Mix(samples, num_samples, consider_framelimit);
m_wiimote_speaker_mixer.Mix(samples, num_samples, consider_framelimit); m_wiimote_speaker_mixer.Mix(samples, num_samples, consider_framelimit);
return num_samples; return num_samples;
} }
void CMixer::MixerFifo::PushSamples(const short *samples, unsigned int num_samples) void CMixer::MixerFifo::PushSamples(const short* samples, unsigned int num_samples)
{ {
// Cache access in non-volatile variable // Cache access in non-volatile variable
// indexR isn't allowed to cache in the audio throttling loop as it // indexR isn't allowed to cache in the audio throttling loop as it
// needs to get updates to not deadlock. // needs to get updates to not deadlock.
u32 indexW = m_indexW.load(); u32 indexW = m_indexW.load();
// Check if we have enough free space // Check if we have enough free space
// indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW // indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW
if (num_samples * 2 + ((indexW - m_indexR.load()) & INDEX_MASK) >= MAX_SAMPLES * 2) if (num_samples * 2 + ((indexW - m_indexR.load()) & INDEX_MASK) >= MAX_SAMPLES * 2)
return; return;
// AyuanX: Actual re-sampling work has been moved to sound thread // AyuanX: Actual re-sampling work has been moved to sound thread
// to alleviate the workload on main thread // to alleviate the workload on main thread
// and we simply store raw data here to make fast mem copy // and we simply store raw data here to make fast mem copy
int over_bytes = num_samples * 4 - (MAX_SAMPLES * 2 - (indexW & INDEX_MASK)) * sizeof(short); int over_bytes = num_samples * 4 - (MAX_SAMPLES * 2 - (indexW & INDEX_MASK)) * sizeof(short);
if (over_bytes > 0) if (over_bytes > 0)
{ {
memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4 - over_bytes); memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4 - over_bytes);
memcpy(&m_buffer[0], samples + (num_samples * 4 - over_bytes) / sizeof(short), over_bytes); memcpy(&m_buffer[0], samples + (num_samples * 4 - over_bytes) / sizeof(short), over_bytes);
} }
else else
{ {
memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4); memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4);
} }
m_indexW.fetch_add(num_samples * 2); m_indexW.fetch_add(num_samples * 2);
} }
void CMixer::PushSamples(const short *samples, unsigned int num_samples) void CMixer::PushSamples(const short* samples, unsigned int num_samples)
{ {
m_dma_mixer.PushSamples(samples, num_samples); m_dma_mixer.PushSamples(samples, num_samples);
if (m_log_dsp_audio) if (m_log_dsp_audio)
m_wave_writer_dsp.AddStereoSamplesBE(samples, num_samples); m_wave_writer_dsp.AddStereoSamplesBE(samples, num_samples);
} }
void CMixer::PushStreamingSamples(const short *samples, unsigned int num_samples) void CMixer::PushStreamingSamples(const short* samples, unsigned int num_samples)
{ {
m_streaming_mixer.PushSamples(samples, num_samples); m_streaming_mixer.PushSamples(samples, num_samples);
if (m_log_dtk_audio) if (m_log_dtk_audio)
m_wave_writer_dtk.AddStereoSamplesBE(samples, num_samples); m_wave_writer_dtk.AddStereoSamplesBE(samples, num_samples);
} }
void CMixer::PushWiimoteSpeakerSamples(const short *samples, unsigned int num_samples, unsigned int sample_rate) void CMixer::PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples,
unsigned int sample_rate)
{ {
short samples_stereo[MAX_SAMPLES * 2]; short samples_stereo[MAX_SAMPLES * 2];
if (num_samples < MAX_SAMPLES) if (num_samples < MAX_SAMPLES)
{ {
m_wiimote_speaker_mixer.SetInputSampleRate(sample_rate); m_wiimote_speaker_mixer.SetInputSampleRate(sample_rate);
for (unsigned int i = 0; i < num_samples; ++i) for (unsigned int i = 0; i < num_samples; ++i)
{ {
samples_stereo[i * 2] = Common::swap16(samples[i]); samples_stereo[i * 2] = Common::swap16(samples[i]);
samples_stereo[i * 2 + 1] = Common::swap16(samples[i]); samples_stereo[i * 2 + 1] = Common::swap16(samples[i]);
} }
m_wiimote_speaker_mixer.PushSamples(samples_stereo, num_samples); m_wiimote_speaker_mixer.PushSamples(samples_stereo, num_samples);
} }
} }
void CMixer::SetDMAInputSampleRate(unsigned int rate) void CMixer::SetDMAInputSampleRate(unsigned int rate)
{ {
m_dma_mixer.SetInputSampleRate(rate); m_dma_mixer.SetInputSampleRate(rate);
} }
void CMixer::SetStreamInputSampleRate(unsigned int rate) void CMixer::SetStreamInputSampleRate(unsigned int rate)
{ {
m_streaming_mixer.SetInputSampleRate(rate); m_streaming_mixer.SetInputSampleRate(rate);
} }
void CMixer::SetStreamingVolume(unsigned int lvolume, unsigned int rvolume) void CMixer::SetStreamingVolume(unsigned int lvolume, unsigned int rvolume)
{ {
m_streaming_mixer.SetVolume(lvolume, rvolume); m_streaming_mixer.SetVolume(lvolume, rvolume);
} }
void CMixer::SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume) void CMixer::SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume)
{ {
m_wiimote_speaker_mixer.SetVolume(lvolume, rvolume); m_wiimote_speaker_mixer.SetVolume(lvolume, rvolume);
} }
void CMixer::StartLogDTKAudio(const std::string& filename) void CMixer::StartLogDTKAudio(const std::string& filename)
{ {
if (!m_log_dtk_audio) if (!m_log_dtk_audio)
{ {
m_log_dtk_audio = true; m_log_dtk_audio = true;
m_wave_writer_dtk.Start(filename, 48000); m_wave_writer_dtk.Start(filename, 48000);
m_wave_writer_dtk.SetSkipSilence(false); m_wave_writer_dtk.SetSkipSilence(false);
NOTICE_LOG(AUDIO, "Starting DTK Audio logging"); NOTICE_LOG(AUDIO, "Starting DTK Audio logging");
} }
else else
{ {
WARN_LOG(AUDIO, "DTK Audio logging has already been started"); WARN_LOG(AUDIO, "DTK Audio logging has already been started");
} }
} }
void CMixer::StopLogDTKAudio() void CMixer::StopLogDTKAudio()
{ {
if (m_log_dtk_audio) if (m_log_dtk_audio)
{ {
m_log_dtk_audio = false; m_log_dtk_audio = false;
m_wave_writer_dtk.Stop(); m_wave_writer_dtk.Stop();
NOTICE_LOG(AUDIO, "Stopping DTK Audio logging"); NOTICE_LOG(AUDIO, "Stopping DTK Audio logging");
} }
else else
{ {
WARN_LOG(AUDIO, "DTK Audio logging has already been stopped"); WARN_LOG(AUDIO, "DTK Audio logging has already been stopped");
} }
} }
void CMixer::StartLogDSPAudio(const std::string& filename) void CMixer::StartLogDSPAudio(const std::string& filename)
{ {
if (!m_log_dsp_audio) if (!m_log_dsp_audio)
{ {
m_log_dsp_audio = true; m_log_dsp_audio = true;
m_wave_writer_dsp.Start(filename, 32000); m_wave_writer_dsp.Start(filename, 32000);
m_wave_writer_dsp.SetSkipSilence(false); m_wave_writer_dsp.SetSkipSilence(false);
NOTICE_LOG(AUDIO, "Starting DSP Audio logging"); NOTICE_LOG(AUDIO, "Starting DSP Audio logging");
} }
else else
{ {
WARN_LOG(AUDIO, "DSP Audio logging has already been started"); WARN_LOG(AUDIO, "DSP Audio logging has already been started");
} }
} }
void CMixer::StopLogDSPAudio() void CMixer::StopLogDSPAudio()
{ {
if (m_log_dsp_audio) if (m_log_dsp_audio)
{ {
m_log_dsp_audio = false; m_log_dsp_audio = false;
m_wave_writer_dsp.Stop(); m_wave_writer_dsp.Stop();
NOTICE_LOG(AUDIO, "Stopping DSP Audio logging"); NOTICE_LOG(AUDIO, "Stopping DSP Audio logging");
} }
else else
{ {
WARN_LOG(AUDIO, "DSP Audio logging has already been stopped"); WARN_LOG(AUDIO, "DSP Audio logging has already been stopped");
} }
} }
void CMixer::MixerFifo::SetInputSampleRate(unsigned int rate) void CMixer::MixerFifo::SetInputSampleRate(unsigned int rate)
{ {
m_input_sample_rate = rate; m_input_sample_rate = rate;
} }
void CMixer::MixerFifo::SetVolume(unsigned int lvolume, unsigned int rvolume) void CMixer::MixerFifo::SetVolume(unsigned int lvolume, unsigned int rvolume)
{ {
m_LVolume.store(lvolume + (lvolume >> 7)); m_LVolume.store(lvolume + (lvolume >> 7));
m_RVolume.store(rvolume + (rvolume >> 7)); m_RVolume.store(rvolume + (rvolume >> 7));
} }

View File

@ -13,74 +13,73 @@
class CMixer final class CMixer final
{ {
public: public:
explicit CMixer(unsigned int BackendSampleRate); explicit CMixer(unsigned int BackendSampleRate);
~CMixer(); ~CMixer();
// Called from audio threads // Called from audio threads
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true); unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
// Called from main thread // Called from main thread
void PushSamples(const short* samples, unsigned int num_samples); void PushSamples(const short* samples, unsigned int num_samples);
void PushStreamingSamples(const short* samples, unsigned int num_samples); void PushStreamingSamples(const short* samples, unsigned int num_samples);
void PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples, unsigned int sample_rate); void PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples,
unsigned int GetSampleRate() const { return m_sampleRate; } unsigned int sample_rate);
unsigned int GetSampleRate() const { return m_sampleRate; }
void SetDMAInputSampleRate(unsigned int rate);
void SetStreamInputSampleRate(unsigned int rate);
void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume);
void SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume);
void SetDMAInputSampleRate(unsigned int rate); void StartLogDTKAudio(const std::string& filename);
void SetStreamInputSampleRate(unsigned int rate); void StopLogDTKAudio();
void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume);
void SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume);
void StartLogDTKAudio(const std::string& filename); void StartLogDSPAudio(const std::string& filename);
void StopLogDTKAudio(); void StopLogDSPAudio();
void StartLogDSPAudio(const std::string& filename);
void StopLogDSPAudio();
float GetCurrentSpeed() const { return m_speed.load(); }
void UpdateSpeed(float val) { m_speed.store(val); }
float GetCurrentSpeed() const { return m_speed.load(); }
void UpdateSpeed(float val) { m_speed.store(val); }
private: private:
static constexpr u32 MAX_SAMPLES = 1024 * 4; // 128 ms static constexpr u32 MAX_SAMPLES = 1024 * 4; // 128 ms
static constexpr u32 INDEX_MASK = MAX_SAMPLES * 2 - 1; static constexpr u32 INDEX_MASK = MAX_SAMPLES * 2 - 1;
static constexpr int MAX_FREQ_SHIFT = 200; // Per 32000 Hz static constexpr int MAX_FREQ_SHIFT = 200; // Per 32000 Hz
static constexpr float CONTROL_FACTOR = 0.2f; static constexpr float CONTROL_FACTOR = 0.2f;
static constexpr u32 CONTROL_AVG = 32; // In freq_shift per FIFO size offset static constexpr u32 CONTROL_AVG = 32; // In freq_shift per FIFO size offset
class MixerFifo final class MixerFifo final
{ {
public: public:
MixerFifo(CMixer* mixer, unsigned sample_rate) MixerFifo(CMixer* mixer, unsigned sample_rate)
: m_mixer(mixer) : m_mixer(mixer), m_input_sample_rate(sample_rate)
, m_input_sample_rate(sample_rate) {
{ }
} void PushSamples(const short* samples, unsigned int num_samples);
void PushSamples(const short* samples, unsigned int num_samples); unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true); void SetInputSampleRate(unsigned int rate);
void SetInputSampleRate(unsigned int rate); void SetVolume(unsigned int lvolume, unsigned int rvolume);
void SetVolume(unsigned int lvolume, unsigned int rvolume);
private:
CMixer* m_mixer;
unsigned m_input_sample_rate;
std::array<short, MAX_SAMPLES * 2> m_buffer{};
std::atomic<u32> m_indexW{0};
std::atomic<u32> m_indexR{0};
// Volume ranges from 0-256
std::atomic<s32> m_LVolume{256};
std::atomic<s32> m_RVolume{256};
float m_numLeftI = 0.0f;
u32 m_frac = 0;
};
MixerFifo m_dma_mixer{this, 32000};
MixerFifo m_streaming_mixer{this, 48000};
MixerFifo m_wiimote_speaker_mixer{this, 3000};
unsigned int m_sampleRate;
WaveFileWriter m_wave_writer_dtk; private:
WaveFileWriter m_wave_writer_dsp; CMixer* m_mixer;
unsigned m_input_sample_rate;
std::array<short, MAX_SAMPLES * 2> m_buffer{};
std::atomic<u32> m_indexW{0};
std::atomic<u32> m_indexR{0};
// Volume ranges from 0-256
std::atomic<s32> m_LVolume{256};
std::atomic<s32> m_RVolume{256};
float m_numLeftI = 0.0f;
u32 m_frac = 0;
};
MixerFifo m_dma_mixer{this, 32000};
MixerFifo m_streaming_mixer{this, 48000};
MixerFifo m_wiimote_speaker_mixer{this, 3000};
unsigned int m_sampleRate;
bool m_log_dtk_audio = false; WaveFileWriter m_wave_writer_dtk;
bool m_log_dsp_audio = false; WaveFileWriter m_wave_writer_dsp;
// Current rate of emulation (1.0 = 100% speed) bool m_log_dtk_audio = false;
std::atomic<float> m_speed{0.0f}; bool m_log_dsp_audio = false;
// Current rate of emulation (1.0 = 100% speed)
std::atomic<float> m_speed{0.0f};
}; };

View File

@ -13,7 +13,7 @@ void NullSound::SoundLoop()
bool NullSound::Start() bool NullSound::Start()
{ {
return true; return true;
} }
void NullSound::SetVolume(int volume) void NullSound::SetVolume(int volume)
@ -22,19 +22,22 @@ void NullSound::SetVolume(int volume)
void NullSound::Update() void NullSound::Update()
{ {
// num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD. // num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD.
constexpr u32 stereo_16_bit_size = 4; constexpr u32 stereo_16_bit_size = 4;
constexpr u32 dma_length = 32; constexpr u32 dma_length = 32;
const u64 audio_dma_period = SystemTimers::GetTicksPerSecond() / (AudioInterface::GetAIDSampleRate() * stereo_16_bit_size / dma_length); const u64 audio_dma_period =
const u64 ais_samples_per_second = 48000 * stereo_16_bit_size; SystemTimers::GetTicksPerSecond() /
const u64 num_samples_to_render = (audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond(); (AudioInterface::GetAIDSampleRate() * stereo_16_bit_size / dma_length);
const u64 ais_samples_per_second = 48000 * stereo_16_bit_size;
const u64 num_samples_to_render =
(audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond();
m_mixer->Mix(m_realtime_buffer.data(), (unsigned int)num_samples_to_render); m_mixer->Mix(m_realtime_buffer.data(), (unsigned int)num_samples_to_render);
} }
void NullSound::Clear(bool mute) void NullSound::Clear(bool mute)
{ {
m_muted = mute; m_muted = mute;
} }
void NullSound::Stop() void NullSound::Stop()

View File

@ -10,18 +10,17 @@
class NullSound final : public SoundStream class NullSound final : public SoundStream
{ {
public: public:
bool Start() override; bool Start() override;
void SoundLoop() override; void SoundLoop() override;
void SetVolume(int volume) override; void SetVolume(int volume) override;
void Stop() override; void Stop() override;
void Clear(bool mute) override; void Clear(bool mute) override;
void Update() override; void Update() override;
static bool isValid() { return true; }
static bool isValid() { return true; }
private: private:
static constexpr size_t BUFFER_SIZE = 48000 * 4 / 32; static constexpr size_t BUFFER_SIZE = 48000 * 4 / 32;
// Playback position // Playback position
std::array<short, BUFFER_SIZE / sizeof(short)> m_realtime_buffer; std::array<short, BUFFER_SIZE / sizeof(short)> m_realtime_buffer;
}; };

View File

@ -5,11 +5,11 @@
#include <cstring> #include <cstring>
#include <thread> #include <thread>
#include "AudioCommon/aldlist.h"
#include "AudioCommon/DPL2Decoder.h" #include "AudioCommon/DPL2Decoder.h"
#include "AudioCommon/OpenALStream.h" #include "AudioCommon/OpenALStream.h"
#include "Common/Thread.h" #include "AudioCommon/aldlist.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/Thread.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#if defined HAVE_OPENAL && HAVE_OPENAL #if defined HAVE_OPENAL && HAVE_OPENAL
@ -25,351 +25,363 @@ static soundtouch::SoundTouch soundTouch;
// //
bool OpenALStream::Start() bool OpenALStream::Start()
{ {
m_run_thread.store(true); m_run_thread.store(true);
bool bReturn = false; bool bReturn = false;
ALDeviceList pDeviceList; ALDeviceList pDeviceList;
if (pDeviceList.GetNumDevices()) if (pDeviceList.GetNumDevices())
{ {
char *defDevName = pDeviceList.GetDeviceName(pDeviceList.GetDefaultDevice()); char* defDevName = pDeviceList.GetDeviceName(pDeviceList.GetDefaultDevice());
WARN_LOG(AUDIO, "Found OpenAL device %s", defDevName); WARN_LOG(AUDIO, "Found OpenAL device %s", defDevName);
ALCdevice *pDevice = alcOpenDevice(defDevName); ALCdevice* pDevice = alcOpenDevice(defDevName);
if (pDevice) if (pDevice)
{ {
ALCcontext *pContext = alcCreateContext(pDevice, nullptr); ALCcontext* pContext = alcCreateContext(pDevice, nullptr);
if (pContext) if (pContext)
{ {
// Used to determine an appropriate period size (2x period = total buffer size) // Used to determine an appropriate period size (2x period = total buffer size)
//ALCint refresh; // ALCint refresh;
//alcGetIntegerv(pDevice, ALC_REFRESH, 1, &refresh); // alcGetIntegerv(pDevice, ALC_REFRESH, 1, &refresh);
//period_size_in_millisec = 1000 / refresh; // period_size_in_millisec = 1000 / refresh;
alcMakeContextCurrent(pContext); alcMakeContextCurrent(pContext);
thread = std::thread(&OpenALStream::SoundLoop, this); thread = std::thread(&OpenALStream::SoundLoop, this);
bReturn = true; bReturn = true;
} }
else else
{ {
alcCloseDevice(pDevice); alcCloseDevice(pDevice);
PanicAlertT("OpenAL: can't create context for device %s", defDevName); PanicAlertT("OpenAL: can't create context for device %s", defDevName);
} }
} }
else else
{ {
PanicAlertT("OpenAL: can't open device %s", defDevName); PanicAlertT("OpenAL: can't open device %s", defDevName);
} }
} }
else else
{ {
PanicAlertT("OpenAL: can't find sound devices"); PanicAlertT("OpenAL: can't find sound devices");
} }
// Initialize DPL2 parameters // Initialize DPL2 parameters
DPL2Reset(); DPL2Reset();
soundTouch.clear(); soundTouch.clear();
return bReturn; return bReturn;
} }
void OpenALStream::Stop() void OpenALStream::Stop()
{ {
m_run_thread.store(false); m_run_thread.store(false);
// kick the thread if it's waiting // kick the thread if it's waiting
soundSyncEvent.Set(); soundSyncEvent.Set();
soundTouch.clear(); soundTouch.clear();
thread.join(); thread.join();
alSourceStop(uiSource); alSourceStop(uiSource);
alSourcei(uiSource, AL_BUFFER, 0); alSourcei(uiSource, AL_BUFFER, 0);
// Clean up buffers and sources // Clean up buffers and sources
alDeleteSources(1, &uiSource); alDeleteSources(1, &uiSource);
uiSource = 0; uiSource = 0;
alDeleteBuffers(numBuffers, uiBuffers); alDeleteBuffers(numBuffers, uiBuffers);
ALCcontext *pContext = alcGetCurrentContext(); ALCcontext* pContext = alcGetCurrentContext();
ALCdevice *pDevice = alcGetContextsDevice(pContext); ALCdevice* pDevice = alcGetContextsDevice(pContext);
alcMakeContextCurrent(nullptr); alcMakeContextCurrent(nullptr);
alcDestroyContext(pContext); alcDestroyContext(pContext);
alcCloseDevice(pDevice); alcCloseDevice(pDevice);
} }
void OpenALStream::SetVolume(int volume) void OpenALStream::SetVolume(int volume)
{ {
fVolume = (float)volume / 100.0f; fVolume = (float)volume / 100.0f;
if (uiSource) if (uiSource)
alSourcef(uiSource, AL_GAIN, fVolume); alSourcef(uiSource, AL_GAIN, fVolume);
} }
void OpenALStream::Update() void OpenALStream::Update()
{ {
soundSyncEvent.Set(); soundSyncEvent.Set();
} }
void OpenALStream::Clear(bool mute) void OpenALStream::Clear(bool mute)
{ {
m_muted = mute; m_muted = mute;
if (m_muted) if (m_muted)
{ {
soundTouch.clear(); soundTouch.clear();
alSourceStop(uiSource); alSourceStop(uiSource);
} }
else else
{ {
alSourcePlay(uiSource); alSourcePlay(uiSource);
} }
} }
void OpenALStream::SoundLoop() void OpenALStream::SoundLoop()
{ {
Common::SetCurrentThreadName("Audio thread - openal"); Common::SetCurrentThreadName("Audio thread - openal");
bool surround_capable = SConfig::GetInstance().bDPL2Decoder; bool surround_capable = SConfig::GetInstance().bDPL2Decoder;
#if defined(__APPLE__) #if defined(__APPLE__)
bool float32_capable = false; bool float32_capable = false;
const ALenum AL_FORMAT_STEREO_FLOAT32 = 0; const ALenum AL_FORMAT_STEREO_FLOAT32 = 0;
// OS X does not have the alext AL_FORMAT_51CHN32 yet. // OS X does not have the alext AL_FORMAT_51CHN32 yet.
surround_capable = false; surround_capable = false;
const ALenum AL_FORMAT_51CHN32 = 0; const ALenum AL_FORMAT_51CHN32 = 0;
const ALenum AL_FORMAT_51CHN16 = 0; const ALenum AL_FORMAT_51CHN16 = 0;
#else #else
bool float32_capable = true; bool float32_capable = true;
#endif #endif
u32 ulFrequency = m_mixer->GetSampleRate(); u32 ulFrequency = m_mixer->GetSampleRate();
numBuffers = SConfig::GetInstance().iLatency + 2; // OpenAL requires a minimum of two buffers numBuffers = SConfig::GetInstance().iLatency + 2; // OpenAL requires a minimum of two buffers
memset(uiBuffers, 0, numBuffers * sizeof(ALuint)); memset(uiBuffers, 0, numBuffers * sizeof(ALuint));
uiSource = 0; uiSource = 0;
// Checks if a X-Fi is being used. If it is, disable FLOAT32 support as this sound card has no support for it even though it reports it does. // Checks if a X-Fi is being used. If it is, disable FLOAT32 support as this sound card has no
if (strstr(alGetString(AL_RENDERER), "X-Fi")) // support for it even though it reports it does.
float32_capable = false; if (strstr(alGetString(AL_RENDERER), "X-Fi"))
float32_capable = false;
// Generate some AL Buffers for streaming // Generate some AL Buffers for streaming
alGenBuffers(numBuffers, (ALuint *)uiBuffers); alGenBuffers(numBuffers, (ALuint*)uiBuffers);
// Generate a Source to playback the Buffers // Generate a Source to playback the Buffers
alGenSources(1, &uiSource); alGenSources(1, &uiSource);
// Short Silence // Short Silence
if (float32_capable) if (float32_capable)
memset(sampleBuffer, 0, OAL_MAX_SAMPLES * numBuffers * FRAME_SURROUND_FLOAT); memset(sampleBuffer, 0, OAL_MAX_SAMPLES * numBuffers * FRAME_SURROUND_FLOAT);
else else
memset(sampleBuffer, 0, OAL_MAX_SAMPLES * numBuffers * FRAME_SURROUND_SHORT); memset(sampleBuffer, 0, OAL_MAX_SAMPLES * numBuffers * FRAME_SURROUND_SHORT);
memset(realtimeBuffer, 0, OAL_MAX_SAMPLES * FRAME_STEREO_SHORT); memset(realtimeBuffer, 0, OAL_MAX_SAMPLES * FRAME_STEREO_SHORT);
for (int i = 0; i < numBuffers; i++) for (int i = 0; i < numBuffers; i++)
{ {
if (surround_capable) if (surround_capable)
{ {
if (float32_capable) if (float32_capable)
alBufferData(uiBuffers[i], AL_FORMAT_51CHN32, sampleBuffer, 4 * FRAME_SURROUND_FLOAT, ulFrequency); alBufferData(uiBuffers[i], AL_FORMAT_51CHN32, sampleBuffer, 4 * FRAME_SURROUND_FLOAT,
else ulFrequency);
alBufferData(uiBuffers[i], AL_FORMAT_51CHN16, sampleBuffer, 4 * FRAME_SURROUND_SHORT, ulFrequency); else
} alBufferData(uiBuffers[i], AL_FORMAT_51CHN16, sampleBuffer, 4 * FRAME_SURROUND_SHORT,
else ulFrequency);
{ }
alBufferData(uiBuffers[i], AL_FORMAT_STEREO16, realtimeBuffer, 4 * FRAME_STEREO_SHORT, ulFrequency); else
} {
} alBufferData(uiBuffers[i], AL_FORMAT_STEREO16, realtimeBuffer, 4 * FRAME_STEREO_SHORT,
alSourceQueueBuffers(uiSource, numBuffers, uiBuffers); ulFrequency);
alSourcePlay(uiSource); }
}
alSourceQueueBuffers(uiSource, numBuffers, uiBuffers);
alSourcePlay(uiSource);
// Set the default sound volume as saved in the config file. // Set the default sound volume as saved in the config file.
alSourcef(uiSource, AL_GAIN, fVolume); alSourcef(uiSource, AL_GAIN, fVolume);
// TODO: Error handling // TODO: Error handling
//ALenum err = alGetError(); // ALenum err = alGetError();
ALint iBuffersFilled = 0; ALint iBuffersFilled = 0;
ALint iBuffersProcessed = 0; ALint iBuffersProcessed = 0;
ALint iState = 0; ALint iState = 0;
ALuint uiBufferTemp[OAL_MAX_BUFFERS] = { 0 }; ALuint uiBufferTemp[OAL_MAX_BUFFERS] = {0};
soundTouch.setChannels(2); soundTouch.setChannels(2);
soundTouch.setSampleRate(ulFrequency); soundTouch.setSampleRate(ulFrequency);
soundTouch.setTempo(1.0); soundTouch.setTempo(1.0);
soundTouch.setSetting(SETTING_USE_QUICKSEEK, 0); soundTouch.setSetting(SETTING_USE_QUICKSEEK, 0);
soundTouch.setSetting(SETTING_USE_AA_FILTER, 0); soundTouch.setSetting(SETTING_USE_AA_FILTER, 0);
soundTouch.setSetting(SETTING_SEQUENCE_MS, 1); soundTouch.setSetting(SETTING_SEQUENCE_MS, 1);
soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 28); soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 28);
soundTouch.setSetting(SETTING_OVERLAP_MS, 12); soundTouch.setSetting(SETTING_OVERLAP_MS, 12);
while (m_run_thread.load()) while (m_run_thread.load())
{ {
// num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD. // num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD.
const u32 stereo_16_bit_size = 4; const u32 stereo_16_bit_size = 4;
const u32 dma_length = 32; const u32 dma_length = 32;
const u64 ais_samples_per_second = 48000 * stereo_16_bit_size; const u64 ais_samples_per_second = 48000 * stereo_16_bit_size;
u64 audio_dma_period = SystemTimers::GetTicksPerSecond() / (AudioInterface::GetAIDSampleRate() * stereo_16_bit_size / dma_length); u64 audio_dma_period = SystemTimers::GetTicksPerSecond() /
u64 num_samples_to_render = (audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond(); (AudioInterface::GetAIDSampleRate() * stereo_16_bit_size / dma_length);
u64 num_samples_to_render =
(audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond();
unsigned int numSamples = (unsigned int)num_samples_to_render; unsigned int numSamples = (unsigned int)num_samples_to_render;
unsigned int minSamples = surround_capable ? 240 : 0; // DPL2 accepts 240 samples minimum (FWRDURATION) unsigned int minSamples =
surround_capable ? 240 : 0; // DPL2 accepts 240 samples minimum (FWRDURATION)
numSamples = (numSamples > OAL_MAX_SAMPLES) ? OAL_MAX_SAMPLES : numSamples; numSamples = (numSamples > OAL_MAX_SAMPLES) ? OAL_MAX_SAMPLES : numSamples;
numSamples = m_mixer->Mix(realtimeBuffer, numSamples, false); numSamples = m_mixer->Mix(realtimeBuffer, numSamples, false);
// Convert the samples from short to float // Convert the samples from short to float
float dest[OAL_MAX_SAMPLES * STEREO_CHANNELS]; float dest[OAL_MAX_SAMPLES * STEREO_CHANNELS];
for (u32 i = 0; i < numSamples * STEREO_CHANNELS; ++i) for (u32 i = 0; i < numSamples * STEREO_CHANNELS; ++i)
dest[i] = (float)realtimeBuffer[i] / (1 << 15); dest[i] = (float)realtimeBuffer[i] / (1 << 15);
soundTouch.putSamples(dest, numSamples); soundTouch.putSamples(dest, numSamples);
if (iBuffersProcessed == iBuffersFilled) if (iBuffersProcessed == iBuffersFilled)
{ {
alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &iBuffersProcessed); alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &iBuffersProcessed);
iBuffersFilled = 0; iBuffersFilled = 0;
} }
if (iBuffersProcessed) if (iBuffersProcessed)
{ {
double rate = (double)m_mixer->GetCurrentSpeed(); double rate = (double)m_mixer->GetCurrentSpeed();
if (rate <= 0) if (rate <= 0)
{ {
Core::RequestRefreshInfo(); Core::RequestRefreshInfo();
rate = (double)m_mixer->GetCurrentSpeed(); rate = (double)m_mixer->GetCurrentSpeed();
} }
// Place a lower limit of 10% speed. When a game boots up, there will be // Place a lower limit of 10% speed. When a game boots up, there will be
// many silence samples. These do not need to be timestretched. // many silence samples. These do not need to be timestretched.
if (rate > 0.10) if (rate > 0.10)
{ {
soundTouch.setTempo(rate); soundTouch.setTempo(rate);
if (rate > 10) if (rate > 10)
{ {
soundTouch.clear(); soundTouch.clear();
} }
} }
unsigned int nSamples = soundTouch.receiveSamples(sampleBuffer, OAL_MAX_SAMPLES * numBuffers); unsigned int nSamples = soundTouch.receiveSamples(sampleBuffer, OAL_MAX_SAMPLES * numBuffers);
if (nSamples <= minSamples) if (nSamples <= minSamples)
continue; continue;
// Remove the Buffer from the Queue. (uiBuffer contains the Buffer ID for the unqueued Buffer) // Remove the Buffer from the Queue. (uiBuffer contains the Buffer ID for the unqueued
if (iBuffersFilled == 0) // Buffer)
{ if (iBuffersFilled == 0)
alSourceUnqueueBuffers(uiSource, iBuffersProcessed, uiBufferTemp); {
ALenum err = alGetError(); alSourceUnqueueBuffers(uiSource, iBuffersProcessed, uiBufferTemp);
if (err != 0) ALenum err = alGetError();
{ if (err != 0)
ERROR_LOG(AUDIO, "Error unqueuing buffers: %08x", err); {
} ERROR_LOG(AUDIO, "Error unqueuing buffers: %08x", err);
} }
}
if (surround_capable) if (surround_capable)
{ {
float dpl2[OAL_MAX_SAMPLES * OAL_MAX_BUFFERS * SURROUND_CHANNELS]; float dpl2[OAL_MAX_SAMPLES * OAL_MAX_BUFFERS * SURROUND_CHANNELS];
DPL2Decode(sampleBuffer, nSamples, dpl2); DPL2Decode(sampleBuffer, nSamples, dpl2);
// zero-out the subwoofer channel - DPL2Decode generates a pretty // zero-out the subwoofer channel - DPL2Decode generates a pretty
// good 5.0 but not a good 5.1 output. Sadly there is not a 5.0 // good 5.0 but not a good 5.1 output. Sadly there is not a 5.0
// AL_FORMAT_50CHN32 to make this super-explicit. // AL_FORMAT_50CHN32 to make this super-explicit.
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR // DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
for (u32 i = 0; i < nSamples; ++i) for (u32 i = 0; i < nSamples; ++i)
{ {
dpl2[i*SURROUND_CHANNELS + 3 /*sub/lfe*/] = 0.0f; dpl2[i * SURROUND_CHANNELS + 3 /*sub/lfe*/] = 0.0f;
} }
if (float32_capable) if (float32_capable)
{ {
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_51CHN32, dpl2, nSamples * FRAME_SURROUND_FLOAT, ulFrequency); alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_51CHN32, dpl2,
} nSamples * FRAME_SURROUND_FLOAT, ulFrequency);
else }
{ else
short surround_short[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS]; {
for (u32 i = 0; i < nSamples * SURROUND_CHANNELS; ++i) short surround_short[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS];
surround_short[i] = (short)((float)dpl2[i] * (1 << 15)); for (u32 i = 0; i < nSamples * SURROUND_CHANNELS; ++i)
surround_short[i] = (short)((float)dpl2[i] * (1 << 15));
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_51CHN16, surround_short, nSamples * FRAME_SURROUND_SHORT, ulFrequency); alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_51CHN16, surround_short,
} nSamples * FRAME_SURROUND_SHORT, ulFrequency);
}
ALenum err = alGetError(); ALenum err = alGetError();
if (err == AL_INVALID_ENUM) if (err == AL_INVALID_ENUM)
{ {
// 5.1 is not supported by the host, fallback to stereo // 5.1 is not supported by the host, fallback to stereo
WARN_LOG(AUDIO, "Unable to set 5.1 surround mode. Updating OpenAL Soft might fix this issue."); WARN_LOG(AUDIO,
surround_capable = false; "Unable to set 5.1 surround mode. Updating OpenAL Soft might fix this issue.");
} surround_capable = false;
else if (err != 0) }
{ else if (err != 0)
ERROR_LOG(AUDIO, "Error occurred while buffering data: %08x", err); {
} ERROR_LOG(AUDIO, "Error occurred while buffering data: %08x", err);
} }
}
else else
{ {
if (float32_capable) if (float32_capable)
{ {
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO_FLOAT32, sampleBuffer, nSamples * FRAME_STEREO_FLOAT, ulFrequency); alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO_FLOAT32, sampleBuffer,
ALenum err = alGetError(); nSamples * FRAME_STEREO_FLOAT, ulFrequency);
if (err == AL_INVALID_ENUM) ALenum err = alGetError();
{ if (err == AL_INVALID_ENUM)
float32_capable = false; {
} float32_capable = false;
else if (err != 0) }
{ else if (err != 0)
ERROR_LOG(AUDIO, "Error occurred while buffering float32 data: %08x", err); {
} ERROR_LOG(AUDIO, "Error occurred while buffering float32 data: %08x", err);
} }
}
else else
{ {
// Convert the samples from float to short // Convert the samples from float to short
short stereo[OAL_MAX_SAMPLES * STEREO_CHANNELS * OAL_MAX_BUFFERS]; short stereo[OAL_MAX_SAMPLES * STEREO_CHANNELS * OAL_MAX_BUFFERS];
for (u32 i = 0; i < nSamples * STEREO_CHANNELS; ++i) for (u32 i = 0; i < nSamples * STEREO_CHANNELS; ++i)
stereo[i] = (short)((float)sampleBuffer[i] * (1 << 15)); stereo[i] = (short)((float)sampleBuffer[i] * (1 << 15));
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO16, stereo, nSamples * FRAME_STEREO_SHORT, ulFrequency); alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO16, stereo,
} nSamples * FRAME_STEREO_SHORT, ulFrequency);
} }
}
alSourceQueueBuffers(uiSource, 1, &uiBufferTemp[iBuffersFilled]); alSourceQueueBuffers(uiSource, 1, &uiBufferTemp[iBuffersFilled]);
ALenum err = alGetError(); ALenum err = alGetError();
if (err != 0) if (err != 0)
{ {
ERROR_LOG(AUDIO, "Error queuing buffers: %08x", err); ERROR_LOG(AUDIO, "Error queuing buffers: %08x", err);
} }
iBuffersFilled++; iBuffersFilled++;
if (iBuffersFilled == numBuffers) if (iBuffersFilled == numBuffers)
{ {
alSourcePlay(uiSource); alSourcePlay(uiSource);
err = alGetError(); err = alGetError();
if (err != 0) if (err != 0)
{ {
ERROR_LOG(AUDIO, "Error occurred during playback: %08x", err); ERROR_LOG(AUDIO, "Error occurred during playback: %08x", err);
} }
} }
alGetSourcei(uiSource, AL_SOURCE_STATE, &iState); alGetSourcei(uiSource, AL_SOURCE_STATE, &iState);
if (iState != AL_PLAYING) if (iState != AL_PLAYING)
{ {
// Buffer underrun occurred, resume playback // Buffer underrun occurred, resume playback
alSourcePlay(uiSource); alSourcePlay(uiSource);
err = alGetError(); err = alGetError();
if (err != 0) if (err != 0)
{ {
ERROR_LOG(AUDIO, "Error occurred resuming playback: %08x", err); ERROR_LOG(AUDIO, "Error occurred resuming playback: %08x", err);
} }
} }
} }
else else
{ {
soundSyncEvent.Wait(); soundSyncEvent.Wait();
} }
} }
} }
#endif //HAVE_OPENAL #endif // HAVE_OPENAL

View File

@ -32,56 +32,52 @@
#define BOOL SoundTouch_BOOL #define BOOL SoundTouch_BOOL
#endif #endif
#include <soundtouch/SoundTouch.h>
#include <soundtouch/STTypes.h> #include <soundtouch/STTypes.h>
#include <soundtouch/SoundTouch.h>
#ifdef __APPLE__ #ifdef __APPLE__
#undef BOOL #undef BOOL
#endif #endif
// 16 bit Stereo // 16 bit Stereo
#define SFX_MAX_SOURCE 1 #define SFX_MAX_SOURCE 1
#define OAL_MAX_BUFFERS 32 #define OAL_MAX_BUFFERS 32
#define OAL_MAX_SAMPLES 256 #define OAL_MAX_SAMPLES 256
#define STEREO_CHANNELS 2 #define STEREO_CHANNELS 2
#define SURROUND_CHANNELS 6 // number of channels in surround mode #define SURROUND_CHANNELS 6 // number of channels in surround mode
#define SIZE_SHORT 2 #define SIZE_SHORT 2
#define SIZE_FLOAT 4 // size of a float in bytes #define SIZE_FLOAT 4 // size of a float in bytes
#define FRAME_STEREO_SHORT STEREO_CHANNELS * SIZE_SHORT #define FRAME_STEREO_SHORT STEREO_CHANNELS* SIZE_SHORT
#define FRAME_STEREO_FLOAT STEREO_CHANNELS * SIZE_FLOAT #define FRAME_STEREO_FLOAT STEREO_CHANNELS* SIZE_FLOAT
#define FRAME_SURROUND_FLOAT SURROUND_CHANNELS * SIZE_FLOAT #define FRAME_SURROUND_FLOAT SURROUND_CHANNELS* SIZE_FLOAT
#define FRAME_SURROUND_SHORT SURROUND_CHANNELS * SIZE_SHORT #define FRAME_SURROUND_SHORT SURROUND_CHANNELS* SIZE_SHORT
#endif #endif
class OpenALStream final : public SoundStream class OpenALStream final : public SoundStream
{ {
#if defined HAVE_OPENAL && HAVE_OPENAL #if defined HAVE_OPENAL && HAVE_OPENAL
public: public:
OpenALStream() : uiSource(0) OpenALStream() : uiSource(0) {}
{ bool Start() override;
} void SoundLoop() override;
void SetVolume(int volume) override;
bool Start() override; void Stop() override;
void SoundLoop() override; void Clear(bool mute) override;
void SetVolume(int volume) override; void Update() override;
void Stop() override;
void Clear(bool mute) override;
void Update() override;
static bool isValid() { return true; }
static bool isValid() { return true; }
private: private:
std::thread thread; std::thread thread;
std::atomic<bool> m_run_thread; std::atomic<bool> m_run_thread;
Common::Event soundSyncEvent; Common::Event soundSyncEvent;
short realtimeBuffer[OAL_MAX_SAMPLES * STEREO_CHANNELS]; short realtimeBuffer[OAL_MAX_SAMPLES * STEREO_CHANNELS];
soundtouch::SAMPLETYPE sampleBuffer[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS]; soundtouch::SAMPLETYPE sampleBuffer[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS];
ALuint uiBuffers[OAL_MAX_BUFFERS]; ALuint uiBuffers[OAL_MAX_BUFFERS];
ALuint uiSource; ALuint uiSource;
ALfloat fVolume; ALfloat fVolume;
u8 numBuffers; u8 numBuffers;
#endif // HAVE_OPENAL #endif // HAVE_OPENAL
}; };

View File

@ -24,7 +24,7 @@ static SLPlayItf bqPlayerPlay;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
static SLMuteSoloItf bqPlayerMuteSolo; static SLMuteSoloItf bqPlayerMuteSolo;
static SLVolumeItf bqPlayerVolume; static SLVolumeItf bqPlayerVolume;
static CMixer *g_mixer; static CMixer* g_mixer;
#define BUFFER_SIZE 512 #define BUFFER_SIZE 512
#define BUFFER_SIZE_IN_SAMPLES (BUFFER_SIZE / 2) #define BUFFER_SIZE_IN_SAMPLES (BUFFER_SIZE / 2)
@ -32,106 +32,107 @@ static CMixer *g_mixer;
static short buffer[2][BUFFER_SIZE]; static short buffer[2][BUFFER_SIZE];
static int curBuffer = 0; static int curBuffer = 0;
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{ {
assert(bq == bqPlayerBufferQueue); assert(bq == bqPlayerBufferQueue);
assert(nullptr == context); assert(nullptr == context);
// Render to the fresh buffer // Render to the fresh buffer
g_mixer->Mix(reinterpret_cast<short *>(buffer[curBuffer]), BUFFER_SIZE_IN_SAMPLES); g_mixer->Mix(reinterpret_cast<short*>(buffer[curBuffer]), BUFFER_SIZE_IN_SAMPLES);
SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeof(buffer[0])); SLresult result =
curBuffer ^= 1; // Switch buffer (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeof(buffer[0]));
curBuffer ^= 1; // Switch buffer
// Comment from sample code: // Comment from sample code:
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT, // the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
// which for this code example would indicate a programming error // which for this code example would indicate a programming error
_assert_msg_(AUDIO, SL_RESULT_SUCCESS == result, "Couldn't enqueue audio stream."); _assert_msg_(AUDIO, SL_RESULT_SUCCESS == result, "Couldn't enqueue audio stream.");
} }
bool OpenSLESStream::Start() bool OpenSLESStream::Start()
{ {
SLresult result; SLresult result;
// create engine // create engine
result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr); result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
assert(SL_RESULT_SUCCESS == result); assert(SL_RESULT_SUCCESS == result);
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result); assert(SL_RESULT_SUCCESS == result);
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
assert(SL_RESULT_SUCCESS == result); assert(SL_RESULT_SUCCESS == result);
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0); result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
assert(SL_RESULT_SUCCESS == result); assert(SL_RESULT_SUCCESS == result);
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result); assert(SL_RESULT_SUCCESS == result);
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = { SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,
SL_DATAFORMAT_PCM, 2,
2, m_mixer->GetSampleRate() * 1000,
m_mixer->GetSampleRate() * 1000, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN};
SL_BYTEORDER_LITTLEENDIAN
};
SLDataSource audioSrc = {&loc_bufq, &format_pcm}; SLDataSource audioSrc = {&loc_bufq, &format_pcm};
// configure audio sink // configure audio sink
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject}; SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
SLDataSink audioSnk = {&loc_outmix, nullptr}; SLDataSink audioSnk = {&loc_outmix, nullptr};
// create audio player // create audio player
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME}; const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req); result =
assert(SL_RESULT_SUCCESS == result); (*engineEngine)
->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result); assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
assert(SL_RESULT_SUCCESS == result); assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, result =
&bqPlayerBufferQueue); (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);
assert(SL_RESULT_SUCCESS == result); assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr); result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr);
assert(SL_RESULT_SUCCESS == result); assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
assert(SL_RESULT_SUCCESS == result); assert(SL_RESULT_SUCCESS == result);
// Render and enqueue a first buffer. // Render and enqueue a first buffer.
curBuffer ^= 1; curBuffer ^= 1;
g_mixer = m_mixer.get(); g_mixer = m_mixer.get();
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[0], sizeof(buffer[0])); result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[0], sizeof(buffer[0]));
if (SL_RESULT_SUCCESS != result) if (SL_RESULT_SUCCESS != result)
return false; return false;
return true; return true;
} }
void OpenSLESStream::Stop() void OpenSLESStream::Stop()
{ {
if (bqPlayerObject != nullptr) if (bqPlayerObject != nullptr)
{ {
(*bqPlayerObject)->Destroy(bqPlayerObject); (*bqPlayerObject)->Destroy(bqPlayerObject);
bqPlayerObject = nullptr; bqPlayerObject = nullptr;
bqPlayerPlay = nullptr; bqPlayerPlay = nullptr;
bqPlayerBufferQueue = nullptr; bqPlayerBufferQueue = nullptr;
bqPlayerMuteSolo = nullptr; bqPlayerMuteSolo = nullptr;
bqPlayerVolume = nullptr; bqPlayerVolume = nullptr;
} }
if (outputMixObject != nullptr) if (outputMixObject != nullptr)
{ {
(*outputMixObject)->Destroy(outputMixObject); (*outputMixObject)->Destroy(outputMixObject);
outputMixObject = nullptr; outputMixObject = nullptr;
} }
if (engineObject != nullptr) if (engineObject != nullptr)
{ {
(*engineObject)->Destroy(engineObject); (*engineObject)->Destroy(engineObject);
engineObject = nullptr; engineObject = nullptr;
engineEngine = nullptr; engineEngine = nullptr;
} }
} }
#endif #endif

View File

@ -13,12 +13,11 @@ class OpenSLESStream final : public SoundStream
{ {
#ifdef ANDROID #ifdef ANDROID
public: public:
bool Start() override; bool Start() override;
void Stop() override; void Stop() override;
static bool isValid() { return true; } static bool isValid() { return true; }
private: private:
std::thread thread; std::thread thread;
Common::Event soundSyncEvent; Common::Event soundSyncEvent;
#endif // HAVE_OPENSL #endif // HAVE_OPENSL
}; };

View File

@ -7,245 +7,247 @@
#include "AudioCommon/DPL2Decoder.h" #include "AudioCommon/DPL2Decoder.h"
#include "AudioCommon/PulseAudioStream.h" #include "AudioCommon/PulseAudioStream.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Thread.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/Thread.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
namespace namespace
{ {
const size_t BUFFER_SAMPLES = 512; // ~10 ms - needs to be at least 240 for surround const size_t BUFFER_SAMPLES = 512; // ~10 ms - needs to be at least 240 for surround
} }
PulseAudio::PulseAudio() PulseAudio::PulseAudio() : m_thread(), m_run_thread()
: m_thread()
, m_run_thread()
{ {
} }
bool PulseAudio::Start() bool PulseAudio::Start()
{ {
m_stereo = !SConfig::GetInstance().bDPL2Decoder; m_stereo = !SConfig::GetInstance().bDPL2Decoder;
m_channels = m_stereo ? 2 : 5; // will tell PA we use a Stereo or 5.0 channel setup m_channels = m_stereo ? 2 : 5; // will tell PA we use a Stereo or 5.0 channel setup
NOTICE_LOG(AUDIO, "PulseAudio backend using %d channels", m_channels); NOTICE_LOG(AUDIO, "PulseAudio backend using %d channels", m_channels);
m_run_thread = true; m_run_thread = true;
m_thread = std::thread(&PulseAudio::SoundLoop, this); m_thread = std::thread(&PulseAudio::SoundLoop, this);
// Initialize DPL2 parameters // Initialize DPL2 parameters
DPL2Reset(); DPL2Reset();
return true; return true;
} }
void PulseAudio::Stop() void PulseAudio::Stop()
{ {
m_run_thread = false; m_run_thread = false;
m_thread.join(); m_thread.join();
} }
void PulseAudio::Update() void PulseAudio::Update()
{ {
// don't need to do anything here. // don't need to do anything here.
} }
// Called on audio thread. // Called on audio thread.
void PulseAudio::SoundLoop() void PulseAudio::SoundLoop()
{ {
Common::SetCurrentThreadName("Audio thread - pulse"); Common::SetCurrentThreadName("Audio thread - pulse");
if (PulseInit()) if (PulseInit())
{ {
while (m_run_thread.load() && m_pa_connected == 1 && m_pa_error >= 0) while (m_run_thread.load() && m_pa_connected == 1 && m_pa_error >= 0)
m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr); m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr);
if (m_pa_error < 0) if (m_pa_error < 0)
ERROR_LOG(AUDIO, "PulseAudio error: %s", pa_strerror(m_pa_error)); ERROR_LOG(AUDIO, "PulseAudio error: %s", pa_strerror(m_pa_error));
PulseShutdown(); PulseShutdown();
} }
} }
bool PulseAudio::PulseInit() bool PulseAudio::PulseInit()
{ {
m_pa_error = 0; m_pa_error = 0;
m_pa_connected = 0; m_pa_connected = 0;
// create pulseaudio main loop and context // create pulseaudio main loop and context
// also register the async state callback which is called when the connection to the pa server has changed // also register the async state callback which is called when the connection to the pa server has
m_pa_ml = pa_mainloop_new(); // changed
m_pa_mlapi = pa_mainloop_get_api(m_pa_ml); m_pa_ml = pa_mainloop_new();
m_pa_ctx = pa_context_new(m_pa_mlapi, "dolphin-emu"); m_pa_mlapi = pa_mainloop_get_api(m_pa_ml);
m_pa_error = pa_context_connect(m_pa_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr); m_pa_ctx = pa_context_new(m_pa_mlapi, "dolphin-emu");
pa_context_set_state_callback(m_pa_ctx, StateCallback, this); m_pa_error = pa_context_connect(m_pa_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr);
pa_context_set_state_callback(m_pa_ctx, StateCallback, this);
// wait until we're connected to the pulseaudio server // wait until we're connected to the pulseaudio server
while (m_pa_connected == 0 && m_pa_error >= 0) while (m_pa_connected == 0 && m_pa_error >= 0)
m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr); m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr);
if (m_pa_connected == 2 || m_pa_error < 0) if (m_pa_connected == 2 || m_pa_error < 0)
{ {
ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error)); ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error));
return false; return false;
} }
// create a new audio stream with our sample format // create a new audio stream with our sample format
// also connect the callbacks for this stream // also connect the callbacks for this stream
pa_sample_spec ss; pa_sample_spec ss;
pa_channel_map channel_map; pa_channel_map channel_map;
pa_channel_map* channel_map_p = nullptr; // auto channel map pa_channel_map* channel_map_p = nullptr; // auto channel map
if (m_stereo) if (m_stereo)
{ {
ss.format = PA_SAMPLE_S16LE; ss.format = PA_SAMPLE_S16LE;
m_bytespersample = sizeof(s16); m_bytespersample = sizeof(s16);
} }
else else
{ {
// surround is remixed in floats, use a float PA buffer to save another conversion // surround is remixed in floats, use a float PA buffer to save another conversion
ss.format = PA_SAMPLE_FLOAT32NE; ss.format = PA_SAMPLE_FLOAT32NE;
m_bytespersample = sizeof(float); m_bytespersample = sizeof(float);
channel_map_p = &channel_map; // explicit channel map: channel_map_p = &channel_map; // explicit channel map:
channel_map.channels = 5; channel_map.channels = 5;
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
channel_map.map[3] = PA_CHANNEL_POSITION_REAR_LEFT; channel_map.map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
channel_map.map[4] = PA_CHANNEL_POSITION_REAR_RIGHT; channel_map.map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
} }
ss.channels = m_channels; ss.channels = m_channels;
ss.rate = m_mixer->GetSampleRate(); ss.rate = m_mixer->GetSampleRate();
assert(pa_sample_spec_valid(&ss)); assert(pa_sample_spec_valid(&ss));
m_pa_s = pa_stream_new(m_pa_ctx, "Playback", &ss, channel_map_p); m_pa_s = pa_stream_new(m_pa_ctx, "Playback", &ss, channel_map_p);
pa_stream_set_write_callback(m_pa_s, WriteCallback, this); pa_stream_set_write_callback(m_pa_s, WriteCallback, this);
pa_stream_set_underflow_callback(m_pa_s, UnderflowCallback, this); pa_stream_set_underflow_callback(m_pa_s, UnderflowCallback, this);
// connect this audio stream to the default audio playback // connect this audio stream to the default audio playback
// limit buffersize to reduce latency // limit buffersize to reduce latency
m_pa_ba.fragsize = -1; m_pa_ba.fragsize = -1;
m_pa_ba.maxlength = -1; // max buffer, so also max latency m_pa_ba.maxlength = -1; // max buffer, so also max latency
m_pa_ba.minreq = -1; // don't read every byte, try to group them _a bit_ m_pa_ba.minreq = -1; // don't read every byte, try to group them _a bit_
m_pa_ba.prebuf = -1; // start as early as possible m_pa_ba.prebuf = -1; // start as early as possible
m_pa_ba.tlength = BUFFER_SAMPLES * m_channels * m_bytespersample; // designed latency, only change this flag for low latency output m_pa_ba.tlength =
pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); BUFFER_SAMPLES * m_channels *
m_pa_error = pa_stream_connect_playback(m_pa_s, nullptr, &m_pa_ba, flags, nullptr, nullptr); m_bytespersample; // designed latency, only change this flag for low latency output
if (m_pa_error < 0) pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY |
{ PA_STREAM_AUTO_TIMING_UPDATE);
ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error)); m_pa_error = pa_stream_connect_playback(m_pa_s, nullptr, &m_pa_ba, flags, nullptr, nullptr);
return false; if (m_pa_error < 0)
} {
ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error));
return false;
}
INFO_LOG(AUDIO, "Pulse successfully initialized"); INFO_LOG(AUDIO, "Pulse successfully initialized");
return true; return true;
} }
void PulseAudio::PulseShutdown() void PulseAudio::PulseShutdown()
{ {
pa_context_disconnect(m_pa_ctx); pa_context_disconnect(m_pa_ctx);
pa_context_unref(m_pa_ctx); pa_context_unref(m_pa_ctx);
pa_mainloop_free(m_pa_ml); pa_mainloop_free(m_pa_ml);
} }
void PulseAudio::StateCallback(pa_context* c) void PulseAudio::StateCallback(pa_context* c)
{ {
pa_context_state_t state = pa_context_get_state(c); pa_context_state_t state = pa_context_get_state(c);
switch (state) switch (state)
{ {
case PA_CONTEXT_FAILED: case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED: case PA_CONTEXT_TERMINATED:
m_pa_connected = 2; m_pa_connected = 2;
break; break;
case PA_CONTEXT_READY: case PA_CONTEXT_READY:
m_pa_connected = 1; m_pa_connected = 1;
break; break;
default: default:
break; break;
} }
} }
// on underflow, increase pulseaudio latency in ~10ms steps // on underflow, increase pulseaudio latency in ~10ms steps
void PulseAudio::UnderflowCallback(pa_stream* s) void PulseAudio::UnderflowCallback(pa_stream* s)
{ {
m_pa_ba.tlength += BUFFER_SAMPLES * m_channels * m_bytespersample; m_pa_ba.tlength += BUFFER_SAMPLES * m_channels * m_bytespersample;
pa_operation* op = pa_stream_set_buffer_attr(s, &m_pa_ba, nullptr, nullptr); pa_operation* op = pa_stream_set_buffer_attr(s, &m_pa_ba, nullptr, nullptr);
pa_operation_unref(op); pa_operation_unref(op);
WARN_LOG(AUDIO, "pulseaudio underflow, new latency: %d bytes", m_pa_ba.tlength); WARN_LOG(AUDIO, "pulseaudio underflow, new latency: %d bytes", m_pa_ba.tlength);
} }
void PulseAudio::WriteCallback(pa_stream* s, size_t length) void PulseAudio::WriteCallback(pa_stream* s, size_t length)
{ {
int bytes_per_frame = m_channels * m_bytespersample; int bytes_per_frame = m_channels * m_bytespersample;
int frames = (length / bytes_per_frame); int frames = (length / bytes_per_frame);
size_t trunc_length = frames * bytes_per_frame; size_t trunc_length = frames * bytes_per_frame;
// fetch dst buffer directly from pulseaudio, so no memcpy is needed // fetch dst buffer directly from pulseaudio, so no memcpy is needed
void* buffer; void* buffer;
m_pa_error = pa_stream_begin_write(s, &buffer, &trunc_length); m_pa_error = pa_stream_begin_write(s, &buffer, &trunc_length);
if (!buffer || m_pa_error < 0) if (!buffer || m_pa_error < 0)
return; // error will be printed from main loop return; // error will be printed from main loop
if (m_stereo) if (m_stereo)
{ {
// use the raw s16 stereo mix // use the raw s16 stereo mix
m_mixer->Mix((s16*) buffer, frames); m_mixer->Mix((s16*)buffer, frames);
} }
else else
{ {
// get a floating point mix // get a floating point mix
s16 s16buffer_stereo[frames * 2]; s16 s16buffer_stereo[frames * 2];
m_mixer->Mix(s16buffer_stereo, frames); // implicitly mixes to 16-bit stereo m_mixer->Mix(s16buffer_stereo, frames); // implicitly mixes to 16-bit stereo
float floatbuffer_stereo[frames * 2]; float floatbuffer_stereo[frames * 2];
// s16 to float // s16 to float
for (int i=0; i < frames * 2; ++i) for (int i = 0; i < frames * 2; ++i)
{ {
floatbuffer_stereo[i] = s16buffer_stereo[i] / float(1 << 15); floatbuffer_stereo[i] = s16buffer_stereo[i] / float(1 << 15);
} }
if (m_channels == 5) // Extract dpl2/5.0 Surround if (m_channels == 5) // Extract dpl2/5.0 Surround
{ {
float floatbuffer_6chan[frames * 6]; float floatbuffer_6chan[frames * 6];
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR // DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
DPL2Decode(floatbuffer_stereo, frames, floatbuffer_6chan); DPL2Decode(floatbuffer_stereo, frames, floatbuffer_6chan);
// Discard the subwoofer channel - DPL2Decode generates a pretty // Discard the subwoofer channel - DPL2Decode generates a pretty
// good 5.0 but not a good 5.1 output. // good 5.0 but not a good 5.1 output.
const int dpl2_to_5chan[] = {0,1,2,4,5}; const int dpl2_to_5chan[] = {0, 1, 2, 4, 5};
for (int i=0; i < frames; ++i) for (int i = 0; i < frames; ++i)
{ {
for (int j=0; j < m_channels; ++j) for (int j = 0; j < m_channels; ++j)
{ {
((float*)buffer)[m_channels * i + j] = floatbuffer_6chan[6 * i + dpl2_to_5chan[j]]; ((float*)buffer)[m_channels * i + j] = floatbuffer_6chan[6 * i + dpl2_to_5chan[j]];
} }
} }
} }
else else
{ {
ERROR_LOG(AUDIO, "Unsupported number of PA channels requested: %d", (int)m_channels); ERROR_LOG(AUDIO, "Unsupported number of PA channels requested: %d", (int)m_channels);
return; return;
} }
} }
m_pa_error = pa_stream_write(s, buffer, trunc_length, nullptr, 0, PA_SEEK_RELATIVE); m_pa_error = pa_stream_write(s, buffer, trunc_length, nullptr, 0, PA_SEEK_RELATIVE);
} }
// Callbacks that forward to internal methods (required because PulseAudio is a C API). // Callbacks that forward to internal methods (required because PulseAudio is a C API).
void PulseAudio::StateCallback(pa_context* c, void* userdata) void PulseAudio::StateCallback(pa_context* c, void* userdata)
{ {
PulseAudio* p = (PulseAudio*) userdata; PulseAudio* p = (PulseAudio*)userdata;
p->StateCallback(c); p->StateCallback(c);
} }
void PulseAudio::UnderflowCallback(pa_stream* s, void* userdata) void PulseAudio::UnderflowCallback(pa_stream* s, void* userdata)
{ {
PulseAudio* p = (PulseAudio*) userdata; PulseAudio* p = (PulseAudio*)userdata;
p->UnderflowCallback(s); p->UnderflowCallback(s);
} }
void PulseAudio::WriteCallback(pa_stream* s, size_t length, void* userdata) void PulseAudio::WriteCallback(pa_stream* s, size_t length, void* userdata)
{ {
PulseAudio* p = (PulseAudio*) userdata; PulseAudio* p = (PulseAudio*)userdata;
p->WriteCallback(s, length); p->WriteCallback(s, length);
} }

View File

@ -18,42 +18,41 @@ class PulseAudio final : public SoundStream
{ {
#if defined(HAVE_PULSEAUDIO) && HAVE_PULSEAUDIO #if defined(HAVE_PULSEAUDIO) && HAVE_PULSEAUDIO
public: public:
PulseAudio(); PulseAudio();
bool Start() override; bool Start() override;
void Stop() override; void Stop() override;
void Update() override; void Update() override;
static bool isValid() { return true; } static bool isValid() { return true; }
void StateCallback(pa_context* c);
void StateCallback(pa_context *c); void WriteCallback(pa_stream* s, size_t length);
void WriteCallback(pa_stream *s, size_t length); void UnderflowCallback(pa_stream* s);
void UnderflowCallback(pa_stream *s);
private: private:
void SoundLoop() override; void SoundLoop() override;
bool PulseInit(); bool PulseInit();
void PulseShutdown(); void PulseShutdown();
// wrapper callback functions, last parameter _must_ be PulseAudio* // wrapper callback functions, last parameter _must_ be PulseAudio*
static void StateCallback(pa_context *c, void *userdata); static void StateCallback(pa_context* c, void* userdata);
static void WriteCallback(pa_stream *s, size_t length, void *userdata); static void WriteCallback(pa_stream* s, size_t length, void* userdata);
static void UnderflowCallback(pa_stream *s, void *userdata); static void UnderflowCallback(pa_stream* s, void* userdata);
std::thread m_thread; std::thread m_thread;
std::atomic<bool> m_run_thread; std::atomic<bool> m_run_thread;
bool m_stereo; // stereo, else surround bool m_stereo; // stereo, else surround
int m_bytespersample; int m_bytespersample;
int m_channels; int m_channels;
int m_pa_error; int m_pa_error;
int m_pa_connected; int m_pa_connected;
pa_mainloop *m_pa_ml; pa_mainloop* m_pa_ml;
pa_mainloop_api *m_pa_mlapi; pa_mainloop_api* m_pa_mlapi;
pa_context *m_pa_ctx; pa_context* m_pa_ctx;
pa_stream *m_pa_s; pa_stream* m_pa_s;
pa_buffer_attr m_pa_ba; pa_buffer_attr m_pa_ba;
#endif #endif
}; };

View File

@ -14,51 +14,49 @@
class SoundStream class SoundStream
{ {
protected: protected:
std::unique_ptr<CMixer> m_mixer; std::unique_ptr<CMixer> m_mixer;
bool m_logAudio; bool m_logAudio;
WaveFileWriter g_wave_writer; WaveFileWriter g_wave_writer;
bool m_muted; bool m_muted;
public: public:
SoundStream() : m_mixer(new CMixer(48000)), m_logAudio(false), m_muted(false) {} SoundStream() : m_mixer(new CMixer(48000)), m_logAudio(false), m_muted(false) {}
virtual ~SoundStream() { } virtual ~SoundStream() {}
static bool isValid() { return false; }
CMixer* GetMixer() const { return m_mixer.get(); }
virtual bool Start() { return false; }
virtual void SetVolume(int) {}
virtual void SoundLoop() {}
virtual void Stop() {}
virtual void Update() {}
virtual void Clear(bool mute) { m_muted = mute; }
bool IsMuted() const { return m_muted; }
void StartLogAudio(const std::string& filename)
{
if (!m_logAudio)
{
m_logAudio = true;
g_wave_writer.Start(filename, m_mixer->GetSampleRate());
g_wave_writer.SetSkipSilence(false);
NOTICE_LOG(AUDIO, "Starting Audio logging");
}
else
{
WARN_LOG(AUDIO, "Audio logging already started");
}
}
static bool isValid() { return false; } void StopLogAudio()
CMixer* GetMixer() const { return m_mixer.get(); } {
virtual bool Start() { return false; } if (m_logAudio)
virtual void SetVolume(int) {} {
virtual void SoundLoop() {} m_logAudio = false;
virtual void Stop() {} g_wave_writer.Stop();
virtual void Update() {} NOTICE_LOG(AUDIO, "Stopping Audio logging");
virtual void Clear(bool mute) { m_muted = mute; } }
bool IsMuted() const { return m_muted; } else
{
void StartLogAudio(const std::string& filename) WARN_LOG(AUDIO, "Audio logging already stopped");
{ }
if (!m_logAudio) }
{
m_logAudio = true;
g_wave_writer.Start(filename, m_mixer->GetSampleRate());
g_wave_writer.SetSkipSilence(false);
NOTICE_LOG(AUDIO, "Starting Audio logging");
}
else
{
WARN_LOG(AUDIO, "Audio logging already started");
}
}
void StopLogAudio()
{
if (m_logAudio)
{
m_logAudio = false;
g_wave_writer.Stop();
NOTICE_LOG(AUDIO, "Stopping Audio logging");
}
else
{
WARN_LOG(AUDIO, "Audio logging already stopped");
}
}
}; };

View File

@ -17,127 +17,130 @@ WaveFileWriter::WaveFileWriter()
WaveFileWriter::~WaveFileWriter() WaveFileWriter::~WaveFileWriter()
{ {
Stop(); Stop();
} }
bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRate) bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRate)
{ {
// Check if the file is already open // Check if the file is already open
if (file) if (file)
{ {
PanicAlertT("The file %s was already open, the file header will not be written.", filename.c_str()); PanicAlertT("The file %s was already open, the file header will not be written.",
return false; filename.c_str());
} return false;
}
file.Open(filename, "wb"); file.Open(filename, "wb");
if (!file) if (!file)
{ {
PanicAlertT("The file %s could not be opened for writing. Please check if it's already opened by another program.", filename.c_str()); PanicAlertT("The file %s could not be opened for writing. Please check if it's already opened "
return false; "by another program.",
} filename.c_str());
return false;
}
audio_size = 0; audio_size = 0;
// ----------------- // -----------------
// Write file header // Write file header
// ----------------- // -----------------
Write4("RIFF"); Write4("RIFF");
Write(100 * 1000 * 1000); // write big value in case the file gets truncated Write(100 * 1000 * 1000); // write big value in case the file gets truncated
Write4("WAVE"); Write4("WAVE");
Write4("fmt "); Write4("fmt ");
Write(16); // size of fmt block Write(16); // size of fmt block
Write(0x00020001); //two channels, uncompressed Write(0x00020001); // two channels, uncompressed
const u32 sample_rate = HLESampleRate; const u32 sample_rate = HLESampleRate;
Write(sample_rate); Write(sample_rate);
Write(sample_rate * 2 * 2); //two channels, 16bit Write(sample_rate * 2 * 2); // two channels, 16bit
Write(0x00100004); Write(0x00100004);
Write4("data"); Write4("data");
Write(100 * 1000 * 1000 - 32); Write(100 * 1000 * 1000 - 32);
// We are now at offset 44 // We are now at offset 44
if (file.Tell() != 44) if (file.Tell() != 44)
PanicAlert("Wrong offset: %lld", (long long)file.Tell()); PanicAlert("Wrong offset: %lld", (long long)file.Tell());
return true; return true;
} }
void WaveFileWriter::Stop() void WaveFileWriter::Stop()
{ {
// u32 file_size = (u32)ftello(file); // u32 file_size = (u32)ftello(file);
file.Seek(4, SEEK_SET); file.Seek(4, SEEK_SET);
Write(audio_size + 36); Write(audio_size + 36);
file.Seek(40, SEEK_SET); file.Seek(40, SEEK_SET);
Write(audio_size); Write(audio_size);
file.Close(); file.Close();
} }
void WaveFileWriter::Write(u32 value) void WaveFileWriter::Write(u32 value)
{ {
file.WriteArray(&value, 1); file.WriteArray(&value, 1);
} }
void WaveFileWriter::Write4(const char *ptr) void WaveFileWriter::Write4(const char* ptr)
{ {
file.WriteBytes(ptr, 4); file.WriteBytes(ptr, 4);
} }
void WaveFileWriter::AddStereoSamples(const short *sample_data, u32 count) void WaveFileWriter::AddStereoSamples(const short* sample_data, u32 count)
{ {
if (!file) if (!file)
PanicAlertT("WaveFileWriter - file not open."); PanicAlertT("WaveFileWriter - file not open.");
if (skip_silence) if (skip_silence)
{ {
bool all_zero = true; bool all_zero = true;
for (u32 i = 0; i < count * 2; i++) for (u32 i = 0; i < count * 2; i++)
{ {
if (sample_data[i]) if (sample_data[i])
all_zero = false; all_zero = false;
} }
if (all_zero) if (all_zero)
return; return;
} }
file.WriteBytes(sample_data, count * 4); file.WriteBytes(sample_data, count * 4);
audio_size += count * 4; audio_size += count * 4;
} }
void WaveFileWriter::AddStereoSamplesBE(const short *sample_data, u32 count) void WaveFileWriter::AddStereoSamplesBE(const short* sample_data, u32 count)
{ {
if (!file) if (!file)
PanicAlertT("WaveFileWriter - file not open."); PanicAlertT("WaveFileWriter - file not open.");
if (count > BUFFER_SIZE * 2) if (count > BUFFER_SIZE * 2)
PanicAlert("WaveFileWriter - buffer too small (count = %u).", count); PanicAlert("WaveFileWriter - buffer too small (count = %u).", count);
if (skip_silence) if (skip_silence)
{ {
bool all_zero = true; bool all_zero = true;
for (u32 i = 0; i < count * 2; i++) for (u32 i = 0; i < count * 2; i++)
{ {
if (sample_data[i]) if (sample_data[i])
all_zero = false; all_zero = false;
} }
if (all_zero) if (all_zero)
return; return;
} }
for (u32 i = 0; i < count; i++) for (u32 i = 0; i < count; i++)
{ {
//Flip the audio channels from RL to LR // Flip the audio channels from RL to LR
conv_buffer[2 * i] = Common::swap16((u16)sample_data[2 * i + 1]); conv_buffer[2 * i] = Common::swap16((u16)sample_data[2 * i + 1]);
conv_buffer[2 * i + 1] = Common::swap16((u16)sample_data[2 * i]); conv_buffer[2 * i + 1] = Common::swap16((u16)sample_data[2 * i]);
} }
file.WriteBytes(conv_buffer.data(), count * 4); file.WriteBytes(conv_buffer.data(), count * 4);
audio_size += count * 4; audio_size += count * 4;
} }

View File

@ -23,25 +23,23 @@
class WaveFileWriter : NonCopyable class WaveFileWriter : NonCopyable
{ {
public: public:
WaveFileWriter(); WaveFileWriter();
~WaveFileWriter(); ~WaveFileWriter();
bool Start(const std::string& filename, unsigned int HLESampleRate); bool Start(const std::string& filename, unsigned int HLESampleRate);
void Stop(); void Stop();
void SetSkipSilence(bool skip) { skip_silence = skip; }
void AddStereoSamples(const short *sample_data, u32 count);
void AddStereoSamplesBE(const short *sample_data, u32 count); // big endian
u32 GetAudioSize() const { return audio_size; }
void SetSkipSilence(bool skip) { skip_silence = skip; }
void AddStereoSamples(const short* sample_data, u32 count);
void AddStereoSamplesBE(const short* sample_data, u32 count); // big endian
u32 GetAudioSize() const { return audio_size; }
private: private:
static constexpr size_t BUFFER_SIZE = 32 * 1024; static constexpr size_t BUFFER_SIZE = 32 * 1024;
File::IOFile file; File::IOFile file;
bool skip_silence = false; bool skip_silence = false;
u32 audio_size = 0; u32 audio_size = 0;
std::array<short, BUFFER_SIZE> conv_buffer{}; std::array<short, BUFFER_SIZE> conv_buffer{};
void Write(u32 value); void Write(u32 value);
void Write4(const char* ptr); void Write4(const char* ptr);
}; };

View File

@ -2,12 +2,12 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "AudioCommon/XAudio2Stream.h"
#include <xaudio2.h> #include <xaudio2.h>
#include "AudioCommon/AudioCommon.h" #include "AudioCommon/AudioCommon.h"
#include "AudioCommon/XAudio2Stream.h"
#include "Common/Event.h" #include "Common/Event.h"
#include "Common/MsgHandler.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#ifndef XAUDIO2_DLL #ifndef XAUDIO2_DLL
#error You are building this module against the wrong version of DirectX. You probably need to remove DXSDK_DIR from your include path. #error You are building this module against the wrong version of DirectX. You probably need to remove DXSDK_DIR from your include path.
@ -16,29 +16,28 @@
struct StreamingVoiceContext : public IXAudio2VoiceCallback struct StreamingVoiceContext : public IXAudio2VoiceCallback
{ {
private: private:
CMixer* const m_mixer; CMixer* const m_mixer;
Common::Event& m_sound_sync_event; Common::Event& m_sound_sync_event;
IXAudio2SourceVoice* m_source_voice; IXAudio2SourceVoice* m_source_voice;
std::unique_ptr<BYTE[]> xaudio_buffer; std::unique_ptr<BYTE[]> xaudio_buffer;
void SubmitBuffer(PBYTE buf_data); void SubmitBuffer(PBYTE buf_data);
public: public:
StreamingVoiceContext(IXAudio2 *pXAudio2, CMixer *pMixer, Common::Event& pSyncEvent); StreamingVoiceContext(IXAudio2* pXAudio2, CMixer* pMixer, Common::Event& pSyncEvent);
~StreamingVoiceContext(); ~StreamingVoiceContext();
void StreamingVoiceContext::Stop(); void StreamingVoiceContext::Stop();
void StreamingVoiceContext::Play(); void StreamingVoiceContext::Play();
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) {} STDMETHOD_(void, OnVoiceError)(THIS_ void* pBufferContext, HRESULT Error) {}
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {} STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {} STDMETHOD_(void, OnVoiceProcessingPassEnd)() {}
STDMETHOD_(void, OnBufferStart) (void*) {} STDMETHOD_(void, OnBufferStart)(void*) {}
STDMETHOD_(void, OnLoopEnd) (void*) {} STDMETHOD_(void, OnLoopEnd)(void*) {}
STDMETHOD_(void, OnStreamEnd) () {} STDMETHOD_(void, OnStreamEnd)() {}
STDMETHOD_(void, OnBufferEnd)(void* context);
STDMETHOD_(void, OnBufferEnd) (void* context);
}; };
const int NUM_BUFFERS = 3; const int NUM_BUFFERS = 3;
@ -50,199 +49,199 @@ const int BUFFER_SIZE_BYTES = BUFFER_SIZE * sizeof(s16);
void StreamingVoiceContext::SubmitBuffer(PBYTE buf_data) void StreamingVoiceContext::SubmitBuffer(PBYTE buf_data)
{ {
XAUDIO2_BUFFER buf = {}; XAUDIO2_BUFFER buf = {};
buf.AudioBytes = BUFFER_SIZE_BYTES; buf.AudioBytes = BUFFER_SIZE_BYTES;
buf.pContext = buf_data; buf.pContext = buf_data;
buf.pAudioData = buf_data; buf.pAudioData = buf_data;
m_source_voice->SubmitSourceBuffer(&buf); m_source_voice->SubmitSourceBuffer(&buf);
} }
StreamingVoiceContext::StreamingVoiceContext(IXAudio2 *pXAudio2, CMixer *pMixer, Common::Event& pSyncEvent) StreamingVoiceContext::StreamingVoiceContext(IXAudio2* pXAudio2, CMixer* pMixer,
: m_mixer(pMixer) Common::Event& pSyncEvent)
, m_sound_sync_event(pSyncEvent) : m_mixer(pMixer), m_sound_sync_event(pSyncEvent),
, xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]()) xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]())
{ {
WAVEFORMATEXTENSIBLE wfx = {}; WAVEFORMATEXTENSIBLE wfx = {};
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate(); wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
wfx.Format.nChannels = 2; wfx.Format.nChannels = 2;
wfx.Format.wBitsPerSample = 16; wfx.Format.wBitsPerSample = 16;
wfx.Format.nBlockAlign = wfx.Format.nChannels*wfx.Format.wBitsPerSample / 8; wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
wfx.Samples.wValidBitsPerSample = 16; wfx.Samples.wValidBitsPerSample = 16;
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
// create source voice // create source voice
HRESULT hr; HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC, 1.0f, this))) if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC,
{ 1.0f, this)))
PanicAlert("XAudio2 CreateSourceVoice failed: %#X", hr); {
return; PanicAlert("XAudio2 CreateSourceVoice failed: %#X", hr);
} return;
}
m_source_voice->Start(); m_source_voice->Start();
// start buffers with silence // start buffers with silence
for (int i = 0; i != NUM_BUFFERS; ++i) for (int i = 0; i != NUM_BUFFERS; ++i)
SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES)); SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES));
} }
StreamingVoiceContext::~StreamingVoiceContext() StreamingVoiceContext::~StreamingVoiceContext()
{ {
if (m_source_voice) if (m_source_voice)
{ {
m_source_voice->Stop(); m_source_voice->Stop();
m_source_voice->DestroyVoice(); m_source_voice->DestroyVoice();
} }
} }
void StreamingVoiceContext::Stop() void StreamingVoiceContext::Stop()
{ {
if (m_source_voice) if (m_source_voice)
m_source_voice->Stop(); m_source_voice->Stop();
} }
void StreamingVoiceContext::Play() void StreamingVoiceContext::Play()
{ {
if (m_source_voice) if (m_source_voice)
m_source_voice->Start(); m_source_voice->Start();
} }
void StreamingVoiceContext::OnBufferEnd(void* context) void StreamingVoiceContext::OnBufferEnd(void* context)
{ {
// buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer // buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer
if (!m_source_voice || !context) if (!m_source_voice || !context)
return; return;
//m_sound_sync_event->Wait(); // sync // m_sound_sync_event->Wait(); // sync
//m_sound_sync_event->Spin(); // or tight sync // m_sound_sync_event->Spin(); // or tight sync
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER); m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
SubmitBuffer(static_cast<BYTE*>(context)); SubmitBuffer(static_cast<BYTE*>(context));
} }
HMODULE XAudio2::m_xaudio2_dll = nullptr; HMODULE XAudio2::m_xaudio2_dll = nullptr;
typedef decltype(&XAudio2Create) XAudio2Create_t; typedef decltype(&XAudio2Create) XAudio2Create_t;
void *XAudio2::PXAudio2Create = nullptr; void* XAudio2::PXAudio2Create = nullptr;
bool XAudio2::InitLibrary() bool XAudio2::InitLibrary()
{ {
if (m_xaudio2_dll) if (m_xaudio2_dll)
{ {
return true; return true;
} }
m_xaudio2_dll = ::LoadLibrary(XAUDIO2_DLL); m_xaudio2_dll = ::LoadLibrary(XAUDIO2_DLL);
if (!m_xaudio2_dll) if (!m_xaudio2_dll)
{ {
return false; return false;
} }
if (!PXAudio2Create) if (!PXAudio2Create)
{ {
PXAudio2Create = (XAudio2Create_t)::GetProcAddress(m_xaudio2_dll, "XAudio2Create"); PXAudio2Create = (XAudio2Create_t)::GetProcAddress(m_xaudio2_dll, "XAudio2Create");
if (!PXAudio2Create) if (!PXAudio2Create)
{ {
::FreeLibrary(m_xaudio2_dll); ::FreeLibrary(m_xaudio2_dll);
m_xaudio2_dll = nullptr; m_xaudio2_dll = nullptr;
return false; return false;
} }
} }
return true; return true;
} }
XAudio2::XAudio2() XAudio2::XAudio2()
: m_mastering_voice(nullptr) : m_mastering_voice(nullptr), m_volume(1.0f),
, m_volume(1.0f) m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
, m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
{ {
} }
XAudio2::~XAudio2() XAudio2::~XAudio2()
{ {
Stop(); Stop();
if (m_cleanup_com) if (m_cleanup_com)
CoUninitialize(); CoUninitialize();
} }
bool XAudio2::Start() bool XAudio2::Start()
{ {
HRESULT hr; HRESULT hr;
// callback doesn't seem to run on a specific CPU anyways // callback doesn't seem to run on a specific CPU anyways
IXAudio2* xaudptr; IXAudio2* xaudptr;
if (FAILED(hr = ((XAudio2Create_t)PXAudio2Create)(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR))) if (FAILED(hr = ((XAudio2Create_t)PXAudio2Create)(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
{ {
PanicAlert("XAudio2 init failed: %#X", hr); PanicAlert("XAudio2 init failed: %#X", hr);
Stop(); Stop();
return false; return false;
} }
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr); m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
// XAudio2 master voice // XAudio2 master voice
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion? // XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate()))) if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate())))
{ {
PanicAlert("XAudio2 master voice creation failed: %#X", hr); PanicAlert("XAudio2 master voice creation failed: %#X", hr);
Stop(); Stop();
return false; return false;
} }
// Volume // Volume
m_mastering_voice->SetVolume(m_volume); m_mastering_voice->SetVolume(m_volume);
m_voice_context = std::unique_ptr<StreamingVoiceContext> m_voice_context = std::unique_ptr<StreamingVoiceContext>(
(new StreamingVoiceContext(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event)); new StreamingVoiceContext(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
return true; return true;
} }
void XAudio2::SetVolume(int volume) void XAudio2::SetVolume(int volume)
{ {
//linear 1- .01 // linear 1- .01
m_volume = (float)volume / 100.f; m_volume = (float)volume / 100.f;
if (m_mastering_voice) if (m_mastering_voice)
m_mastering_voice->SetVolume(m_volume); m_mastering_voice->SetVolume(m_volume);
} }
void XAudio2::Clear(bool mute) void XAudio2::Clear(bool mute)
{ {
m_muted = mute; m_muted = mute;
if (m_voice_context) if (m_voice_context)
{ {
if (m_muted) if (m_muted)
m_voice_context->Stop(); m_voice_context->Stop();
else else
m_voice_context->Play(); m_voice_context->Play();
} }
} }
void XAudio2::Stop() void XAudio2::Stop()
{ {
//m_sound_sync_event.Set(); // m_sound_sync_event.Set();
m_voice_context.reset(); m_voice_context.reset();
if (m_mastering_voice) if (m_mastering_voice)
{ {
m_mastering_voice->DestroyVoice(); m_mastering_voice->DestroyVoice();
m_mastering_voice = nullptr; m_mastering_voice = nullptr;
} }
m_xaudio2.reset(); // release interface m_xaudio2.reset(); // release interface
if (m_xaudio2_dll) if (m_xaudio2_dll)
{ {
::FreeLibrary(m_xaudio2_dll); ::FreeLibrary(m_xaudio2_dll);
m_xaudio2_dll = nullptr; m_xaudio2_dll = nullptr;
PXAudio2Create = nullptr; PXAudio2Create = nullptr;
} }
} }

View File

@ -26,40 +26,40 @@ class XAudio2 final : public SoundStream
#ifdef _WIN32 #ifdef _WIN32
private: private:
class Releaser class Releaser
{ {
public: public:
template <typename R> template <typename R>
void operator()(R* ptr) void operator()(R* ptr)
{ {
ptr->Release(); ptr->Release();
} }
}; };
std::unique_ptr<IXAudio2, Releaser> m_xaudio2; std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
std::unique_ptr<StreamingVoiceContext> m_voice_context; std::unique_ptr<StreamingVoiceContext> m_voice_context;
IXAudio2MasteringVoice *m_mastering_voice; IXAudio2MasteringVoice* m_mastering_voice;
Common::Event m_sound_sync_event; Common::Event m_sound_sync_event;
float m_volume; float m_volume;
const bool m_cleanup_com; const bool m_cleanup_com;
static HMODULE m_xaudio2_dll; static HMODULE m_xaudio2_dll;
static void *PXAudio2Create; static void* PXAudio2Create;
static bool InitLibrary(); static bool InitLibrary();
public: public:
XAudio2(); XAudio2();
virtual ~XAudio2(); virtual ~XAudio2();
bool Start() override; bool Start() override;
void Stop() override; void Stop() override;
void Clear(bool mute) override; void Clear(bool mute) override;
void SetVolume(int volume) override; void SetVolume(int volume) override;
static bool isValid() { return InitLibrary(); } static bool isValid() { return InitLibrary(); }
#endif #endif
}; };

View File

@ -10,35 +10,34 @@
#include "AudioCommon/AudioCommon.h" #include "AudioCommon/AudioCommon.h"
#include "AudioCommon/XAudio2_7Stream.h" #include "AudioCommon/XAudio2_7Stream.h"
#include "Common/Event.h" #include "Common/Event.h"
#include "Common/MsgHandler.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
struct StreamingVoiceContext2_7 : public IXAudio2VoiceCallback struct StreamingVoiceContext2_7 : public IXAudio2VoiceCallback
{ {
private: private:
CMixer* const m_mixer; CMixer* const m_mixer;
Common::Event& m_sound_sync_event; Common::Event& m_sound_sync_event;
IXAudio2SourceVoice* m_source_voice; IXAudio2SourceVoice* m_source_voice;
std::unique_ptr<BYTE[]> xaudio_buffer; std::unique_ptr<BYTE[]> xaudio_buffer;
void SubmitBuffer(PBYTE buf_data); void SubmitBuffer(PBYTE buf_data);
public: public:
StreamingVoiceContext2_7(IXAudio2 *pXAudio2, CMixer *pMixer, Common::Event& pSyncEvent); StreamingVoiceContext2_7(IXAudio2* pXAudio2, CMixer* pMixer, Common::Event& pSyncEvent);
~StreamingVoiceContext2_7(); ~StreamingVoiceContext2_7();
void StreamingVoiceContext2_7::Stop(); void StreamingVoiceContext2_7::Stop();
void StreamingVoiceContext2_7::Play(); void StreamingVoiceContext2_7::Play();
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) {} STDMETHOD_(void, OnVoiceError)(THIS_ void* pBufferContext, HRESULT Error) {}
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {} STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {} STDMETHOD_(void, OnVoiceProcessingPassEnd)() {}
STDMETHOD_(void, OnBufferStart) (void*) {} STDMETHOD_(void, OnBufferStart)(void*) {}
STDMETHOD_(void, OnLoopEnd) (void*) {} STDMETHOD_(void, OnLoopEnd)(void*) {}
STDMETHOD_(void, OnStreamEnd) () {} STDMETHOD_(void, OnStreamEnd)() {}
STDMETHOD_(void, OnBufferEnd)(void* context);
STDMETHOD_(void, OnBufferEnd) (void* context);
}; };
const int NUM_BUFFERS = 3; const int NUM_BUFFERS = 3;
@ -50,186 +49,186 @@ const int BUFFER_SIZE_BYTES = BUFFER_SIZE * sizeof(s16);
void StreamingVoiceContext2_7::SubmitBuffer(PBYTE buf_data) void StreamingVoiceContext2_7::SubmitBuffer(PBYTE buf_data)
{ {
XAUDIO2_BUFFER buf = {}; XAUDIO2_BUFFER buf = {};
buf.AudioBytes = BUFFER_SIZE_BYTES; buf.AudioBytes = BUFFER_SIZE_BYTES;
buf.pContext = buf_data; buf.pContext = buf_data;
buf.pAudioData = buf_data; buf.pAudioData = buf_data;
m_source_voice->SubmitSourceBuffer(&buf); m_source_voice->SubmitSourceBuffer(&buf);
} }
StreamingVoiceContext2_7::StreamingVoiceContext2_7(IXAudio2 *pXAudio2, CMixer *pMixer, Common::Event& pSyncEvent) StreamingVoiceContext2_7::StreamingVoiceContext2_7(IXAudio2* pXAudio2, CMixer* pMixer,
: m_mixer(pMixer) Common::Event& pSyncEvent)
, m_sound_sync_event(pSyncEvent) : m_mixer(pMixer), m_sound_sync_event(pSyncEvent),
, xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]()) xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]())
{ {
WAVEFORMATEXTENSIBLE wfx = {}; WAVEFORMATEXTENSIBLE wfx = {};
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate(); wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
wfx.Format.nChannels = 2; wfx.Format.nChannels = 2;
wfx.Format.wBitsPerSample = 16; wfx.Format.wBitsPerSample = 16;
wfx.Format.nBlockAlign = wfx.Format.nChannels*wfx.Format.wBitsPerSample / 8; wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
wfx.Samples.wValidBitsPerSample = 16; wfx.Samples.wValidBitsPerSample = 16;
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
// create source voice // create source voice
HRESULT hr; HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC, 1.0f, this))) if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC,
{ 1.0f, this)))
PanicAlert("XAudio2_7 CreateSourceVoice failed: %#X", hr); {
return; PanicAlert("XAudio2_7 CreateSourceVoice failed: %#X", hr);
} return;
}
m_source_voice->Start(); m_source_voice->Start();
// start buffers with silence // start buffers with silence
for (int i = 0; i != NUM_BUFFERS; ++i) for (int i = 0; i != NUM_BUFFERS; ++i)
SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES)); SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES));
} }
StreamingVoiceContext2_7::~StreamingVoiceContext2_7() StreamingVoiceContext2_7::~StreamingVoiceContext2_7()
{ {
if (m_source_voice) if (m_source_voice)
{ {
m_source_voice->Stop(); m_source_voice->Stop();
m_source_voice->DestroyVoice(); m_source_voice->DestroyVoice();
} }
} }
void StreamingVoiceContext2_7::Stop() void StreamingVoiceContext2_7::Stop()
{ {
if (m_source_voice) if (m_source_voice)
m_source_voice->Stop(); m_source_voice->Stop();
} }
void StreamingVoiceContext2_7::Play() void StreamingVoiceContext2_7::Play()
{ {
if (m_source_voice) if (m_source_voice)
m_source_voice->Start(); m_source_voice->Start();
} }
void StreamingVoiceContext2_7::OnBufferEnd(void* context) void StreamingVoiceContext2_7::OnBufferEnd(void* context)
{ {
// buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer // buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer
if (!m_source_voice || !context) if (!m_source_voice || !context)
return; return;
//m_sound_sync_event->Wait(); // sync // m_sound_sync_event->Wait(); // sync
//m_sound_sync_event->Spin(); // or tight sync // m_sound_sync_event->Spin(); // or tight sync
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER); m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
SubmitBuffer(static_cast<BYTE*>(context)); SubmitBuffer(static_cast<BYTE*>(context));
} }
HMODULE XAudio2_7::m_xaudio2_dll = nullptr; HMODULE XAudio2_7::m_xaudio2_dll = nullptr;
void XAudio2_7::ReleaseIXAudio2(IXAudio2* ptr) void XAudio2_7::ReleaseIXAudio2(IXAudio2* ptr)
{ {
ptr->Release(); ptr->Release();
} }
bool XAudio2_7::InitLibrary() bool XAudio2_7::InitLibrary()
{ {
if (m_xaudio2_dll) if (m_xaudio2_dll)
{ {
return true; return true;
} }
m_xaudio2_dll = ::LoadLibrary(TEXT("xaudio2_7.dll")); m_xaudio2_dll = ::LoadLibrary(TEXT("xaudio2_7.dll"));
return m_xaudio2_dll != nullptr; return m_xaudio2_dll != nullptr;
} }
XAudio2_7::XAudio2_7() XAudio2_7::XAudio2_7()
: m_mastering_voice(nullptr) : m_mastering_voice(nullptr), m_volume(1.0f),
, m_volume(1.0f) m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
, m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
{ {
} }
XAudio2_7::~XAudio2_7() XAudio2_7::~XAudio2_7()
{ {
Stop(); Stop();
if (m_cleanup_com) if (m_cleanup_com)
CoUninitialize(); CoUninitialize();
} }
bool XAudio2_7::Start() bool XAudio2_7::Start()
{ {
HRESULT hr; HRESULT hr;
// callback doesn't seem to run on a specific CPU anyways // callback doesn't seem to run on a specific CPU anyways
IXAudio2* xaudptr; IXAudio2* xaudptr;
if (FAILED(hr = XAudio2Create(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR))) if (FAILED(hr = XAudio2Create(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
{ {
PanicAlert("XAudio2_7 init failed: %#X", hr); PanicAlert("XAudio2_7 init failed: %#X", hr);
Stop(); Stop();
return false; return false;
} }
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr); m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
// XAudio2 master voice // XAudio2 master voice
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion? // XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate()))) if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate())))
{ {
PanicAlert("XAudio2_7 master voice creation failed: %#X", hr); PanicAlert("XAudio2_7 master voice creation failed: %#X", hr);
Stop(); Stop();
return false; return false;
} }
// Volume // Volume
m_mastering_voice->SetVolume(m_volume); m_mastering_voice->SetVolume(m_volume);
m_voice_context = std::unique_ptr<StreamingVoiceContext2_7> m_voice_context = std::unique_ptr<StreamingVoiceContext2_7>(
(new StreamingVoiceContext2_7(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event)); new StreamingVoiceContext2_7(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
return true; return true;
} }
void XAudio2_7::SetVolume(int volume) void XAudio2_7::SetVolume(int volume)
{ {
//linear 1- .01 // linear 1- .01
m_volume = (float)volume / 100.f; m_volume = (float)volume / 100.f;
if (m_mastering_voice) if (m_mastering_voice)
m_mastering_voice->SetVolume(m_volume); m_mastering_voice->SetVolume(m_volume);
} }
void XAudio2_7::Clear(bool mute) void XAudio2_7::Clear(bool mute)
{ {
m_muted = mute; m_muted = mute;
if (m_voice_context) if (m_voice_context)
{ {
if (m_muted) if (m_muted)
m_voice_context->Stop(); m_voice_context->Stop();
else else
m_voice_context->Play(); m_voice_context->Play();
} }
} }
void XAudio2_7::Stop() void XAudio2_7::Stop()
{ {
//m_sound_sync_event.Set(); // m_sound_sync_event.Set();
m_voice_context.reset(); m_voice_context.reset();
if (m_mastering_voice) if (m_mastering_voice)
{ {
m_mastering_voice->DestroyVoice(); m_mastering_voice->DestroyVoice();
m_mastering_voice = nullptr; m_mastering_voice = nullptr;
} }
m_xaudio2.reset(); // release interface m_xaudio2.reset(); // release interface
if (m_xaudio2_dll) if (m_xaudio2_dll)
{ {
::FreeLibrary(m_xaudio2_dll); ::FreeLibrary(m_xaudio2_dll);
m_xaudio2_dll = nullptr; m_xaudio2_dll = nullptr;
} }
} }

View File

@ -32,41 +32,41 @@ class XAudio2_7 final : public SoundStream
#ifdef _WIN32 #ifdef _WIN32
private: private:
static void ReleaseIXAudio2(IXAudio2 *ptr); static void ReleaseIXAudio2(IXAudio2* ptr);
class Releaser class Releaser
{ {
public: public:
template <typename R> template <typename R>
void operator()(R *ptr) void operator()(R* ptr)
{ {
ReleaseIXAudio2(ptr); ReleaseIXAudio2(ptr);
} }
}; };
std::unique_ptr<IXAudio2, Releaser> m_xaudio2; std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
std::unique_ptr<StreamingVoiceContext2_7> m_voice_context; std::unique_ptr<StreamingVoiceContext2_7> m_voice_context;
IXAudio2MasteringVoice *m_mastering_voice; IXAudio2MasteringVoice* m_mastering_voice;
Common::Event m_sound_sync_event; Common::Event m_sound_sync_event;
float m_volume; float m_volume;
const bool m_cleanup_com; const bool m_cleanup_com;
static HMODULE m_xaudio2_dll; static HMODULE m_xaudio2_dll;
static bool InitLibrary(); static bool InitLibrary();
public: public:
XAudio2_7(); XAudio2_7();
virtual ~XAudio2_7(); virtual ~XAudio2_7();
bool Start() override; bool Start() override;
void Stop() override; void Stop() override;
void Clear(bool mute) override; void Clear(bool mute) override;
void SetVolume(int volume) override; void SetVolume(int volume) override;
static bool isValid() { return InitLibrary(); } static bool isValid() { return InitLibrary(); }
#endif #endif
}; };

View File

@ -6,23 +6,35 @@
* Copyright (c) 2006, Creative Labs Inc. * Copyright (c) 2006, Creative Labs Inc.
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided * Redistribution and use in source and binary forms, with or without modification, are permitted
* provided
* that the following conditions are met: * that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this list of conditions and * * Redistributions of source code must retain the above copyright notice, this list of
* conditions and
* the following disclaimer. * the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions * * Redistributions in binary form must reproduce the above copyright notice, this list of
* and the following disclaimer in the documentation and/or other materials provided with the distribution. * conditions
* * Neither the name of Creative Labs Inc. nor the names of its contributors may be used to endorse or * and the following disclaimer in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Creative Labs Inc. nor the names of its contributors may be used to
* endorse or
* promote products derived from this software without specific prior written permission. * promote products derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * IMPLIED
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * FOR A
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * LIABLE FOR
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -42,101 +54,104 @@
#include <AL/alc.h> #include <AL/alc.h>
#endif #endif
/* /*
* Init call * Init call
*/ */
ALDeviceList::ALDeviceList() ALDeviceList::ALDeviceList()
{ {
ALDEVICEINFO ALDeviceInfo; ALDEVICEINFO ALDeviceInfo;
// DeviceInfo vector stores, for each enumerated device, it's device name, selection status, spec version #, and extension support // DeviceInfo vector stores, for each enumerated device, it's device name, selection status, spec
vDeviceInfo.clear(); // version #, and extension support
vDeviceInfo.reserve(10); vDeviceInfo.clear();
vDeviceInfo.reserve(10);
defaultDeviceIndex = 0; defaultDeviceIndex = 0;
// grab function pointers for 1.0-API functions, and if successful proceed to enumerate all devices // grab function pointers for 1.0-API functions, and if successful proceed to enumerate all
//if (LoadOAL10Library(nullptr, &ALFunction) == TRUE) { // devices
if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT")) // if (LoadOAL10Library(nullptr, &ALFunction) == TRUE) {
{ if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT"))
const char *devices = alcGetString(nullptr, ALC_DEVICE_SPECIFIER); {
const char *defaultDeviceName = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER); const char* devices = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
// go through device list (each device terminated with a single nullptr, list terminated with double nullptr) const char* defaultDeviceName = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);
for (s32 index = 0; devices != nullptr && strlen(devices) > 0; index++, devices += strlen(devices) + 1) // go through device list (each device terminated with a single nullptr, list terminated with
{ // double nullptr)
if (strcmp(defaultDeviceName, devices) == 0) for (s32 index = 0; devices != nullptr && strlen(devices) > 0;
{ index++, devices += strlen(devices) + 1)
defaultDeviceIndex = index; {
} if (strcmp(defaultDeviceName, devices) == 0)
ALCdevice *device = alcOpenDevice(devices); {
if (device) defaultDeviceIndex = index;
{ }
ALCcontext *context = alcCreateContext(device, nullptr); ALCdevice* device = alcOpenDevice(devices);
if (context) if (device)
{ {
alcMakeContextCurrent(context); ALCcontext* context = alcCreateContext(device, nullptr);
// if new actual device name isn't already in the list, then add it... if (context)
const char *actualDeviceName = alcGetString(device, ALC_DEVICE_SPECIFIER); {
bool bNewName = true; alcMakeContextCurrent(context);
for (s32 i = 0; i < GetNumDevices(); i++) // if new actual device name isn't already in the list, then add it...
{ const char* actualDeviceName = alcGetString(device, ALC_DEVICE_SPECIFIER);
if (strcmp(GetDeviceName(i), actualDeviceName) == 0) bool bNewName = true;
{ for (s32 i = 0; i < GetNumDevices(); i++)
bNewName = false; {
} if (strcmp(GetDeviceName(i), actualDeviceName) == 0)
} {
if ((bNewName) && (actualDeviceName != nullptr) && (strlen(actualDeviceName) > 0)) bNewName = false;
{ }
ALDeviceInfo.bSelected = true; }
ALDeviceInfo.strDeviceName = actualDeviceName; if ((bNewName) && (actualDeviceName != nullptr) && (strlen(actualDeviceName) > 0))
alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(s32), &ALDeviceInfo.iMajorVersion); {
alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(s32), &ALDeviceInfo.iMinorVersion); ALDeviceInfo.bSelected = true;
ALDeviceInfo.strDeviceName = actualDeviceName;
alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(s32), &ALDeviceInfo.iMajorVersion);
alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(s32), &ALDeviceInfo.iMinorVersion);
ALDeviceInfo.pvstrExtensions = new std::vector<std::string>; ALDeviceInfo.pvstrExtensions = new std::vector<std::string>;
// Check for ALC Extensions // Check for ALC Extensions
if (alcIsExtensionPresent(device, "ALC_EXT_CAPTURE") == AL_TRUE) if (alcIsExtensionPresent(device, "ALC_EXT_CAPTURE") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_CAPTURE"); ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_CAPTURE");
if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_TRUE) if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_EFX"); ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_EFX");
// Check for AL Extensions // Check for AL Extensions
if (alIsExtensionPresent("AL_EXT_OFFSET") == AL_TRUE) if (alIsExtensionPresent("AL_EXT_OFFSET") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_OFFSET"); ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_OFFSET");
if (alIsExtensionPresent("AL_EXT_LINEAR_DISTANCE") == AL_TRUE) if (alIsExtensionPresent("AL_EXT_LINEAR_DISTANCE") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_LINEAR_DISTANCE"); ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_LINEAR_DISTANCE");
if (alIsExtensionPresent("AL_EXT_EXPONENT_DISTANCE") == AL_TRUE) if (alIsExtensionPresent("AL_EXT_EXPONENT_DISTANCE") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_EXPONENT_DISTANCE"); ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_EXPONENT_DISTANCE");
if (alIsExtensionPresent("EAX2.0") == AL_TRUE) if (alIsExtensionPresent("EAX2.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX2.0"); ALDeviceInfo.pvstrExtensions->push_back("EAX2.0");
if (alIsExtensionPresent("EAX3.0") == AL_TRUE) if (alIsExtensionPresent("EAX3.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX3.0"); ALDeviceInfo.pvstrExtensions->push_back("EAX3.0");
if (alIsExtensionPresent("EAX4.0") == AL_TRUE) if (alIsExtensionPresent("EAX4.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX4.0"); ALDeviceInfo.pvstrExtensions->push_back("EAX4.0");
if (alIsExtensionPresent("EAX5.0") == AL_TRUE) if (alIsExtensionPresent("EAX5.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX5.0"); ALDeviceInfo.pvstrExtensions->push_back("EAX5.0");
if (alIsExtensionPresent("EAX-RAM") == AL_TRUE) if (alIsExtensionPresent("EAX-RAM") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX-RAM"); ALDeviceInfo.pvstrExtensions->push_back("EAX-RAM");
// Get Source Count // Get Source Count
ALDeviceInfo.uiSourceCount = GetMaxNumSources(); ALDeviceInfo.uiSourceCount = GetMaxNumSources();
vDeviceInfo.push_back(ALDeviceInfo); vDeviceInfo.push_back(ALDeviceInfo);
} }
alcMakeContextCurrent(nullptr); alcMakeContextCurrent(nullptr);
alcDestroyContext(context); alcDestroyContext(context);
} }
alcCloseDevice(device); alcCloseDevice(device);
} }
} }
} }
//} //}
ResetFilters(); ResetFilters();
} }
/* /*
@ -144,16 +159,16 @@ ALDeviceList::ALDeviceList()
*/ */
ALDeviceList::~ALDeviceList() ALDeviceList::~ALDeviceList()
{ {
for (auto& di : vDeviceInfo) for (auto& di : vDeviceInfo)
{ {
if (di.pvstrExtensions) if (di.pvstrExtensions)
{ {
di.pvstrExtensions->clear(); di.pvstrExtensions->clear();
delete di.pvstrExtensions; delete di.pvstrExtensions;
} }
} }
vDeviceInfo.clear(); vDeviceInfo.clear();
} }
/* /*
@ -161,32 +176,33 @@ ALDeviceList::~ALDeviceList()
*/ */
s32 ALDeviceList::GetNumDevices() s32 ALDeviceList::GetNumDevices()
{ {
return (s32)vDeviceInfo.size(); return (s32)vDeviceInfo.size();
} }
/* /*
* Returns the device name at an index in the complete device list * Returns the device name at an index in the complete device list
*/ */
char * ALDeviceList::GetDeviceName(s32 index) char* ALDeviceList::GetDeviceName(s32 index)
{ {
if (index < GetNumDevices()) if (index < GetNumDevices())
return (char *)vDeviceInfo[index].strDeviceName.c_str(); return (char*)vDeviceInfo[index].strDeviceName.c_str();
else else
return nullptr; return nullptr;
} }
/* /*
* Returns the major and minor version numbers for a device at a specified index in the complete list * Returns the major and minor version numbers for a device at a specified index in the complete
* list
*/ */
void ALDeviceList::GetDeviceVersion(s32 index, s32 *major, s32 *minor) void ALDeviceList::GetDeviceVersion(s32 index, s32* major, s32* minor)
{ {
if (index < GetNumDevices()) if (index < GetNumDevices())
{ {
if (major) if (major)
*major = vDeviceInfo[index].iMajorVersion; *major = vDeviceInfo[index].iMajorVersion;
if (minor) if (minor)
*minor = vDeviceInfo[index].iMinorVersion; *minor = vDeviceInfo[index].iMinorVersion;
} }
} }
/* /*
@ -194,32 +210,32 @@ void ALDeviceList::GetDeviceVersion(s32 index, s32 *major, s32 *minor)
*/ */
u32 ALDeviceList::GetMaxNumSources(s32 index) u32 ALDeviceList::GetMaxNumSources(s32 index)
{ {
if (index < GetNumDevices()) if (index < GetNumDevices())
return vDeviceInfo[index].uiSourceCount; return vDeviceInfo[index].uiSourceCount;
else else
return 0; return 0;
} }
/* /*
* Checks if the extension is supported on the given device * Checks if the extension is supported on the given device
*/ */
bool ALDeviceList::IsExtensionSupported(s32 index, char *szExtName) bool ALDeviceList::IsExtensionSupported(s32 index, char* szExtName)
{ {
bool bReturn = false; bool bReturn = false;
if (index < GetNumDevices()) if (index < GetNumDevices())
{ {
for (auto& ext : *vDeviceInfo[index].pvstrExtensions) for (auto& ext : *vDeviceInfo[index].pvstrExtensions)
{ {
if (!strcasecmp(ext.c_str(), szExtName)) if (!strcasecmp(ext.c_str(), szExtName))
{ {
bReturn = true; bReturn = true;
break; break;
} }
} }
} }
return bReturn; return bReturn;
} }
/* /*
@ -227,7 +243,7 @@ bool ALDeviceList::IsExtensionSupported(s32 index, char *szExtName)
*/ */
s32 ALDeviceList::GetDefaultDevice() s32 ALDeviceList::GetDefaultDevice()
{ {
return defaultDeviceIndex; return defaultDeviceIndex;
} }
/* /*
@ -235,15 +251,15 @@ s32 ALDeviceList::GetDefaultDevice()
*/ */
void ALDeviceList::FilterDevicesMinVer(s32 major, s32 minor) void ALDeviceList::FilterDevicesMinVer(s32 major, s32 minor)
{ {
s32 dMajor = 0, dMinor = 0; s32 dMajor = 0, dMinor = 0;
for (u32 i = 0; i < vDeviceInfo.size(); i++) for (u32 i = 0; i < vDeviceInfo.size(); i++)
{ {
GetDeviceVersion(i, &dMajor, &dMinor); GetDeviceVersion(i, &dMajor, &dMinor);
if ((dMajor < major) || ((dMajor == major) && (dMinor < minor))) if ((dMajor < major) || ((dMajor == major) && (dMinor < minor)))
{ {
vDeviceInfo[i].bSelected = false; vDeviceInfo[i].bSelected = false;
} }
} }
} }
/* /*
@ -251,39 +267,39 @@ void ALDeviceList::FilterDevicesMinVer(s32 major, s32 minor)
*/ */
void ALDeviceList::FilterDevicesMaxVer(s32 major, s32 minor) void ALDeviceList::FilterDevicesMaxVer(s32 major, s32 minor)
{ {
s32 dMajor = 0, dMinor = 0; s32 dMajor = 0, dMinor = 0;
for (u32 i = 0; i < vDeviceInfo.size(); i++) for (u32 i = 0; i < vDeviceInfo.size(); i++)
{ {
GetDeviceVersion(i, &dMajor, &dMinor); GetDeviceVersion(i, &dMajor, &dMinor);
if ((dMajor > major) || ((dMajor == major) && (dMinor > minor))) if ((dMajor > major) || ((dMajor == major) && (dMinor > minor)))
{ {
vDeviceInfo[i].bSelected = false; vDeviceInfo[i].bSelected = false;
} }
} }
} }
/* /*
* Deselects device which don't support the given extension name * Deselects device which don't support the given extension name
*/ */
void ALDeviceList::FilterDevicesExtension(char *szExtName) void ALDeviceList::FilterDevicesExtension(char* szExtName)
{ {
bool bFound; bool bFound;
for (auto& di : vDeviceInfo) for (auto& di : vDeviceInfo)
{ {
bFound = false; bFound = false;
for (auto& ext : *di.pvstrExtensions) for (auto& ext : *di.pvstrExtensions)
{ {
if (!strcasecmp(ext.c_str(), szExtName)) if (!strcasecmp(ext.c_str(), szExtName))
{ {
bFound = true; bFound = true;
break; break;
} }
} }
if (!bFound) if (!bFound)
di.bSelected = false; di.bSelected = false;
} }
} }
/* /*
@ -291,12 +307,12 @@ void ALDeviceList::FilterDevicesExtension(char *szExtName)
*/ */
void ALDeviceList::ResetFilters() void ALDeviceList::ResetFilters()
{ {
for (s32 i = 0; i < GetNumDevices(); i++) for (s32 i = 0; i < GetNumDevices(); i++)
{ {
vDeviceInfo[i].bSelected = true; vDeviceInfo[i].bSelected = true;
} }
filterIndex = 0; filterIndex = 0;
} }
/* /*
@ -304,18 +320,18 @@ void ALDeviceList::ResetFilters()
*/ */
s32 ALDeviceList::GetFirstFilteredDevice() s32 ALDeviceList::GetFirstFilteredDevice()
{ {
s32 i; s32 i;
for (i = 0; i < GetNumDevices(); i++) for (i = 0; i < GetNumDevices(); i++)
{ {
if (vDeviceInfo[i].bSelected == true) if (vDeviceInfo[i].bSelected == true)
{ {
break; break;
} }
} }
filterIndex = i + 1; filterIndex = i + 1;
return i; return i;
} }
/* /*
@ -323,18 +339,18 @@ s32 ALDeviceList::GetFirstFilteredDevice()
*/ */
s32 ALDeviceList::GetNextFilteredDevice() s32 ALDeviceList::GetNextFilteredDevice()
{ {
s32 i; s32 i;
for (i = filterIndex; i < GetNumDevices(); i++) for (i = filterIndex; i < GetNumDevices(); i++)
{ {
if (vDeviceInfo[i].bSelected == true) if (vDeviceInfo[i].bSelected == true)
{ {
break; break;
} }
} }
filterIndex = i + 1; filterIndex = i + 1;
return i; return i;
} }
/* /*
@ -342,29 +358,29 @@ s32 ALDeviceList::GetNextFilteredDevice()
*/ */
u32 ALDeviceList::GetMaxNumSources() u32 ALDeviceList::GetMaxNumSources()
{ {
ALuint uiSources[256]; ALuint uiSources[256];
u32 iSourceCount = 0; u32 iSourceCount = 0;
// Clear AL Error Code // Clear AL Error Code
alGetError(); alGetError();
// Generate up to 256 Sources, checking for any errors // Generate up to 256 Sources, checking for any errors
for (iSourceCount = 0; iSourceCount < 256; iSourceCount++) for (iSourceCount = 0; iSourceCount < 256; iSourceCount++)
{ {
alGenSources(1, &uiSources[iSourceCount]); alGenSources(1, &uiSources[iSourceCount]);
if (alGetError() != AL_NO_ERROR) if (alGetError() != AL_NO_ERROR)
break; break;
} }
// Release the Sources // Release the Sources
alDeleteSources(iSourceCount, uiSources); alDeleteSources(iSourceCount, uiSources);
if (alGetError() != AL_NO_ERROR) if (alGetError() != AL_NO_ERROR)
{ {
for (auto& uiSource : uiSources) for (auto& uiSource : uiSources)
{ {
alDeleteSources(1, &uiSource); alDeleteSources(1, &uiSource);
} }
} }
return iSourceCount; return iSourceCount;
} }

View File

@ -10,43 +10,43 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#ifdef _WIN32 #ifdef _WIN32
#pragma warning(disable: 4786) //disable warning "identifier was truncated to #pragma warning(disable : 4786) // disable warning "identifier was truncated to
//'255' characters in the browser information" //'255' characters in the browser information"
#endif #endif
typedef struct typedef struct
{ {
std::string strDeviceName; std::string strDeviceName;
s32 iMajorVersion; s32 iMajorVersion;
s32 iMinorVersion; s32 iMinorVersion;
u32 uiSourceCount; u32 uiSourceCount;
std::vector<std::string>* pvstrExtensions; std::vector<std::string>* pvstrExtensions;
bool bSelected; bool bSelected;
} ALDEVICEINFO, *LPALDEVICEINFO; } ALDEVICEINFO, *LPALDEVICEINFO;
class ALDeviceList class ALDeviceList
{ {
private: private:
std::vector<ALDEVICEINFO> vDeviceInfo; std::vector<ALDEVICEINFO> vDeviceInfo;
s32 defaultDeviceIndex; s32 defaultDeviceIndex;
s32 filterIndex; s32 filterIndex;
public: public:
ALDeviceList(); ALDeviceList();
~ALDeviceList(); ~ALDeviceList();
s32 GetNumDevices(); s32 GetNumDevices();
char* GetDeviceName(s32 index); char* GetDeviceName(s32 index);
void GetDeviceVersion(s32 index, s32* major, s32* minor); void GetDeviceVersion(s32 index, s32* major, s32* minor);
u32 GetMaxNumSources(s32 index); u32 GetMaxNumSources(s32 index);
bool IsExtensionSupported(s32 index, char* szExtName); bool IsExtensionSupported(s32 index, char* szExtName);
s32 GetDefaultDevice(); s32 GetDefaultDevice();
void FilterDevicesMinVer(s32 major, s32 minor); void FilterDevicesMinVer(s32 major, s32 minor);
void FilterDevicesMaxVer(s32 major, s32 minor); void FilterDevicesMaxVer(s32 major, s32 minor);
void FilterDevicesExtension(char* szExtName); void FilterDevicesExtension(char* szExtName);
void ResetFilters(); void ResetFilters();
s32 GetFirstFilteredDevice(); s32 GetFirstFilteredDevice();
s32 GetNextFilteredDevice(); s32 GetNextFilteredDevice();
private: private:
u32 GetMaxNumSources(); u32 GetMaxNumSources();
}; };

View File

@ -4,8 +4,8 @@
#include <cmath> #include <cmath>
#include <cstdio> #include <cstdio>
#include <string>
#include <curl/curl.h> #include <curl/curl.h>
#include <string>
#include "Common/Analytics.h" #include "Common/Analytics.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
@ -23,211 +23,201 @@ constexpr u8 WIRE_FORMAT_VERSION = 0;
// format. // format.
enum class TypeId : u8 enum class TypeId : u8
{ {
STRING = 0, STRING = 0,
BOOL = 1, BOOL = 1,
UINT = 2, UINT = 2,
SINT = 3, SINT = 3,
FLOAT = 4, FLOAT = 4,
}; };
void AppendBool(std::string* out, bool v) void AppendBool(std::string* out, bool v)
{ {
out->push_back(v ? '\xFF' : '\x00'); out->push_back(v ? '\xFF' : '\x00');
} }
void AppendVarInt(std::string* out, u64 v) void AppendVarInt(std::string* out, u64 v)
{ {
do do
{ {
u8 current_byte = v & 0x7F; u8 current_byte = v & 0x7F;
v >>= 7; v >>= 7;
current_byte |= (!!v) << 7; current_byte |= (!!v) << 7;
out->push_back(current_byte); out->push_back(current_byte);
} } while (v);
while (v);
} }
void AppendBytes(std::string* out, const u8* bytes, u32 length, void AppendBytes(std::string* out, const u8* bytes, u32 length, bool encode_length = true)
bool encode_length = true)
{ {
if (encode_length) if (encode_length)
{ {
AppendVarInt(out, length); AppendVarInt(out, length);
} }
out->append(reinterpret_cast<const char*>(bytes), length); out->append(reinterpret_cast<const char*>(bytes), length);
} }
void AppendType(std::string* out, TypeId type) void AppendType(std::string* out, TypeId type)
{ {
out->push_back(static_cast<u8>(type)); out->push_back(static_cast<u8>(type));
} }
// Dummy write function for curl. // Dummy write function for curl.
size_t DummyCurlWriteFunction(char* ptr, size_t size, size_t nmemb, void* userdata) size_t DummyCurlWriteFunction(char* ptr, size_t size, size_t nmemb, void* userdata)
{ {
return size * nmemb; return size * nmemb;
} }
} // namespace } // namespace
AnalyticsReportBuilder::AnalyticsReportBuilder() AnalyticsReportBuilder::AnalyticsReportBuilder()
{ {
m_report.push_back(WIRE_FORMAT_VERSION); m_report.push_back(WIRE_FORMAT_VERSION);
} }
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, const std::string& v)
const std::string& v)
{ {
AppendType(report, TypeId::STRING); AppendType(report, TypeId::STRING);
AppendBytes(report, reinterpret_cast<const u8*>(v.data()), static_cast<u32>(v.size())); AppendBytes(report, reinterpret_cast<const u8*>(v.data()), static_cast<u32>(v.size()));
} }
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, const char* v)
const char* v)
{ {
AppendSerializedValue(report, std::string(v)); AppendSerializedValue(report, std::string(v));
} }
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, bool v)
bool v)
{ {
AppendType(report, TypeId::BOOL); AppendType(report, TypeId::BOOL);
AppendBool(report, v); AppendBool(report, v);
} }
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, u64 v)
u64 v)
{ {
AppendType(report, TypeId::UINT); AppendType(report, TypeId::UINT);
AppendVarInt(report, v); AppendVarInt(report, v);
} }
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, s64 v)
s64 v)
{ {
AppendType(report, TypeId::SINT); AppendType(report, TypeId::SINT);
AppendBool(report, v >= 0); AppendBool(report, v >= 0);
AppendVarInt(report, static_cast<u64>(std::abs(v))); AppendVarInt(report, static_cast<u64>(std::abs(v)));
} }
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, u32 v)
u32 v)
{ {
AppendSerializedValue(report, static_cast<u64>(v)); AppendSerializedValue(report, static_cast<u64>(v));
} }
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, s32 v)
s32 v)
{ {
AppendSerializedValue(report, static_cast<s64>(v)); AppendSerializedValue(report, static_cast<s64>(v));
} }
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, float v)
float v)
{ {
AppendType(report, TypeId::FLOAT); AppendType(report, TypeId::FLOAT);
AppendBytes(report, reinterpret_cast<u8*>(&v), sizeof (v), false); AppendBytes(report, reinterpret_cast<u8*>(&v), sizeof(v), false);
} }
AnalyticsReporter::AnalyticsReporter() AnalyticsReporter::AnalyticsReporter()
{ {
m_reporter_thread = std::thread(&AnalyticsReporter::ThreadProc, this); m_reporter_thread = std::thread(&AnalyticsReporter::ThreadProc, this);
} }
AnalyticsReporter::~AnalyticsReporter() AnalyticsReporter::~AnalyticsReporter()
{ {
// Set the exit request flag and wait for the thread to honor it. // Set the exit request flag and wait for the thread to honor it.
m_reporter_stop_request.Set(); m_reporter_stop_request.Set();
m_reporter_event.Set(); m_reporter_event.Set();
m_reporter_thread.join(); m_reporter_thread.join();
} }
void AnalyticsReporter::Send(AnalyticsReportBuilder&& report) void AnalyticsReporter::Send(AnalyticsReportBuilder&& report)
{ {
// Put a bound on the size of the queue to avoid uncontrolled memory growth. // Put a bound on the size of the queue to avoid uncontrolled memory growth.
constexpr u32 QUEUE_SIZE_LIMIT = 25; constexpr u32 QUEUE_SIZE_LIMIT = 25;
if (m_reports_queue.Size() < QUEUE_SIZE_LIMIT) if (m_reports_queue.Size() < QUEUE_SIZE_LIMIT)
{ {
m_reports_queue.Push(report.Consume()); m_reports_queue.Push(report.Consume());
m_reporter_event.Set(); m_reporter_event.Set();
} }
} }
void AnalyticsReporter::ThreadProc() void AnalyticsReporter::ThreadProc()
{ {
while (true) while (true)
{ {
m_reporter_event.Wait(); m_reporter_event.Wait();
if (m_reporter_stop_request.IsSet()) if (m_reporter_stop_request.IsSet())
{ {
return; return;
} }
while (!m_reports_queue.Empty()) while (!m_reports_queue.Empty())
{ {
std::shared_ptr<AnalyticsReportingBackend> backend(m_backend); std::shared_ptr<AnalyticsReportingBackend> backend(m_backend);
if (backend) if (backend)
{ {
std::string report; std::string report;
m_reports_queue.Pop(report); m_reports_queue.Pop(report);
backend->Send(std::move(report)); backend->Send(std::move(report));
} }
else else
{ {
break; break;
} }
// Recheck after each report sent. // Recheck after each report sent.
if (m_reporter_stop_request.IsSet()) if (m_reporter_stop_request.IsSet())
{ {
return; return;
} }
} }
} }
} }
void StdoutAnalyticsBackend::Send(std::string report) void StdoutAnalyticsBackend::Send(std::string report)
{ {
printf("Analytics report sent:\n%s", HexDump( printf("Analytics report sent:\n%s",
reinterpret_cast<const u8*>(report.data()), report.size()).c_str()); HexDump(reinterpret_cast<const u8*>(report.data()), report.size()).c_str());
} }
HttpAnalyticsBackend::HttpAnalyticsBackend(const std::string& endpoint) HttpAnalyticsBackend::HttpAnalyticsBackend(const std::string& endpoint)
{ {
CURL* curl = curl_easy_init(); CURL* curl = curl_easy_init();
if (curl) if (curl)
{ {
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str()); curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
curl_easy_setopt(curl, CURLOPT_POST, true); curl_easy_setopt(curl, CURLOPT_POST, true);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &DummyCurlWriteFunction); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &DummyCurlWriteFunction);
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 3000); curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 3000);
#ifdef _WIN32 #ifdef _WIN32
// ALPN support is enabled by default but requires Windows >= 8.1. // ALPN support is enabled by default but requires Windows >= 8.1.
curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, false); curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, false);
#endif #endif
m_curl = curl; m_curl = curl;
} }
} }
HttpAnalyticsBackend::~HttpAnalyticsBackend() HttpAnalyticsBackend::~HttpAnalyticsBackend()
{ {
if (m_curl) if (m_curl)
{ {
curl_easy_cleanup(m_curl); curl_easy_cleanup(m_curl);
} }
} }
void HttpAnalyticsBackend::Send(std::string report) void HttpAnalyticsBackend::Send(std::string report)
{ {
if (!m_curl) if (!m_curl)
return; return;
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, report.c_str()); curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, report.c_str());
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, report.size()); curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, report.size());
curl_easy_perform(m_curl); curl_easy_perform(m_curl);
} }
} // namespace Common } // namespace Common

View File

@ -41,132 +41,123 @@ typedef void CURL;
namespace Common namespace Common
{ {
// Generic interface for an analytics reporting backends. The main // Generic interface for an analytics reporting backends. The main
// implementation used in Dolphin can be found in Core/Analytics.h. // implementation used in Dolphin can be found in Core/Analytics.h.
class AnalyticsReportingBackend class AnalyticsReportingBackend
{ {
public: public:
virtual ~AnalyticsReportingBackend() {} virtual ~AnalyticsReportingBackend() {}
// Called from the AnalyticsReporter backend thread.
// Called from the AnalyticsReporter backend thread. virtual void Send(std::string report) = 0;
virtual void Send(std::string report) = 0;
}; };
// Builder object for an analytics report. // Builder object for an analytics report.
class AnalyticsReportBuilder class AnalyticsReportBuilder
{ {
public: public:
AnalyticsReportBuilder(); AnalyticsReportBuilder();
~AnalyticsReportBuilder() = default; ~AnalyticsReportBuilder() = default;
AnalyticsReportBuilder(const AnalyticsReportBuilder& other) AnalyticsReportBuilder(const AnalyticsReportBuilder& other) { *this = other; }
{ AnalyticsReportBuilder(AnalyticsReportBuilder&& other)
*this = other; {
} std::lock_guard<std::mutex> lk(other.m_lock);
m_report = std::move(other.m_report);
}
AnalyticsReportBuilder(AnalyticsReportBuilder&& other) const AnalyticsReportBuilder& operator=(const AnalyticsReportBuilder& other)
{ {
std::lock_guard<std::mutex> lk(other.m_lock); if (this != &other)
m_report = std::move(other.m_report); {
} std::lock_guard<std::mutex> lk(m_lock);
std::lock_guard<std::mutex> lk2(other.m_lock);
m_report = other.m_report;
}
return *this;
}
const AnalyticsReportBuilder& operator=(const AnalyticsReportBuilder& other) // Append another builder to this one.
{ AnalyticsReportBuilder& AddBuilder(const AnalyticsReportBuilder& other)
if (this != &other) {
{ // Get before locking the object to avoid deadlocks with this += this.
std::lock_guard<std::mutex> lk(m_lock); std::string other_report = other.Get();
std::lock_guard<std::mutex> lk2(other.m_lock); std::lock_guard<std::mutex> lk(m_lock);
m_report = other.m_report; m_report += other_report;
} return *this;
return *this; }
}
// Append another builder to this one. template <typename T>
AnalyticsReportBuilder& AddBuilder(const AnalyticsReportBuilder& other) AnalyticsReportBuilder& AddData(const std::string& key, const T& value)
{ {
// Get before locking the object to avoid deadlocks with this += this. std::lock_guard<std::mutex> lk(m_lock);
std::string other_report = other.Get(); AppendSerializedValue(&m_report, key);
std::lock_guard<std::mutex> lk(m_lock); AppendSerializedValue(&m_report, value);
m_report += other_report; return *this;
return *this; }
}
template <typename T> std::string Get() const
AnalyticsReportBuilder& AddData(const std::string& key, const T& value) {
{ std::lock_guard<std::mutex> lk(m_lock);
std::lock_guard<std::mutex> lk(m_lock); return m_report;
AppendSerializedValue(&m_report, key); }
AppendSerializedValue(&m_report, value);
return *this;
}
std::string Get() const // More efficient version of Get().
{ std::string Consume()
std::lock_guard<std::mutex> lk(m_lock); {
return m_report; std::lock_guard<std::mutex> lk(m_lock);
} return std::move(m_report);
}
// More efficient version of Get().
std::string Consume()
{
std::lock_guard<std::mutex> lk(m_lock);
return std::move(m_report);
}
protected: protected:
static void AppendSerializedValue(std::string* report, const std::string& v); static void AppendSerializedValue(std::string* report, const std::string& v);
static void AppendSerializedValue(std::string* report, const char* v); static void AppendSerializedValue(std::string* report, const char* v);
static void AppendSerializedValue(std::string* report, bool v); static void AppendSerializedValue(std::string* report, bool v);
static void AppendSerializedValue(std::string* report, u64 v); static void AppendSerializedValue(std::string* report, u64 v);
static void AppendSerializedValue(std::string* report, s64 v); static void AppendSerializedValue(std::string* report, s64 v);
static void AppendSerializedValue(std::string* report, u32 v); static void AppendSerializedValue(std::string* report, u32 v);
static void AppendSerializedValue(std::string* report, s32 v); static void AppendSerializedValue(std::string* report, s32 v);
static void AppendSerializedValue(std::string* report, float v); static void AppendSerializedValue(std::string* report, float v);
// Should really be a std::shared_mutex, unfortunately that's C++17 only. // Should really be a std::shared_mutex, unfortunately that's C++17 only.
mutable std::mutex m_lock; mutable std::mutex m_lock;
std::string m_report; std::string m_report;
}; };
class AnalyticsReporter class AnalyticsReporter
{ {
public: public:
AnalyticsReporter(); AnalyticsReporter();
~AnalyticsReporter(); ~AnalyticsReporter();
// Sets a reporting backend and enables sending reports. Do not set a remote // Sets a reporting backend and enables sending reports. Do not set a remote
// backend without user consent. // backend without user consent.
void SetBackend(std::unique_ptr<AnalyticsReportingBackend> backend) void SetBackend(std::unique_ptr<AnalyticsReportingBackend> backend)
{ {
m_backend = std::move(backend); m_backend = std::move(backend);
m_reporter_event.Set(); // In case reports are waiting queued. m_reporter_event.Set(); // In case reports are waiting queued.
} }
// Gets the base report builder which is closed for each subsequent report // Gets the base report builder which is closed for each subsequent report
// being sent. DO NOT use this builder to send a report. Only use it to add // being sent. DO NOT use this builder to send a report. Only use it to add
// new fields that should be globally available. // new fields that should be globally available.
AnalyticsReportBuilder& BaseBuilder() { return m_base_builder; } AnalyticsReportBuilder& BaseBuilder() { return m_base_builder; }
// Gets a cloned builder that can be used to send a report.
// Gets a cloned builder that can be used to send a report. AnalyticsReportBuilder Builder() const { return m_base_builder; }
AnalyticsReportBuilder Builder() const { return m_base_builder; } // Enqueues a report for sending. Consumes the report builder.
void Send(AnalyticsReportBuilder&& report);
// Enqueues a report for sending. Consumes the report builder.
void Send(AnalyticsReportBuilder&& report);
// For convenience.
void Send(AnalyticsReportBuilder& report) { Send(std::move(report)); }
// For convenience.
void Send(AnalyticsReportBuilder& report) { Send(std::move(report)); }
protected: protected:
void ThreadProc(); void ThreadProc();
std::shared_ptr<AnalyticsReportingBackend> m_backend; std::shared_ptr<AnalyticsReportingBackend> m_backend;
AnalyticsReportBuilder m_base_builder; AnalyticsReportBuilder m_base_builder;
std::thread m_reporter_thread; std::thread m_reporter_thread;
Common::Event m_reporter_event; Common::Event m_reporter_event;
Common::Flag m_reporter_stop_request; Common::Flag m_reporter_stop_request;
FifoQueue<std::string> m_reports_queue; FifoQueue<std::string> m_reports_queue;
}; };
// Analytics backend to be used for debugging purpose, which dumps reports to // Analytics backend to be used for debugging purpose, which dumps reports to
@ -174,7 +165,7 @@ protected:
class StdoutAnalyticsBackend : public AnalyticsReportingBackend class StdoutAnalyticsBackend : public AnalyticsReportingBackend
{ {
public: public:
void Send(std::string report) override; void Send(std::string report) override;
}; };
// Analytics backend that POSTs data to a remote HTTP(s) endpoint. WARNING: // Analytics backend that POSTs data to a remote HTTP(s) endpoint. WARNING:
@ -182,13 +173,13 @@ public:
class HttpAnalyticsBackend : public AnalyticsReportingBackend class HttpAnalyticsBackend : public AnalyticsReportingBackend
{ {
public: public:
HttpAnalyticsBackend(const std::string& endpoint); HttpAnalyticsBackend(const std::string& endpoint);
~HttpAnalyticsBackend() override; ~HttpAnalyticsBackend() override;
void Send(std::string report) override; void Send(std::string report) override;
protected: protected:
CURL* m_curl = nullptr; CURL* m_curl = nullptr;
}; };
} // namespace Common } // namespace Common

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,87 +2,92 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <asm/hwcap.h>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <unistd.h>
#include <asm/hwcap.h>
#include <sys/auxv.h> #include <sys/auxv.h>
#include <unistd.h>
#include "Common/CommonTypes.h"
#include "Common/CPUDetect.h" #include "Common/CPUDetect.h"
#include "Common/CommonTypes.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
const char procfile[] = "/proc/cpuinfo"; const char procfile[] = "/proc/cpuinfo";
static std::string GetCPUString() static std::string GetCPUString()
{ {
const std::string marker = "Hardware\t: "; const std::string marker = "Hardware\t: ";
std::string cpu_string = "Unknown"; std::string cpu_string = "Unknown";
std::string line; std::string line;
std::ifstream file(procfile); std::ifstream file(procfile);
if (!file) if (!file)
return cpu_string; return cpu_string;
while (std::getline(file, line)) while (std::getline(file, line))
{ {
if (line.find(marker) != std::string::npos) if (line.find(marker) != std::string::npos)
{ {
cpu_string = line.substr(marker.length()); cpu_string = line.substr(marker.length());
break; break;
} }
} }
return cpu_string; return cpu_string;
} }
CPUInfo cpu_info; CPUInfo cpu_info;
CPUInfo::CPUInfo() CPUInfo::CPUInfo()
{ {
Detect(); Detect();
} }
// Detects the various CPU features // Detects the various CPU features
void CPUInfo::Detect() void CPUInfo::Detect()
{ {
// Set some defaults here // Set some defaults here
// When ARMv8 CPUs come out, these need to be updated. // When ARMv8 CPUs come out, these need to be updated.
HTT = false; HTT = false;
OS64bit = true; OS64bit = true;
CPU64bit = true; CPU64bit = true;
Mode64bit = true; Mode64bit = true;
vendor = VENDOR_ARM; vendor = VENDOR_ARM;
// Get the information about the CPU // Get the information about the CPU
num_cores = sysconf(_SC_NPROCESSORS_CONF); num_cores = sysconf(_SC_NPROCESSORS_CONF);
strncpy(cpu_string, GetCPUString().c_str(), sizeof(cpu_string)); strncpy(cpu_string, GetCPUString().c_str(), sizeof(cpu_string));
unsigned long hwcaps = getauxval(AT_HWCAP); unsigned long hwcaps = getauxval(AT_HWCAP);
bFP = hwcaps & HWCAP_FP; bFP = hwcaps & HWCAP_FP;
bASIMD = hwcaps & HWCAP_ASIMD; bASIMD = hwcaps & HWCAP_ASIMD;
bAES = hwcaps & HWCAP_AES; bAES = hwcaps & HWCAP_AES;
bCRC32 = hwcaps & HWCAP_CRC32; bCRC32 = hwcaps & HWCAP_CRC32;
bSHA1 = hwcaps & HWCAP_SHA1; bSHA1 = hwcaps & HWCAP_SHA1;
bSHA2 = hwcaps & HWCAP_SHA2; bSHA2 = hwcaps & HWCAP_SHA2;
} }
// Turn the CPU info into a string we can show // Turn the CPU info into a string we can show
std::string CPUInfo::Summarize() std::string CPUInfo::Summarize()
{ {
std::string sum; std::string sum;
if (num_cores == 1) if (num_cores == 1)
sum = StringFromFormat("%s, %i core", cpu_string, num_cores); sum = StringFromFormat("%s, %i core", cpu_string, num_cores);
else else
sum = StringFromFormat("%s, %i cores", cpu_string, num_cores); sum = StringFromFormat("%s, %i cores", cpu_string, num_cores);
if (bAES) sum += ", AES"; if (bAES)
if (bCRC32) sum += ", CRC32"; sum += ", AES";
if (bSHA1) sum += ", SHA1"; if (bCRC32)
if (bSHA2) sum += ", SHA2"; sum += ", CRC32";
if (CPU64bit) sum += ", 64-bit"; if (bSHA1)
sum += ", SHA1";
if (bSHA2)
sum += ", SHA2";
if (CPU64bit)
sum += ", 64-bit";
return sum; return sum;
} }

View File

@ -6,23 +6,22 @@
enum CCFlags enum CCFlags
{ {
CC_EQ = 0, // Equal CC_EQ = 0, // Equal
CC_NEQ, // Not equal CC_NEQ, // Not equal
CC_CS, // Carry Set CC_CS, // Carry Set
CC_CC, // Carry Clear CC_CC, // Carry Clear
CC_MI, // Minus (Negative) CC_MI, // Minus (Negative)
CC_PL, // Plus CC_PL, // Plus
CC_VS, // Overflow CC_VS, // Overflow
CC_VC, // No Overflow CC_VC, // No Overflow
CC_HI, // Unsigned higher CC_HI, // Unsigned higher
CC_LS, // Unsigned lower or same CC_LS, // Unsigned lower or same
CC_GE, // Signed greater than or equal CC_GE, // Signed greater than or equal
CC_LT, // Signed less than CC_LT, // Signed less than
CC_GT, // Signed greater than CC_GT, // Signed greater than
CC_LE, // Signed less than or equal CC_LE, // Signed less than or equal
CC_AL, // Always (unconditional) 14 CC_AL, // Always (unconditional) 14
CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same
CC_LO = CC_CC, // Alias of CC_CC Unsigned lower CC_LO = CC_CC, // Alias of CC_CC Unsigned lower
}; };
const u32 NO_COND = 0xE0000000; const u32 NO_COND = 0xE0000000;

View File

@ -6,42 +6,46 @@
#include "Common/Common.h" #include "Common/Common.h"
#include "Common/CommonFuncs.h" #include "Common/CommonFuncs.h"
#include "Common/MsgHandler.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#ifdef _WIN32 #ifdef _WIN32
#define _assert_msg_(_t_, _a_, _fmt_, ...) \ #define _assert_msg_(_t_, _a_, _fmt_, ...) \
if (!(_a_)) {\ if (!(_a_)) \
if (!PanicYesNo(_fmt_, __VA_ARGS__)) \ { \
Crash(); \ if (!PanicYesNo(_fmt_, __VA_ARGS__)) \
} Crash(); \
}
#define _dbg_assert_msg_(_t_, _a_, _msg_, ...)\ #define _dbg_assert_msg_(_t_, _a_, _msg_, ...) \
if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG && !(_a_)) {\ if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG && !(_a_)) \
ERROR_LOG(_t_, _msg_, __VA_ARGS__); \ { \
if (!PanicYesNo(_msg_, __VA_ARGS__)) \ ERROR_LOG(_t_, _msg_, __VA_ARGS__); \
Crash(); \ if (!PanicYesNo(_msg_, __VA_ARGS__)) \
} Crash(); \
}
#else #else
#define _assert_msg_(_t_, _a_, _fmt_, ...) \ #define _assert_msg_(_t_, _a_, _fmt_, ...) \
if (!(_a_)) {\ if (!(_a_)) \
if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) \ { \
Crash(); \ if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) \
} Crash(); \
}
#define _dbg_assert_msg_(_t_, _a_, _msg_, ...)\ #define _dbg_assert_msg_(_t_, _a_, _msg_, ...) \
if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG && !(_a_)) {\ if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG && !(_a_)) \
ERROR_LOG(_t_, _msg_, ##__VA_ARGS__); \ { \
if (!PanicYesNo(_msg_, ##__VA_ARGS__)) \ ERROR_LOG(_t_, _msg_, ##__VA_ARGS__); \
Crash(); \ if (!PanicYesNo(_msg_, ##__VA_ARGS__)) \
} Crash(); \
}
#endif #endif
#define _assert_(_a_) \ #define _assert_(_a_) \
_assert_msg_(MASTER_LOG, _a_, \ _assert_msg_(MASTER_LOG, _a_, \
_trans("An error occurred.\n\n Line: %d\n File: %s\n\nIgnore and continue?"), \ _trans("An error occurred.\n\n Line: %d\n File: %s\n\nIgnore and continue?"), \
__LINE__, __FILE__) __LINE__, __FILE__)
#define _dbg_assert_(_t_, _a_) \ #define _dbg_assert_(_t_, _a_) \
if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG) \ if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG) \
_assert_(_a_) _assert_(_a_)

View File

@ -6,11 +6,11 @@
#ifdef _WIN32 #ifdef _WIN32
#include "Common/Atomic_Win32.h" // IWYU pragma: export #include "Common/Atomic_Win32.h" // IWYU pragma: export
#else #else
// GCC-compatible compiler assumed! // GCC-compatible compiler assumed!
#include "Common/Atomic_GCC.h" // IWYU pragma: export #include "Common/Atomic_GCC.h" // IWYU pragma: export
#endif #endif

View File

@ -25,30 +25,29 @@
namespace Common namespace Common
{ {
inline void AtomicAdd(volatile u32& target, u32 value) inline void AtomicAdd(volatile u32& target, u32 value)
{ {
__sync_add_and_fetch(&target, value); __sync_add_and_fetch(&target, value);
} }
inline void AtomicAnd(volatile u32& target, u32 value) inline void AtomicAnd(volatile u32& target, u32 value)
{ {
__sync_and_and_fetch(&target, value); __sync_and_and_fetch(&target, value);
} }
inline void AtomicDecrement(volatile u32& target) inline void AtomicDecrement(volatile u32& target)
{ {
__sync_add_and_fetch(&target, -1); __sync_add_and_fetch(&target, -1);
} }
inline void AtomicIncrement(volatile u32& target) inline void AtomicIncrement(volatile u32& target)
{ {
__sync_add_and_fetch(&target, 1); __sync_add_and_fetch(&target, 1);
} }
inline void AtomicOr(volatile u32& target, u32 value) inline void AtomicOr(volatile u32& target, u32 value)
{ {
__sync_or_and_fetch(&target, value); __sync_or_and_fetch(&target, value);
} }
#ifndef __ATOMIC_RELAXED #ifndef __ATOMIC_RELAXED
@ -58,31 +57,30 @@ inline void AtomicOr(volatile u32& target, u32 value)
template <typename T> template <typename T>
inline T AtomicLoad(volatile T& src) inline T AtomicLoad(volatile T& src)
{ {
return __atomic_load_n(&src, __ATOMIC_RELAXED); return __atomic_load_n(&src, __ATOMIC_RELAXED);
} }
template <typename T> template <typename T>
inline T AtomicLoadAcquire(volatile T& src) inline T AtomicLoadAcquire(volatile T& src)
{ {
return __atomic_load_n(&src, __ATOMIC_ACQUIRE); return __atomic_load_n(&src, __ATOMIC_ACQUIRE);
} }
template <typename T, typename U> template <typename T, typename U>
inline void AtomicStore(volatile T& dest, U value) inline void AtomicStore(volatile T& dest, U value)
{ {
__atomic_store_n(&dest, value, __ATOMIC_RELAXED); __atomic_store_n(&dest, value, __ATOMIC_RELAXED);
} }
template <typename T, typename U> template <typename T, typename U>
inline void AtomicStoreRelease(volatile T& dest, U value) inline void AtomicStoreRelease(volatile T& dest, U value)
{ {
__atomic_store_n(&dest, value, __ATOMIC_RELEASE); __atomic_store_n(&dest, value, __ATOMIC_RELEASE);
} }
template <typename T, typename U> template <typename T, typename U>
inline T* AtomicExchangeAcquire(T* volatile& loc, U newval) inline T* AtomicExchangeAcquire(T* volatile& loc, U newval)
{ {
return __atomic_exchange_n(&loc, newval, __ATOMIC_ACQ_REL); return __atomic_exchange_n(&loc, newval, __ATOMIC_ACQ_REL);
} }
} }

View File

@ -31,63 +31,61 @@
namespace Common namespace Common
{ {
inline void AtomicAdd(volatile u32& target, u32 value) inline void AtomicAdd(volatile u32& target, u32 value)
{ {
_InterlockedExchangeAdd((volatile LONG*)&target, (LONG)value); _InterlockedExchangeAdd((volatile LONG*)&target, (LONG)value);
} }
inline void AtomicAnd(volatile u32& target, u32 value) inline void AtomicAnd(volatile u32& target, u32 value)
{ {
_InterlockedAnd((volatile LONG*)&target, (LONG)value); _InterlockedAnd((volatile LONG*)&target, (LONG)value);
} }
inline void AtomicIncrement(volatile u32& target) inline void AtomicIncrement(volatile u32& target)
{ {
_InterlockedIncrement((volatile LONG*)&target); _InterlockedIncrement((volatile LONG*)&target);
} }
inline void AtomicDecrement(volatile u32& target) inline void AtomicDecrement(volatile u32& target)
{ {
_InterlockedDecrement((volatile LONG*)&target); _InterlockedDecrement((volatile LONG*)&target);
} }
inline void AtomicOr(volatile u32& target, u32 value) inline void AtomicOr(volatile u32& target, u32 value)
{ {
_InterlockedOr((volatile LONG*)&target, (LONG)value); _InterlockedOr((volatile LONG*)&target, (LONG)value);
} }
template <typename T> template <typename T>
inline T AtomicLoad(volatile T& src) inline T AtomicLoad(volatile T& src)
{ {
return src; // 32-bit reads are always atomic. return src; // 32-bit reads are always atomic.
} }
template <typename T> template <typename T>
inline T AtomicLoadAcquire(volatile T& src) inline T AtomicLoadAcquire(volatile T& src)
{ {
T result = src; // 32-bit reads are always atomic. T result = src; // 32-bit reads are always atomic.
_ReadBarrier(); // Compiler instruction only. x86 loads always have acquire semantics. _ReadBarrier(); // Compiler instruction only. x86 loads always have acquire semantics.
return result; return result;
} }
template <typename T, typename U> template <typename T, typename U>
inline void AtomicStore(volatile T& dest, U value) inline void AtomicStore(volatile T& dest, U value)
{ {
dest = (T) value; // 32-bit writes are always atomic. dest = (T)value; // 32-bit writes are always atomic.
} }
template <typename T, typename U> template <typename T, typename U>
inline void AtomicStoreRelease(volatile T& dest, U value) inline void AtomicStoreRelease(volatile T& dest, U value)
{ {
_WriteBarrier(); // Compiler instruction only. x86 stores always have release semantics. _WriteBarrier(); // Compiler instruction only. x86 stores always have release semantics.
dest = (T) value; // 32-bit writes are always atomic. dest = (T)value; // 32-bit writes are always atomic.
} }
template <typename T, typename U> template <typename T, typename U>
inline T* AtomicExchangeAcquire(T* volatile& loc, U newval) inline T* AtomicExchangeAcquire(T* volatile& loc, U newval)
{ {
return (T*) _InterlockedExchangePointer_acq((void* volatile*) &loc, (void*) newval); return (T*)_InterlockedExchangePointer_acq((void* volatile*)&loc, (void*)newval);
} }
} }

View File

@ -2,7 +2,6 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
// Copyright 2014 Tony Wasserka // Copyright 2014 Tony Wasserka
// All rights reserved. // All rights reserved.
// //
@ -30,7 +29,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once #pragma once
#include <limits> #include <limits>
@ -111,78 +109,73 @@
* symptoms. * symptoms.
*/ */
#pragma pack(1) #pragma pack(1)
template<std::size_t position, std::size_t bits, typename T> template <std::size_t position, std::size_t bits, typename T>
struct BitField struct BitField
{ {
private: private:
// This constructor might be considered ambiguous: // This constructor might be considered ambiguous:
// Would it initialize the storage or just the bitfield? // Would it initialize the storage or just the bitfield?
// Hence, delete it. Use the assignment operator to set bitfield values! // Hence, delete it. Use the assignment operator to set bitfield values!
BitField(T val) = delete; BitField(T val) = delete;
public: public:
// Force default constructor to be created // Force default constructor to be created
// so that we can use this within unions // so that we can use this within unions
BitField() = default; BitField() = default;
// We explicitly delete the copy assignment operator here, because the // We explicitly delete the copy assignment operator here, because the
// default copy assignment would copy the full storage value, rather than // default copy assignment would copy the full storage value, rather than
// just the bits relevant to this particular bit field. // just the bits relevant to this particular bit field.
// Ideally, we would just implement the copy assignment to copy only the // Ideally, we would just implement the copy assignment to copy only the
// relevant bits, but this requires compiler support for unrestricted // relevant bits, but this requires compiler support for unrestricted
// unions. // unions.
// TODO: Implement this operator properly once all target compilers // TODO: Implement this operator properly once all target compilers
// support unrestricted unions. // support unrestricted unions.
BitField& operator=(const BitField&) = delete; BitField& operator=(const BitField&) = delete;
__forceinline BitField& operator=(T val) __forceinline BitField& operator=(T val)
{ {
storage = (storage & ~GetMask()) | ((val << position) & GetMask()); storage = (storage & ~GetMask()) | ((val << position) & GetMask());
return *this; return *this;
} }
__forceinline T Value() const __forceinline T Value() const
{ {
if (std::numeric_limits<T>::is_signed) if (std::numeric_limits<T>::is_signed)
{ {
std::size_t shift = 8 * sizeof(T) - bits; std::size_t shift = 8 * sizeof(T) - bits;
return (T)((storage << (shift - position)) >> shift); return (T)((storage << (shift - position)) >> shift);
} }
else else
{ {
return (T)((storage & GetMask()) >> position); return (T)((storage & GetMask()) >> position);
} }
} }
__forceinline operator T() const
{
return Value();
}
__forceinline operator T() const { return Value(); }
private: private:
// StorageType is T for non-enum types and the underlying type of T if // StorageType is T for non-enum types and the underlying type of T if
// T is an enumeration. Note that T is wrapped within an enable_if in the // T is an enumeration. Note that T is wrapped within an enable_if in the
// former case to workaround compile errors which arise when using // former case to workaround compile errors which arise when using
// std::underlying_type<T>::type directly. // std::underlying_type<T>::type directly.
typedef typename std::conditional<std::is_enum<T>::value, typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
std::underlying_type<T>, std::enable_if<true, T>>::type::type StorageType;
std::enable_if<true,T>>::type::type StorageType;
// Unsigned version of StorageType // Unsigned version of StorageType
typedef typename std::make_unsigned<StorageType>::type StorageTypeU; typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
__forceinline StorageType GetMask() const __forceinline StorageType GetMask() const
{ {
return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
} }
StorageType storage; StorageType storage;
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
// And, you know, just in case people specify something stupid like bits=position=0x80000000 // And, you know, just in case people specify something stupid like bits=position=0x80000000
static_assert(position < 8 * sizeof(T), "Invalid position"); static_assert(position < 8 * sizeof(T), "Invalid position");
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits"); static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
static_assert(bits > 0, "Invalid number of bits"); static_assert(bits > 0, "Invalid number of bits");
}; };
#pragma pack() #pragma pack()

View File

@ -16,53 +16,76 @@
template <typename T> template <typename T>
static inline int CountSetBits(T v) static inline int CountSetBits(T v)
{ {
// from https://graphics.stanford.edu/~seander/bithacks.html // from https://graphics.stanford.edu/~seander/bithacks.html
// GCC has this built in, but MSVC's intrinsic will only emit the actual // GCC has this built in, but MSVC's intrinsic will only emit the actual
// POPCNT instruction, which we're not depending on // POPCNT instruction, which we're not depending on
v = v - ((v >> 1) & (T)~(T)0/3); v = v - ((v >> 1) & (T) ~(T)0 / 3);
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3);
v = (v + (v >> 4)) & (T)~(T)0/255*15; v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15;
return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8;
} }
static inline int LeastSignificantSetBit(u8 val) static inline int LeastSignificantSetBit(u8 val)
{ {
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u16 val) static inline int LeastSignificantSetBit(u16 val)
{ {
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u32 val) static inline int LeastSignificantSetBit(u32 val)
{ {
unsigned long index; unsigned long index;
_BitScanForward(&index, val); _BitScanForward(&index, val);
return (int)index; return (int)index;
} }
static inline int LeastSignificantSetBit(u64 val) static inline int LeastSignificantSetBit(u64 val)
{ {
unsigned long index; unsigned long index;
_BitScanForward64(&index, val); _BitScanForward64(&index, val);
return (int)index; return (int)index;
} }
#else #else
static inline int CountSetBits(u8 val) { return __builtin_popcount(val); } static inline int CountSetBits(u8 val)
static inline int CountSetBits(u16 val) { return __builtin_popcount(val); } {
static inline int CountSetBits(u32 val) { return __builtin_popcount(val); } return __builtin_popcount(val);
static inline int CountSetBits(u64 val) { return __builtin_popcountll(val); } }
static inline int LeastSignificantSetBit(u8 val) { return __builtin_ctz(val); } static inline int CountSetBits(u16 val)
static inline int LeastSignificantSetBit(u16 val) { return __builtin_ctz(val); } {
static inline int LeastSignificantSetBit(u32 val) { return __builtin_ctz(val); } return __builtin_popcount(val);
static inline int LeastSignificantSetBit(u64 val) { return __builtin_ctzll(val); } }
static inline int CountSetBits(u32 val)
{
return __builtin_popcount(val);
}
static inline int CountSetBits(u64 val)
{
return __builtin_popcountll(val);
}
static inline int LeastSignificantSetBit(u8 val)
{
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u16 val)
{
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u32 val)
{
return __builtin_ctz(val);
}
static inline int LeastSignificantSetBit(u64 val)
{
return __builtin_ctzll(val);
}
#endif #endif
// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly // namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly
namespace BS namespace BS
{ {
// Similar to std::bitset, this is a class which encapsulates a bitset, i.e. // Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
// using the set bits of an integer to represent a set of integers. Like that // using the set bits of an integer to represent a set of integers. Like that
// class, it acts like an array of bools: // class, it acts like an array of bools:
@ -86,101 +109,107 @@ namespace BS
template <typename IntTy> template <typename IntTy>
class BitSet class BitSet
{ {
static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types"); static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
public: public:
// A reference to a particular bit, returned from operator[]. // A reference to a particular bit, returned from operator[].
class Ref class Ref
{ {
public: public:
Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {} Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {} Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
operator bool() const { return (m_bs->m_val & m_mask) != 0; } operator bool() const { return (m_bs->m_val & m_mask) != 0; }
bool operator=(bool set) bool operator=(bool set)
{ {
m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0); m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
return set; return set;
} }
private:
BitSet* m_bs;
IntTy m_mask;
};
// A STL-like iterator is required to be able to use range-based for loops. private:
class Iterator BitSet* m_bs;
{ IntTy m_mask;
public: };
Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {}
Iterator& operator=(Iterator other) { new (this) Iterator(other); return *this; }
int operator*() { return m_bit; }
Iterator& operator++()
{
if (m_val == 0)
{
m_bit = -1;
}
else
{
int bit = LeastSignificantSetBit(m_val);
m_val &= ~(1 << bit);
m_bit = bit;
}
return *this;
}
Iterator operator++(int _)
{
Iterator other(*this);
++*this;
return other;
}
bool operator==(Iterator other) const { return m_bit == other.m_bit; }
bool operator!=(Iterator other) const { return m_bit != other.m_bit; }
private:
IntTy m_val;
int m_bit;
};
BitSet() : m_val(0) {} // A STL-like iterator is required to be able to use range-based for loops.
explicit BitSet(IntTy val) : m_val(val) {} class Iterator
BitSet(std::initializer_list<int> init) {
{ public:
m_val = 0; Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
for (int bit : init) Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {}
m_val |= (IntTy)1 << bit; Iterator& operator=(Iterator other)
} {
new (this) Iterator(other);
return *this;
}
int operator*() { return m_bit; }
Iterator& operator++()
{
if (m_val == 0)
{
m_bit = -1;
}
else
{
int bit = LeastSignificantSetBit(m_val);
m_val &= ~(1 << bit);
m_bit = bit;
}
return *this;
}
Iterator operator++(int _)
{
Iterator other(*this);
++*this;
return other;
}
bool operator==(Iterator other) const { return m_bit == other.m_bit; }
bool operator!=(Iterator other) const { return m_bit != other.m_bit; }
private:
IntTy m_val;
int m_bit;
};
static BitSet AllTrue(size_t count) BitSet() : m_val(0) {}
{ explicit BitSet(IntTy val) : m_val(val) {}
return BitSet(count == sizeof(IntTy)*8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); BitSet(std::initializer_list<int> init)
} {
m_val = 0;
for (int bit : init)
m_val |= (IntTy)1 << bit;
}
Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); } static BitSet AllTrue(size_t count)
const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; } {
bool operator==(BitSet other) const { return m_val == other.m_val; } return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
bool operator!=(BitSet other) const { return m_val != other.m_val; } }
bool operator<(BitSet other) const { return m_val < other.m_val; }
bool operator>(BitSet other) const { return m_val > other.m_val; }
BitSet operator|(BitSet other) const { return BitSet(m_val | other.m_val); }
BitSet operator&(BitSet other) const { return BitSet(m_val & other.m_val); }
BitSet operator^(BitSet other) const { return BitSet(m_val ^ other.m_val); }
BitSet operator~() const { return BitSet(~m_val); }
BitSet& operator|=(BitSet other) { return *this = *this | other; }
BitSet& operator&=(BitSet other) { return *this = *this & other; }
BitSet& operator^=(BitSet other) { return *this = *this ^ other; }
explicit operator bool() const { return m_val != 0; }
// Warning: Even though on modern CPUs this is a single fast instruction, Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); }
// Dolphin's official builds do not currently assume POPCNT support on x86, const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; }
// so slower explicit bit twiddling is generated. Still should generally bool operator==(BitSet other) const { return m_val == other.m_val; }
// be faster than a loop. bool operator!=(BitSet other) const { return m_val != other.m_val; }
unsigned int Count() const { return CountSetBits(m_val); } bool operator<(BitSet other) const { return m_val < other.m_val; }
bool operator>(BitSet other) const { return m_val > other.m_val; }
Iterator begin() const { Iterator it(m_val, 0); return ++it; } BitSet operator|(BitSet other) const { return BitSet(m_val | other.m_val); }
Iterator end() const { return Iterator(m_val, -1); } BitSet operator&(BitSet other) const { return BitSet(m_val & other.m_val); }
BitSet operator^(BitSet other) const { return BitSet(m_val ^ other.m_val); }
IntTy m_val; BitSet operator~() const { return BitSet(~m_val); }
BitSet& operator|=(BitSet other) { return *this = *this | other; }
BitSet& operator&=(BitSet other) { return *this = *this & other; }
BitSet& operator^=(BitSet other) { return *this = *this ^ other; }
explicit operator bool() const { return m_val != 0; }
// Warning: Even though on modern CPUs this is a single fast instruction,
// Dolphin's official builds do not currently assume POPCNT support on x86,
// so slower explicit bit twiddling is generated. Still should generally
// be faster than a loop.
unsigned int Count() const { return CountSetBits(m_val); }
Iterator begin() const
{
Iterator it(m_val, 0);
return ++it;
}
Iterator end() const { return Iterator(m_val, -1); }
IntTy m_val;
}; };
} }
typedef BS::BitSet<u8> BitSet8; typedef BS::BitSet<u8> BitSet8;

View File

@ -13,207 +13,197 @@
namespace Common namespace Common
{ {
// This class provides a synchronized loop. // This class provides a synchronized loop.
// It's a thread-safe way to trigger a new iteration without busy loops. // It's a thread-safe way to trigger a new iteration without busy loops.
// It's optimized for high-usage iterations which usually are already running while it's triggered often. // It's optimized for high-usage iterations which usually are already running while it's triggered
// Be careful when using Wait() and Wakeup() at the same time. Wait() may block forever while Wakeup() is called regularly. // often.
// Be careful when using Wait() and Wakeup() at the same time. Wait() may block forever while
// Wakeup() is called regularly.
class BlockingLoop class BlockingLoop
{ {
public: public:
BlockingLoop() BlockingLoop() { m_stopped.Set(); }
{ ~BlockingLoop() { Stop(); }
m_stopped.Set(); // Triggers to rerun the payload of the Run() function at least once again.
} // This function will never block and is designed to finish as fast as possible.
void Wakeup()
{
// Already running, so no need for a wakeup.
// This is the common case, so try to get this as fast as possible.
if (m_running_state.load() >= STATE_NEED_EXECUTION)
return;
~BlockingLoop() // Mark that new data is available. If the old state will rerun the payload
{ // itself, we don't have to set the event to interrupt the worker.
Stop(); if (m_running_state.exchange(STATE_NEED_EXECUTION) != STATE_SLEEPING)
} return;
// Triggers to rerun the payload of the Run() function at least once again. // Else as the worker thread may sleep now, we have to set the event.
// This function will never block and is designed to finish as fast as possible. m_new_work_event.Set();
void Wakeup() }
{
// Already running, so no need for a wakeup.
// This is the common case, so try to get this as fast as possible.
if (m_running_state.load() >= STATE_NEED_EXECUTION)
return;
// Mark that new data is available. If the old state will rerun the payload // Wait for a complete payload run after the last Wakeup() call.
// itself, we don't have to set the event to interrupt the worker. // If stopped, this returns immediately.
if (m_running_state.exchange(STATE_NEED_EXECUTION) != STATE_SLEEPING) void Wait()
return; {
// already done
if (IsDone())
return;
// Else as the worker thread may sleep now, we have to set the event. // notifying this event will only wake up one thread, so use a mutex here to
m_new_work_event.Set(); // allow only one waiting thread. And in this way, we get an event free wakeup
} // but for the first thread for free
std::lock_guard<std::mutex> lk(m_wait_lock);
// Wait for a complete payload run after the last Wakeup() call. // Wait for the worker thread to finish.
// If stopped, this returns immediately. while (!IsDone())
void Wait() {
{ m_done_event.Wait();
// already done }
if (IsDone())
return;
// notifying this event will only wake up one thread, so use a mutex here to // As we wanted to wait for the other thread, there is likely no work remaining.
// allow only one waiting thread. And in this way, we get an event free wakeup // So there is no need for a busy loop any more.
// but for the first thread for free m_may_sleep.Set();
std::lock_guard<std::mutex> lk(m_wait_lock); }
// Wait for the worker thread to finish. // Half start the worker.
while (!IsDone()) // So this object is in a running state and Wait() will block until the worker calls Run().
{ // This may be called from any thread and is supposed to be called at least once before Wait() is
m_done_event.Wait(); // used.
} void Prepare()
{
// There is a race condition if the other threads call this function while
// the loop thread is initializing. Using this lock will ensure a valid state.
std::lock_guard<std::mutex> lk(m_prepare_lock);
// As we wanted to wait for the other thread, there is likely no work remaining. if (!m_stopped.TestAndClear())
// So there is no need for a busy loop any more. return;
m_may_sleep.Set(); m_running_state.store(
} STATE_LAST_EXECUTION); // so the payload will only be executed once without any Wakeup call
m_shutdown.Clear();
m_may_sleep.Set();
}
// Half start the worker. // Main loop of this object.
// So this object is in a running state and Wait() will block until the worker calls Run(). // The payload callback is called at least as often as it's needed to match the Wakeup()
// This may be called from any thread and is supposed to be called at least once before Wait() is used. // requirements.
void Prepare() // The optional timeout parameter is a timeout for how periodically the payload should be called.
{ // Use timeout = 0 to run without a timeout at all.
// There is a race condition if the other threads call this function while template <class F>
// the loop thread is initializing. Using this lock will ensure a valid state. void Run(F payload, int64_t timeout = 0)
std::lock_guard<std::mutex> lk(m_prepare_lock); {
// Asserts that Prepare is called at least once before we enter the loop.
// But a good implementation should call this before already.
Prepare();
if (!m_stopped.TestAndClear()) while (!m_shutdown.IsSet())
return; {
m_running_state.store(STATE_LAST_EXECUTION); // so the payload will only be executed once without any Wakeup call payload();
m_shutdown.Clear();
m_may_sleep.Set();
}
// Main loop of this object. switch (m_running_state.load())
// The payload callback is called at least as often as it's needed to match the Wakeup() requirements. {
// The optional timeout parameter is a timeout for how periodically the payload should be called. case STATE_NEED_EXECUTION:
// Use timeout = 0 to run without a timeout at all. // We won't get notified while we are in the STATE_NEED_EXECUTION state, so maybe Wakeup was
template<class F> void Run(F payload, int64_t timeout = 0) // called.
{ // So we have to assume on finishing the STATE_NEED_EXECUTION state, that there may be some
// Asserts that Prepare is called at least once before we enter the loop. // remaining tasks.
// But a good implementation should call this before already. // To process this tasks, we call the payload again within the STATE_LAST_EXECUTION state.
Prepare(); m_running_state--;
break;
while (!m_shutdown.IsSet()) case STATE_LAST_EXECUTION:
{ // If we're still in the STATE_LAST_EXECUTION state, then Wakeup wasn't called within the
payload(); // last
// execution of the payload. This means we should be ready now.
// But bad luck, Wakeup may have been called right now. So break and rerun the payload
// if the state was touched.
if (m_running_state-- != STATE_LAST_EXECUTION)
break;
switch (m_running_state.load()) // Else we're likely in the STATE_DONE state now, so wakeup the waiting threads right now.
{ // However, if we're not in the STATE_DONE state any more, the event should also be
case STATE_NEED_EXECUTION: // triggered so that we'll skip the next waiting call quite fast.
// We won't get notified while we are in the STATE_NEED_EXECUTION state, so maybe Wakeup was called. m_done_event.Set();
// So we have to assume on finishing the STATE_NEED_EXECUTION state, that there may be some remaining tasks.
// To process this tasks, we call the payload again within the STATE_LAST_EXECUTION state.
m_running_state--;
break;
case STATE_LAST_EXECUTION: case STATE_DONE:
// If we're still in the STATE_LAST_EXECUTION state, then Wakeup wasn't called within the last // We're done now. So time to check if we want to sleep or if we want to stay in a busy
// execution of the payload. This means we should be ready now. // loop.
// But bad luck, Wakeup may have been called right now. So break and rerun the payload if (m_may_sleep.TestAndClear())
// if the state was touched. {
if (m_running_state-- != STATE_LAST_EXECUTION) // Try to set the sleeping state.
break; if (m_running_state-- != STATE_DONE)
break;
}
else
{
// Busy loop.
break;
}
// Else we're likely in the STATE_DONE state now, so wakeup the waiting threads right now. case STATE_SLEEPING:
// However, if we're not in the STATE_DONE state any more, the event should also be // Just relax
// triggered so that we'll skip the next waiting call quite fast. if (timeout > 0)
m_done_event.Set(); {
m_new_work_event.WaitFor(std::chrono::milliseconds(timeout));
}
else
{
m_new_work_event.Wait();
}
break;
}
}
case STATE_DONE: // Shutdown down, so get a safe state
// We're done now. So time to check if we want to sleep or if we want to stay in a busy loop. m_running_state.store(STATE_DONE);
if (m_may_sleep.TestAndClear()) m_stopped.Set();
{
// Try to set the sleeping state.
if (m_running_state-- != STATE_DONE)
break;
}
else
{
// Busy loop.
break;
}
case STATE_SLEEPING: // Wake up the last Wait calls.
// Just relax m_done_event.Set();
if (timeout > 0) }
{
m_new_work_event.WaitFor(std::chrono::milliseconds(timeout));
}
else
{
m_new_work_event.Wait();
}
break;
}
}
// Shutdown down, so get a safe state // Quits the main loop.
m_running_state.store(STATE_DONE); // By default, it will wait until the main loop quits.
m_stopped.Set(); // Be careful to not use the blocking way within the payload of the Run() method.
void Stop(bool block = true)
{
if (m_stopped.IsSet())
return;
// Wake up the last Wait calls. m_shutdown.Set();
m_done_event.Set();
}
// Quits the main loop. // We have to interrupt the sleeping call to let the worker shut down soon.
// By default, it will wait until the main loop quits. Wakeup();
// Be careful to not use the blocking way within the payload of the Run() method.
void Stop(bool block = true)
{
if (m_stopped.IsSet())
return;
m_shutdown.Set(); if (block)
Wait();
// We have to interrupt the sleeping call to let the worker shut down soon. }
Wakeup();
if (block)
Wait();
}
bool IsRunning() const
{
return !m_stopped.IsSet() && !m_shutdown.IsSet();
}
bool IsDone() const
{
return m_stopped.IsSet() || m_running_state.load() <= STATE_DONE;
}
// This function should be triggered regularly over time so
// that we will fall back from the busy loop to sleeping.
void AllowSleep()
{
m_may_sleep.Set();
}
bool IsRunning() const { return !m_stopped.IsSet() && !m_shutdown.IsSet(); }
bool IsDone() const { return m_stopped.IsSet() || m_running_state.load() <= STATE_DONE; }
// This function should be triggered regularly over time so
// that we will fall back from the busy loop to sleeping.
void AllowSleep() { m_may_sleep.Set(); }
private: private:
std::mutex m_wait_lock; std::mutex m_wait_lock;
std::mutex m_prepare_lock; std::mutex m_prepare_lock;
Flag m_stopped; // If this is set, Wait() shall not block. Flag m_stopped; // If this is set, Wait() shall not block.
Flag m_shutdown; // If this is set, the loop shall end. Flag m_shutdown; // If this is set, the loop shall end.
Event m_new_work_event; Event m_new_work_event;
Event m_done_event; Event m_done_event;
enum RUNNING_TYPE { enum RUNNING_TYPE
STATE_SLEEPING = 0, {
STATE_DONE = 1, STATE_SLEEPING = 0,
STATE_LAST_EXECUTION = 2, STATE_DONE = 1,
STATE_NEED_EXECUTION = 3 STATE_LAST_EXECUTION = 2,
}; STATE_NEED_EXECUTION = 3
std::atomic<int> m_running_state; // must be of type RUNNING_TYPE };
std::atomic<int> m_running_state; // must be of type RUNNING_TYPE
Flag m_may_sleep; // If this is set, we fall back from the busy loop to an event based synchronization. Flag m_may_sleep; // If this is set, we fall back from the busy loop to an event based
// synchronization.
}; };
} }

View File

@ -14,297 +14,295 @@
bool BreakPoints::IsAddressBreakPoint(u32 address) const bool BreakPoints::IsAddressBreakPoint(u32 address) const
{ {
for (const TBreakPoint& bp : m_BreakPoints) for (const TBreakPoint& bp : m_BreakPoints)
if (bp.iAddress == address) if (bp.iAddress == address)
return true; return true;
return false; return false;
} }
bool BreakPoints::IsTempBreakPoint(u32 address) const bool BreakPoints::IsTempBreakPoint(u32 address) const
{ {
for (const TBreakPoint& bp : m_BreakPoints) for (const TBreakPoint& bp : m_BreakPoints)
if (bp.iAddress == address && bp.bTemporary) if (bp.iAddress == address && bp.bTemporary)
return true; return true;
return false; return false;
} }
BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const
{ {
TBreakPointsStr bps; TBreakPointsStr bps;
for (const TBreakPoint& bp : m_BreakPoints) for (const TBreakPoint& bp : m_BreakPoints)
{ {
if (!bp.bTemporary) if (!bp.bTemporary)
{ {
std::stringstream ss; std::stringstream ss;
ss << std::hex << bp.iAddress << " " << (bp.bOn ? "n" : ""); ss << std::hex << bp.iAddress << " " << (bp.bOn ? "n" : "");
bps.push_back(ss.str()); bps.push_back(ss.str());
} }
} }
return bps; return bps;
} }
void BreakPoints::AddFromStrings(const TBreakPointsStr& bpstrs) void BreakPoints::AddFromStrings(const TBreakPointsStr& bpstrs)
{ {
for (const std::string& bpstr : bpstrs) for (const std::string& bpstr : bpstrs)
{ {
TBreakPoint bp; TBreakPoint bp;
std::stringstream ss; std::stringstream ss;
ss << std::hex << bpstr; ss << std::hex << bpstr;
ss >> bp.iAddress; ss >> bp.iAddress;
bp.bOn = bpstr.find("n") != bpstr.npos; bp.bOn = bpstr.find("n") != bpstr.npos;
bp.bTemporary = false; bp.bTemporary = false;
Add(bp); Add(bp);
} }
} }
void BreakPoints::Add(const TBreakPoint& bp) void BreakPoints::Add(const TBreakPoint& bp)
{ {
if (!IsAddressBreakPoint(bp.iAddress)) if (!IsAddressBreakPoint(bp.iAddress))
{ {
m_BreakPoints.push_back(bp); m_BreakPoints.push_back(bp);
if (jit) if (jit)
jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true); jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true);
} }
} }
void BreakPoints::Add(u32 em_address, bool temp) void BreakPoints::Add(u32 em_address, bool temp)
{ {
if (!IsAddressBreakPoint(em_address)) // only add new addresses if (!IsAddressBreakPoint(em_address)) // only add new addresses
{ {
TBreakPoint pt; // breakpoint settings TBreakPoint pt; // breakpoint settings
pt.bOn = true; pt.bOn = true;
pt.bTemporary = temp; pt.bTemporary = temp;
pt.iAddress = em_address; pt.iAddress = em_address;
m_BreakPoints.push_back(pt); m_BreakPoints.push_back(pt);
if (jit) if (jit)
jit->GetBlockCache()->InvalidateICache(em_address, 4, true); jit->GetBlockCache()->InvalidateICache(em_address, 4, true);
} }
} }
void BreakPoints::Remove(u32 em_address) void BreakPoints::Remove(u32 em_address)
{ {
for (auto i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i) for (auto i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i)
{ {
if (i->iAddress == em_address) if (i->iAddress == em_address)
{ {
m_BreakPoints.erase(i); m_BreakPoints.erase(i);
if (jit) if (jit)
jit->GetBlockCache()->InvalidateICache(em_address, 4, true); jit->GetBlockCache()->InvalidateICache(em_address, 4, true);
return; return;
} }
} }
} }
void BreakPoints::Clear() void BreakPoints::Clear()
{ {
if (jit) if (jit)
{ {
for (const TBreakPoint& bp : m_BreakPoints) for (const TBreakPoint& bp : m_BreakPoints)
{ {
jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true); jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true);
} }
} }
m_BreakPoints.clear(); m_BreakPoints.clear();
} }
void BreakPoints::ClearAllTemporary() void BreakPoints::ClearAllTemporary()
{ {
for (const TBreakPoint& bp : m_BreakPoints) for (const TBreakPoint& bp : m_BreakPoints)
{ {
if (bp.bTemporary) if (bp.bTemporary)
{ {
if (jit) if (jit)
jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true); jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true);
Remove(bp.iAddress); Remove(bp.iAddress);
} }
} }
} }
MemChecks::TMemChecksStr MemChecks::GetStrings() const MemChecks::TMemChecksStr MemChecks::GetStrings() const
{ {
TMemChecksStr mcs; TMemChecksStr mcs;
for (const TMemCheck& bp : m_MemChecks) for (const TMemCheck& bp : m_MemChecks)
{ {
std::stringstream mc; std::stringstream mc;
mc << std::hex << bp.StartAddress; mc << std::hex << bp.StartAddress;
mc << " " << (bp.bRange ? bp.EndAddress : bp.StartAddress) << " " << mc << " " << (bp.bRange ? bp.EndAddress : bp.StartAddress) << " " << (bp.bRange ? "n" : "")
(bp.bRange ? "n" : "") << (bp.OnRead ? "r" : "") << << (bp.OnRead ? "r" : "") << (bp.OnWrite ? "w" : "") << (bp.Log ? "l" : "")
(bp.OnWrite ? "w" : "") << (bp.Log ? "l" : "") << (bp.Break ? "p" : ""); << (bp.Break ? "p" : "");
mcs.push_back(mc.str()); mcs.push_back(mc.str());
} }
return mcs; return mcs;
} }
void MemChecks::AddFromStrings(const TMemChecksStr& mcstrs) void MemChecks::AddFromStrings(const TMemChecksStr& mcstrs)
{ {
for (const std::string& mcstr : mcstrs) for (const std::string& mcstr : mcstrs)
{ {
TMemCheck mc; TMemCheck mc;
std::stringstream ss; std::stringstream ss;
ss << std::hex << mcstr; ss << std::hex << mcstr;
ss >> mc.StartAddress; ss >> mc.StartAddress;
mc.bRange = mcstr.find("n") != mcstr.npos; mc.bRange = mcstr.find("n") != mcstr.npos;
mc.OnRead = mcstr.find("r") != mcstr.npos; mc.OnRead = mcstr.find("r") != mcstr.npos;
mc.OnWrite = mcstr.find("w") != mcstr.npos; mc.OnWrite = mcstr.find("w") != mcstr.npos;
mc.Log = mcstr.find("l") != mcstr.npos; mc.Log = mcstr.find("l") != mcstr.npos;
mc.Break = mcstr.find("p") != mcstr.npos; mc.Break = mcstr.find("p") != mcstr.npos;
if (mc.bRange) if (mc.bRange)
ss >> mc.EndAddress; ss >> mc.EndAddress;
else else
mc.EndAddress = mc.StartAddress; mc.EndAddress = mc.StartAddress;
Add(mc); Add(mc);
} }
} }
void MemChecks::Add(const TMemCheck& _rMemoryCheck) void MemChecks::Add(const TMemCheck& _rMemoryCheck)
{ {
bool had_any = HasAny(); bool had_any = HasAny();
if (GetMemCheck(_rMemoryCheck.StartAddress) == nullptr) if (GetMemCheck(_rMemoryCheck.StartAddress) == nullptr)
m_MemChecks.push_back(_rMemoryCheck); m_MemChecks.push_back(_rMemoryCheck);
// If this is the first one, clear the JIT cache so it can switch to // If this is the first one, clear the JIT cache so it can switch to
// watchpoint-compatible code. // watchpoint-compatible code.
if (!had_any && jit) if (!had_any && jit)
jit->ClearCache(); jit->ClearCache();
} }
void MemChecks::Remove(u32 _Address) void MemChecks::Remove(u32 _Address)
{ {
for (auto i = m_MemChecks.begin(); i != m_MemChecks.end(); ++i) for (auto i = m_MemChecks.begin(); i != m_MemChecks.end(); ++i)
{ {
if (i->StartAddress == _Address) if (i->StartAddress == _Address)
{ {
m_MemChecks.erase(i); m_MemChecks.erase(i);
return; return;
} }
} }
if (!HasAny() && jit) if (!HasAny() && jit)
jit->ClearCache(); jit->ClearCache();
} }
TMemCheck* MemChecks::GetMemCheck(u32 address) TMemCheck* MemChecks::GetMemCheck(u32 address)
{ {
for (TMemCheck& bp : m_MemChecks) for (TMemCheck& bp : m_MemChecks)
{ {
if (bp.bRange) if (bp.bRange)
{ {
if (address >= bp.StartAddress && address <= bp.EndAddress) if (address >= bp.StartAddress && address <= bp.EndAddress)
return &(bp); return &(bp);
} }
else if (bp.StartAddress == address) else if (bp.StartAddress == address)
{ {
return &(bp); return &(bp);
} }
} }
// none found // none found
return nullptr; return nullptr;
} }
bool TMemCheck::Action(DebugInterface* debug_interface, u32 iValue, u32 addr, bool write, int size, u32 pc) bool TMemCheck::Action(DebugInterface* debug_interface, u32 iValue, u32 addr, bool write, int size,
u32 pc)
{ {
if ((write && OnWrite) || (!write && OnRead)) if ((write && OnWrite) || (!write && OnRead))
{ {
if (Log) if (Log)
{ {
INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)", INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)", pc,
pc, debug_interface->GetDescription(pc).c_str(), debug_interface->GetDescription(pc).c_str(), write ? "Write" : "Read", size * 8,
write ? "Write" : "Read", size*8, size*2, iValue, addr, size * 2, iValue, addr, debug_interface->GetDescription(addr).c_str());
debug_interface->GetDescription(addr).c_str() }
);
}
return true; return true;
} }
return false; return false;
} }
bool Watches::IsAddressWatch(u32 _iAddress) const bool Watches::IsAddressWatch(u32 _iAddress) const
{ {
for (const TWatch& bp : m_Watches) for (const TWatch& bp : m_Watches)
if (bp.iAddress == _iAddress) if (bp.iAddress == _iAddress)
return true; return true;
return false; return false;
} }
Watches::TWatchesStr Watches::GetStrings() const Watches::TWatchesStr Watches::GetStrings() const
{ {
TWatchesStr bps; TWatchesStr bps;
for (const TWatch& bp : m_Watches) for (const TWatch& bp : m_Watches)
{ {
std::stringstream ss; std::stringstream ss;
ss << std::hex << bp.iAddress << " " << bp.name; ss << std::hex << bp.iAddress << " " << bp.name;
bps.push_back(ss.str()); bps.push_back(ss.str());
} }
return bps; return bps;
} }
void Watches::AddFromStrings(const TWatchesStr& bpstrs) void Watches::AddFromStrings(const TWatchesStr& bpstrs)
{ {
for (const std::string& bpstr : bpstrs) for (const std::string& bpstr : bpstrs)
{ {
TWatch bp; TWatch bp;
std::stringstream ss; std::stringstream ss;
ss << std::hex << bpstr; ss << std::hex << bpstr;
ss >> bp.iAddress; ss >> bp.iAddress;
ss >> std::ws; ss >> std::ws;
getline(ss, bp.name); getline(ss, bp.name);
Add(bp); Add(bp);
} }
} }
void Watches::Add(const TWatch& bp) void Watches::Add(const TWatch& bp)
{ {
if (!IsAddressWatch(bp.iAddress)) if (!IsAddressWatch(bp.iAddress))
{ {
m_Watches.push_back(bp); m_Watches.push_back(bp);
} }
} }
void Watches::Add(u32 em_address) void Watches::Add(u32 em_address)
{ {
if (!IsAddressWatch(em_address)) // only add new addresses if (!IsAddressWatch(em_address)) // only add new addresses
{ {
TWatch pt; // breakpoint settings TWatch pt; // breakpoint settings
pt.bOn = true; pt.bOn = true;
pt.iAddress = em_address; pt.iAddress = em_address;
m_Watches.push_back(pt); m_Watches.push_back(pt);
} }
} }
void Watches::Update(int count, u32 em_address) void Watches::Update(int count, u32 em_address)
{ {
m_Watches.at(count).iAddress = em_address; m_Watches.at(count).iAddress = em_address;
} }
void Watches::UpdateName(int count, const std::string name) void Watches::UpdateName(int count, const std::string name)
{ {
m_Watches.at(count).name = name; m_Watches.at(count).name = name;
} }
void Watches::Remove(u32 em_address) void Watches::Remove(u32 em_address)
{ {
for (auto i = m_Watches.begin(); i != m_Watches.end(); ++i) for (auto i = m_Watches.begin(); i != m_Watches.end(); ++i)
{ {
if (i->iAddress == em_address) if (i->iAddress == em_address)
{ {
m_Watches.erase(i); m_Watches.erase(i);
return; return;
} }
} }
} }
void Watches::Clear() void Watches::Clear()
{ {
m_Watches.clear(); m_Watches.clear();
} }

View File

@ -13,124 +13,118 @@ class DebugInterface;
struct TBreakPoint struct TBreakPoint
{ {
u32 iAddress; u32 iAddress;
bool bOn; bool bOn;
bool bTemporary; bool bTemporary;
}; };
struct TMemCheck struct TMemCheck
{ {
TMemCheck() TMemCheck()
{ {
numHits = 0; numHits = 0;
StartAddress = EndAddress = 0; StartAddress = EndAddress = 0;
bRange = OnRead = OnWrite = Log = Break = false; bRange = OnRead = OnWrite = Log = Break = false;
} }
u32 StartAddress; u32 StartAddress;
u32 EndAddress; u32 EndAddress;
bool bRange; bool bRange;
bool OnRead; bool OnRead;
bool OnWrite; bool OnWrite;
bool Log; bool Log;
bool Break; bool Break;
u32 numHits; u32 numHits;
// returns whether to break // returns whether to break
bool Action(DebugInterface* dbg_interface, u32 _iValue, u32 addr, bool Action(DebugInterface* dbg_interface, u32 _iValue, u32 addr, bool write, int size, u32 pc);
bool write, int size, u32 pc);
}; };
struct TWatch struct TWatch
{ {
std::string name = ""; std::string name = "";
u32 iAddress; u32 iAddress;
bool bOn; bool bOn;
}; };
// Code breakpoints. // Code breakpoints.
class BreakPoints class BreakPoints
{ {
public: public:
typedef std::vector<TBreakPoint> TBreakPoints; typedef std::vector<TBreakPoint> TBreakPoints;
typedef std::vector<std::string> TBreakPointsStr; typedef std::vector<std::string> TBreakPointsStr;
const TBreakPoints& GetBreakPoints() { return m_BreakPoints; } const TBreakPoints& GetBreakPoints() { return m_BreakPoints; }
TBreakPointsStr GetStrings() const;
void AddFromStrings(const TBreakPointsStr& bps);
TBreakPointsStr GetStrings() const; // is address breakpoint
void AddFromStrings(const TBreakPointsStr& bps); bool IsAddressBreakPoint(u32 address) const;
bool IsTempBreakPoint(u32 address) const;
// is address breakpoint // Add BreakPoint
bool IsAddressBreakPoint(u32 address) const; void Add(u32 em_address, bool temp = false);
bool IsTempBreakPoint(u32 address) const; void Add(const TBreakPoint& bp);
// Add BreakPoint // Remove Breakpoint
void Add(u32 em_address, bool temp = false); void Remove(u32 _iAddress);
void Add(const TBreakPoint& bp); void Clear();
void ClearAllTemporary();
// Remove Breakpoint
void Remove(u32 _iAddress);
void Clear();
void ClearAllTemporary();
private: private:
TBreakPoints m_BreakPoints; TBreakPoints m_BreakPoints;
}; };
// Memory breakpoints // Memory breakpoints
class MemChecks class MemChecks
{ {
public: public:
typedef std::vector<TMemCheck> TMemChecks; typedef std::vector<TMemCheck> TMemChecks;
typedef std::vector<std::string> TMemChecksStr; typedef std::vector<std::string> TMemChecksStr;
TMemChecks m_MemChecks; TMemChecks m_MemChecks;
const TMemChecks& GetMemChecks() { return m_MemChecks; } const TMemChecks& GetMemChecks() { return m_MemChecks; }
TMemChecksStr GetStrings() const;
void AddFromStrings(const TMemChecksStr& mcs);
TMemChecksStr GetStrings() const; void Add(const TMemCheck& _rMemoryCheck);
void AddFromStrings(const TMemChecksStr& mcs);
void Add(const TMemCheck& _rMemoryCheck); // memory breakpoint
TMemCheck* GetMemCheck(u32 address);
void Remove(u32 _Address);
// memory breakpoint void Clear() { m_MemChecks.clear(); }
TMemCheck* GetMemCheck(u32 address); bool HasAny() const { return !m_MemChecks.empty(); }
void Remove(u32 _Address);
void Clear() { m_MemChecks.clear(); }
bool HasAny() const { return !m_MemChecks.empty(); }
}; };
class Watches class Watches
{ {
public: public:
typedef std::vector<TWatch> TWatches; typedef std::vector<TWatch> TWatches;
typedef std::vector<std::string> TWatchesStr; typedef std::vector<std::string> TWatchesStr;
const TWatches& GetWatches() { return m_Watches; } const TWatches& GetWatches() { return m_Watches; }
TWatchesStr GetStrings() const;
void AddFromStrings(const TWatchesStr& bps);
TWatchesStr GetStrings() const; bool IsAddressWatch(u32 _iAddress) const;
void AddFromStrings(const TWatchesStr& bps);
bool IsAddressWatch(u32 _iAddress) const; // Add BreakPoint
void Add(u32 em_address);
void Add(const TWatch& bp);
// Add BreakPoint void Update(int count, u32 em_address);
void Add(u32 em_address); void UpdateName(int count, const std::string name);
void Add(const TWatch& bp);
void Update(int count, u32 em_address); // Remove Breakpoint
void UpdateName(int count, const std::string name); void Remove(u32 _iAddress);
void Clear();
// Remove Breakpoint
void Remove(u32 _iAddress);
void Clear();
private: private:
TWatches m_Watches; TWatches m_Watches;
}; };

View File

@ -25,10 +25,10 @@
#include <paths.h> #include <paths.h>
#else #else
#include <fcntl.h> #include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#endif // WIN32 #endif // WIN32
#ifdef __linux__ #ifdef __linux__
#include <linux/cdrom.h> #include <linux/cdrom.h>
@ -38,171 +38,163 @@
// takes a root drive path, returns true if it is a cdrom drive // takes a root drive path, returns true if it is a cdrom drive
bool is_cdrom(const TCHAR* drive) bool is_cdrom(const TCHAR* drive)
{ {
return (DRIVE_CDROM == GetDriveType(drive)); return (DRIVE_CDROM == GetDriveType(drive));
} }
// Returns a vector with the device names // Returns a vector with the device names
std::vector<std::string> cdio_get_devices() std::vector<std::string> cdio_get_devices()
{ {
std::vector<std::string> drives; std::vector<std::string> drives;
const DWORD buffsize = GetLogicalDriveStrings(0, nullptr); const DWORD buffsize = GetLogicalDriveStrings(0, nullptr);
std::vector<TCHAR> buff(buffsize); std::vector<TCHAR> buff(buffsize);
if (GetLogicalDriveStrings(buffsize, buff.data()) == buffsize - 1) if (GetLogicalDriveStrings(buffsize, buff.data()) == buffsize - 1)
{ {
auto drive = buff.data(); auto drive = buff.data();
while (*drive) while (*drive)
{ {
if (is_cdrom(drive)) if (is_cdrom(drive))
{ {
std::string str(TStrToUTF8(drive)); std::string str(TStrToUTF8(drive));
str.pop_back(); // we don't want the final backslash str.pop_back(); // we don't want the final backslash
drives.push_back(std::move(str)); drives.push_back(std::move(str));
} }
// advance to next drive // advance to next drive
while (*drive++) {} while (*drive++)
} {
} }
return drives; }
}
return drives;
} }
#elif defined __APPLE__ #elif defined __APPLE__
// Returns a pointer to an array of strings with the device names // Returns a pointer to an array of strings with the device names
std::vector<std::string> cdio_get_devices() std::vector<std::string> cdio_get_devices()
{ {
io_object_t next_media; io_object_t next_media;
mach_port_t master_port; mach_port_t master_port;
kern_return_t kern_result; kern_return_t kern_result;
io_iterator_t media_iterator; io_iterator_t media_iterator;
CFMutableDictionaryRef classes_to_match; CFMutableDictionaryRef classes_to_match;
std::vector<std::string> drives; std::vector<std::string> drives;
kern_result = IOMasterPort(MACH_PORT_NULL, &master_port); kern_result = IOMasterPort(MACH_PORT_NULL, &master_port);
if (kern_result != KERN_SUCCESS) if (kern_result != KERN_SUCCESS)
return drives; return drives;
classes_to_match = IOServiceMatching(kIOCDMediaClass); classes_to_match = IOServiceMatching(kIOCDMediaClass);
if (classes_to_match == nullptr) if (classes_to_match == nullptr)
return drives; return drives;
CFDictionarySetValue(classes_to_match, CFDictionarySetValue(classes_to_match, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
kern_result = IOServiceGetMatchingServices(master_port, kern_result = IOServiceGetMatchingServices(master_port, classes_to_match, &media_iterator);
classes_to_match, &media_iterator); if (kern_result != KERN_SUCCESS)
if (kern_result != KERN_SUCCESS) return drives;
return drives;
next_media = IOIteratorNext(media_iterator); next_media = IOIteratorNext(media_iterator);
if (next_media != 0) if (next_media != 0)
{ {
CFTypeRef str_bsd_path; CFTypeRef str_bsd_path;
do do
{ {
str_bsd_path = str_bsd_path =
IORegistryEntryCreateCFProperty(next_media, IORegistryEntryCreateCFProperty(next_media, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
CFSTR(kIOBSDNameKey), kCFAllocatorDefault, if (str_bsd_path == nullptr)
0); {
if (str_bsd_path == nullptr) IOObjectRelease(next_media);
{ continue;
IOObjectRelease(next_media); }
continue;
}
if (CFGetTypeID(str_bsd_path) == CFStringGetTypeID()) if (CFGetTypeID(str_bsd_path) == CFStringGetTypeID())
{ {
size_t buf_size = CFStringGetLength((CFStringRef)str_bsd_path) * 4 + 1; size_t buf_size = CFStringGetLength((CFStringRef)str_bsd_path) * 4 + 1;
char* buf = new char[buf_size]; char* buf = new char[buf_size];
if (CFStringGetCString((CFStringRef)str_bsd_path, buf, buf_size, kCFStringEncodingUTF8)) if (CFStringGetCString((CFStringRef)str_bsd_path, buf, buf_size, kCFStringEncodingUTF8))
{ {
// Below, by appending 'r' to the BSD node name, we indicate // Below, by appending 'r' to the BSD node name, we indicate
// a raw disk. Raw disks receive I/O requests directly and // a raw disk. Raw disks receive I/O requests directly and
// don't go through a buffer cache. // don't go through a buffer cache.
drives.push_back(std::string(_PATH_DEV "r") + buf); drives.push_back(std::string(_PATH_DEV "r") + buf);
} }
delete[] buf; delete[] buf;
} }
CFRelease(str_bsd_path); CFRelease(str_bsd_path);
IOObjectRelease(next_media); IOObjectRelease(next_media);
} while ((next_media = IOIteratorNext(media_iterator)) != 0); } while ((next_media = IOIteratorNext(media_iterator)) != 0);
} }
IOObjectRelease(media_iterator); IOObjectRelease(media_iterator);
return drives; return drives;
} }
#else #else
// checklist: /dev/cdrom, /dev/dvd /dev/hd?, /dev/scd? /dev/sr? // checklist: /dev/cdrom, /dev/dvd /dev/hd?, /dev/scd? /dev/sr?
static struct static struct
{ {
const char* format; const char* format;
unsigned int num_min; unsigned int num_min;
unsigned int num_max; unsigned int num_max;
} checklist[] = } checklist[] = {
{
#ifdef __linux__ #ifdef __linux__
{ "/dev/cdrom", 0, 0 }, {"/dev/cdrom", 0, 0}, {"/dev/dvd", 0, 0}, {"/dev/hd%c", 'a', 'z'},
{ "/dev/dvd", 0, 0 }, {"/dev/scd%d", 0, 27}, {"/dev/sr%d", 0, 27},
{ "/dev/hd%c", 'a', 'z' },
{ "/dev/scd%d", 0, 27 },
{ "/dev/sr%d", 0, 27 },
#else #else
{ "/dev/acd%d", 0, 27 }, {"/dev/acd%d", 0, 27},
{ "/dev/cd%d", 0, 27 }, {"/dev/cd%d", 0, 27},
#endif #endif
{ nullptr, 0, 0 } {nullptr, 0, 0}};
};
// Returns true if a device is a block or char device and not a symbolic link // Returns true if a device is a block or char device and not a symbolic link
static bool is_device(const std::string& source_name) static bool is_device(const std::string& source_name)
{ {
struct stat buf; struct stat buf;
if (0 != lstat(source_name.c_str(), &buf)) if (0 != lstat(source_name.c_str(), &buf))
return false; return false;
return ((S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)) && return ((S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)) && !S_ISLNK(buf.st_mode));
!S_ISLNK(buf.st_mode));
} }
// Check a device to see if it is a DVD/CD-ROM drive // Check a device to see if it is a DVD/CD-ROM drive
static bool is_cdrom(const std::string& drive, char* mnttype) static bool is_cdrom(const std::string& drive, char* mnttype)
{ {
// Check if the device exists // Check if the device exists
if (!is_device(drive)) if (!is_device(drive))
return false; return false;
bool is_cd = false; bool is_cd = false;
// If it does exist, verify that it is a cdrom/dvd drive // If it does exist, verify that it is a cdrom/dvd drive
int cdfd = open(drive.c_str(), (O_RDONLY | O_NONBLOCK), 0); int cdfd = open(drive.c_str(), (O_RDONLY | O_NONBLOCK), 0);
if (cdfd >= 0) if (cdfd >= 0)
{ {
#ifdef __linux__ #ifdef __linux__
if (ioctl(cdfd, CDROM_GET_CAPABILITY, 0) != -1) if (ioctl(cdfd, CDROM_GET_CAPABILITY, 0) != -1)
#endif #endif
is_cd = true; is_cd = true;
close(cdfd); close(cdfd);
} }
return is_cd; return is_cd;
} }
// Returns a pointer to an array of strings with the device names // Returns a pointer to an array of strings with the device names
std::vector<std::string> cdio_get_devices() std::vector<std::string> cdio_get_devices()
{ {
std::vector<std::string> drives; std::vector<std::string> drives;
// Scan the system for DVD/CD-ROM drives. // Scan the system for DVD/CD-ROM drives.
for (unsigned int i = 0; checklist[i].format; ++i) for (unsigned int i = 0; checklist[i].format; ++i)
{ {
for (unsigned int j = checklist[i].num_min; j <= checklist[i].num_max; ++j) for (unsigned int j = checklist[i].num_min; j <= checklist[i].num_max; ++j)
{ {
std::string drive = StringFromFormat(checklist[i].format, j); std::string drive = StringFromFormat(checklist[i].format, j);
if (is_cdrom(drive, nullptr)) if (is_cdrom(drive, nullptr))
{ {
drives.push_back(std::move(drive)); drives.push_back(std::move(drive));
} }
} }
} }
return drives; return drives;
} }
#endif #endif
@ -210,20 +202,20 @@ std::vector<std::string> cdio_get_devices()
bool cdio_is_cdrom(std::string device) bool cdio_is_cdrom(std::string device)
{ {
#ifndef _WIN32 #ifndef _WIN32
// Resolve symbolic links. This allows symbolic links to valid // Resolve symbolic links. This allows symbolic links to valid
// drives to be passed from the command line with the -e flag. // drives to be passed from the command line with the -e flag.
char resolved_path[MAX_PATH]; char resolved_path[MAX_PATH];
char* devname = realpath(device.c_str(), resolved_path); char* devname = realpath(device.c_str(), resolved_path);
if (!devname) if (!devname)
return false; return false;
device = devname; device = devname;
#endif #endif
std::vector<std::string> devices = cdio_get_devices(); std::vector<std::string> devices = cdio_get_devices();
for (const std::string& d : devices) for (const std::string& d : devices)
{ {
if (d == device) if (d == device)
return true; return true;
} }
return false; return false;
} }

View File

@ -2,7 +2,6 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
// Detect the CPU, so we'll know which optimizations to use // Detect the CPU, so we'll know which optimizations to use
#pragma once #pragma once
@ -10,68 +9,68 @@
enum CPUVendor enum CPUVendor
{ {
VENDOR_INTEL = 0, VENDOR_INTEL = 0,
VENDOR_AMD = 1, VENDOR_AMD = 1,
VENDOR_ARM = 2, VENDOR_ARM = 2,
VENDOR_OTHER = 3, VENDOR_OTHER = 3,
}; };
struct CPUInfo struct CPUInfo
{ {
CPUVendor vendor = VENDOR_INTEL; CPUVendor vendor = VENDOR_INTEL;
char cpu_string[0x41] = {}; char cpu_string[0x41] = {};
char brand_string[0x21] = {}; char brand_string[0x21] = {};
bool OS64bit = false; bool OS64bit = false;
bool CPU64bit = false; bool CPU64bit = false;
bool Mode64bit = false; bool Mode64bit = false;
bool HTT = false; bool HTT = false;
int num_cores = 0; int num_cores = 0;
int logical_cpu_count = 0; int logical_cpu_count = 0;
bool bSSE = false; bool bSSE = false;
bool bSSE2 = false; bool bSSE2 = false;
bool bSSE3 = false; bool bSSE3 = false;
bool bSSSE3 = false; bool bSSSE3 = false;
bool bPOPCNT = false; bool bPOPCNT = false;
bool bSSE4_1 = false; bool bSSE4_1 = false;
bool bSSE4_2 = false; bool bSSE4_2 = false;
bool bLZCNT = false; bool bLZCNT = false;
bool bSSE4A = false; bool bSSE4A = false;
bool bAVX = false; bool bAVX = false;
bool bAVX2 = false; bool bAVX2 = false;
bool bBMI1 = false; bool bBMI1 = false;
bool bBMI2 = false; bool bBMI2 = false;
bool bFMA = false; bool bFMA = false;
bool bFMA4 = false; bool bFMA4 = false;
bool bAES = false; bool bAES = false;
// FXSAVE/FXRSTOR // FXSAVE/FXRSTOR
bool bFXSR = false; bool bFXSR = false;
bool bMOVBE = false; bool bMOVBE = false;
// This flag indicates that the hardware supports some mode // This flag indicates that the hardware supports some mode
// in which denormal inputs _and_ outputs are automatically set to (signed) zero. // in which denormal inputs _and_ outputs are automatically set to (signed) zero.
bool bFlushToZero = false; bool bFlushToZero = false;
bool bLAHFSAHF64 = false; bool bLAHFSAHF64 = false;
bool bLongMode = false; bool bLongMode = false;
bool bAtom = false; bool bAtom = false;
// ARMv8 specific // ARMv8 specific
bool bFP = false; bool bFP = false;
bool bASIMD = false; bool bASIMD = false;
bool bCRC32 = false; bool bCRC32 = false;
bool bSHA1 = false; bool bSHA1 = false;
bool bSHA2 = false; bool bSHA2 = false;
// Call Detect() // Call Detect()
explicit CPUInfo(); explicit CPUInfo();
// Turn the CPU info into a string we can show // Turn the CPU info into a string we can show
std::string Summarize(); std::string Summarize();
private: private:
// Detects the various CPU features // Detects the various CPU features
void Detect(); void Detect();
}; };
extern CPUInfo cpu_info; extern CPUInfo cpu_info;

View File

@ -34,11 +34,14 @@
// ewww // ewww
#ifndef __has_feature #ifndef __has_feature
#define __has_feature(x) (0) #define __has_feature(x) (0)
#endif #endif
#if (__has_feature(is_trivially_copyable) && (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || (defined(__GNUC__) && __GNUC__ >= 5) #if (__has_feature(is_trivially_copyable) && \
#define IsTriviallyCopyable(T) std::is_trivially_copyable<typename std::remove_volatile<T>::type>::value (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \
(defined(__GNUC__) && __GNUC__ >= 5)
#define IsTriviallyCopyable(T) \
std::is_trivially_copyable<typename std::remove_volatile<T>::type>::value
#elif __GNUC__ #elif __GNUC__
#define IsTriviallyCopyable(T) std::has_trivial_copy_constructor<T>::value #define IsTriviallyCopyable(T) std::has_trivial_copy_constructor<T>::value
#elif _MSC_VER #elif _MSC_VER
@ -48,312 +51,310 @@
#error No version of is_trivially_copyable #error No version of is_trivially_copyable
#endif #endif
template <class T> template <class T>
struct LinkedListItem : public T struct LinkedListItem : public T
{ {
LinkedListItem<T> *next; LinkedListItem<T>* next;
}; };
// Wrapper class // Wrapper class
class PointerWrap class PointerWrap
{ {
public: public:
enum Mode enum Mode
{ {
MODE_READ = 1, // load MODE_READ = 1, // load
MODE_WRITE, // save MODE_WRITE, // save
MODE_MEASURE, // calculate size MODE_MEASURE, // calculate size
MODE_VERIFY, // compare MODE_VERIFY, // compare
}; };
u8 **ptr; u8** ptr;
Mode mode; Mode mode;
public: public:
PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_) {} PointerWrap(u8** ptr_, Mode mode_) : ptr(ptr_), mode(mode_) {}
void SetMode(Mode mode_) { mode = mode_; }
Mode GetMode() const { return mode; }
template <typename K, class V>
void Do(std::map<K, V>& x)
{
u32 count = (u32)x.size();
Do(count);
void SetMode(Mode mode_) { mode = mode_; } switch (mode)
Mode GetMode() const { return mode; } {
case MODE_READ:
for (x.clear(); count != 0; --count)
{
std::pair<K, V> pair;
Do(pair.first);
Do(pair.second);
x.insert(pair);
}
break;
template <typename K, class V> case MODE_WRITE:
void Do(std::map<K, V>& x) case MODE_MEASURE:
{ case MODE_VERIFY:
u32 count = (u32)x.size(); for (auto& elem : x)
Do(count); {
Do(elem.first);
Do(elem.second);
}
break;
}
}
switch (mode) template <typename V>
{ void Do(std::set<V>& x)
case MODE_READ: {
for (x.clear(); count != 0; --count) u32 count = (u32)x.size();
{ Do(count);
std::pair<K, V> pair;
Do(pair.first);
Do(pair.second);
x.insert(pair);
}
break;
case MODE_WRITE: switch (mode)
case MODE_MEASURE: {
case MODE_VERIFY: case MODE_READ:
for (auto& elem : x) for (x.clear(); count != 0; --count)
{ {
Do(elem.first); V value;
Do(elem.second); Do(value);
} x.insert(value);
break; }
} break;
}
template <typename V> case MODE_WRITE:
void Do(std::set<V>& x) case MODE_MEASURE:
{ case MODE_VERIFY:
u32 count = (u32)x.size(); for (V& val : x)
Do(count); {
Do(val);
}
break;
}
}
switch (mode) template <typename T>
{ void Do(std::vector<T>& x)
case MODE_READ: {
for (x.clear(); count != 0; --count) DoContainer(x);
{ }
V value;
Do(value);
x.insert(value);
}
break;
case MODE_WRITE: template <typename T>
case MODE_MEASURE: void Do(std::list<T>& x)
case MODE_VERIFY: {
for (V& val : x) DoContainer(x);
{ }
Do(val);
}
break;
}
}
template <typename T> template <typename T>
void Do(std::vector<T>& x) void Do(std::deque<T>& x)
{ {
DoContainer(x); DoContainer(x);
} }
template <typename T> template <typename T>
void Do(std::list<T>& x) void Do(std::basic_string<T>& x)
{ {
DoContainer(x); DoContainer(x);
} }
template <typename T> template <typename T, typename U>
void Do(std::deque<T>& x) void Do(std::pair<T, U>& x)
{ {
DoContainer(x); Do(x.first);
} Do(x.second);
}
template <typename T> template <typename T, std::size_t N>
void Do(std::basic_string<T>& x) void DoArray(std::array<T, N>& x)
{ {
DoContainer(x); DoArray(x.data(), (u32)x.size());
} }
template <typename T, typename U> template <typename T>
void Do(std::pair<T, U>& x) void DoArray(T* x, u32 count)
{ {
Do(x.first); static_assert(IsTriviallyCopyable(T), "Only sane for trivially copyable types");
Do(x.second); DoVoid(x, count * sizeof(T));
} }
template <typename T, std::size_t N> template <typename T, std::size_t N>
void DoArray(std::array<T, N>& x) void DoArray(T (&arr)[N])
{ {
DoArray(x.data(), (u32)x.size()); DoArray(arr, static_cast<u32>(N));
} }
template <typename T> void Do(Common::Flag& flag)
void DoArray(T* x, u32 count) {
{ bool s = flag.IsSet();
static_assert(IsTriviallyCopyable(T), "Only sane for trivially copyable types"); Do(s);
DoVoid(x, count * sizeof(T)); if (mode == MODE_READ)
} flag.Set(s);
}
template <typename T, std::size_t N> template <typename T>
void DoArray(T (&arr)[N]) void Do(std::atomic<T>& atomic)
{ {
DoArray(arr, static_cast<u32>(N)); T temp = atomic.load();
} Do(temp);
if (mode == MODE_READ)
atomic.store(temp);
}
void Do(Common::Flag& flag) template <typename T>
{ void Do(T& x)
bool s = flag.IsSet(); {
Do(s); static_assert(IsTriviallyCopyable(T), "Only sane for trivially copyable types");
if (mode == MODE_READ) // Note:
flag.Set(s); // Usually we can just use x = **ptr, etc. However, this doesn't work
} // for unions containing BitFields (long story, stupid language rules)
// or arrays. This will get optimized anyway.
DoVoid((void*)&x, sizeof(x));
}
template<typename T> template <typename T>
void Do(std::atomic<T>& atomic) void DoPOD(T& x)
{ {
T temp = atomic.load(); DoVoid((void*)&x, sizeof(x));
Do(temp); }
if (mode == MODE_READ)
atomic.store(temp);
}
template <typename T> void Do(bool& x)
void Do(T& x) {
{ // bool's size can vary depending on platform, which can
static_assert(IsTriviallyCopyable(T), "Only sane for trivially copyable types"); // cause breakages. This treats all bools as if they were
// Note: // 8 bits in size.
// Usually we can just use x = **ptr, etc. However, this doesn't work u8 stable = static_cast<u8>(x);
// for unions containing BitFields (long story, stupid language rules)
// or arrays. This will get optimized anyway.
DoVoid((void*)&x, sizeof(x));
}
template <typename T> Do(stable);
void DoPOD(T& x)
{
DoVoid((void*)&x, sizeof(x));
}
if (mode == MODE_READ)
x = stable != 0;
}
void Do(bool& x) template <typename T>
{ void DoPointer(T*& x, T* const base)
// bool's size can vary depending on platform, which can {
// cause breakages. This treats all bools as if they were // pointers can be more than 2^31 apart, but you're using this function wrong if you need that
// 8 bits in size. // much range
u8 stable = static_cast<u8>(x); ptrdiff_t offset = x - base;
Do(offset);
if (mode == MODE_READ)
{
x = base + offset;
}
}
Do(stable); // Let's pretend std::list doesn't exist!
template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*),
void (*TDo)(PointerWrap&, T*)>
void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = 0)
{
LinkedListItem<T>* list_cur = list_start;
LinkedListItem<T>* prev = nullptr;
if (mode == MODE_READ) while (true)
x = stable != 0; {
} u8 shouldExist = !!list_cur;
Do(shouldExist);
if (shouldExist == 1)
{
LinkedListItem<T>* cur = list_cur ? list_cur : TNew();
TDo(*this, (T*)cur);
if (!list_cur)
{
if (mode == MODE_READ)
{
cur->next = nullptr;
list_cur = cur;
if (prev)
prev->next = cur;
else
list_start = cur;
}
else
{
TFree(cur);
continue;
}
}
}
else
{
if (mode == MODE_READ)
{
if (prev)
prev->next = nullptr;
if (list_end)
*list_end = prev;
if (list_cur)
{
if (list_start == list_cur)
list_start = nullptr;
do
{
LinkedListItem<T>* next = list_cur->next;
TFree(list_cur);
list_cur = next;
} while (list_cur);
}
}
break;
}
prev = list_cur;
list_cur = list_cur->next;
}
}
template <typename T> void DoMarker(const std::string& prevName, u32 arbitraryNumber = 0x42)
void DoPointer(T*& x, T* const base) {
{ u32 cookie = arbitraryNumber;
// pointers can be more than 2^31 apart, but you're using this function wrong if you need that much range Do(cookie);
ptrdiff_t offset = x - base;
Do(offset);
if (mode == MODE_READ)
{
x = base + offset;
}
}
// Let's pretend std::list doesn't exist! if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber)
template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)> {
void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end=0) PanicAlertT("Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting "
{ "savestate load...",
LinkedListItem<T>* list_cur = list_start; prevName.c_str(), cookie, cookie, arbitraryNumber, arbitraryNumber);
LinkedListItem<T>* prev = nullptr; mode = PointerWrap::MODE_MEASURE;
}
while (true) }
{
u8 shouldExist = !!list_cur;
Do(shouldExist);
if (shouldExist == 1)
{
LinkedListItem<T>* cur = list_cur ? list_cur : TNew();
TDo(*this, (T*)cur);
if (!list_cur)
{
if (mode == MODE_READ)
{
cur->next = nullptr;
list_cur = cur;
if (prev)
prev->next = cur;
else
list_start = cur;
}
else
{
TFree(cur);
continue;
}
}
}
else
{
if (mode == MODE_READ)
{
if (prev)
prev->next = nullptr;
if (list_end)
*list_end = prev;
if (list_cur)
{
if (list_start == list_cur)
list_start = nullptr;
do
{
LinkedListItem<T>* next = list_cur->next;
TFree(list_cur);
list_cur = next;
} while (list_cur);
}
}
break;
}
prev = list_cur;
list_cur = list_cur->next;
}
}
void DoMarker(const std::string& prevName, u32 arbitraryNumber = 0x42)
{
u32 cookie = arbitraryNumber;
Do(cookie);
if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber)
{
PanicAlertT("Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...",
prevName.c_str(), cookie, cookie, arbitraryNumber, arbitraryNumber);
mode = PointerWrap::MODE_MEASURE;
}
}
private: private:
template <typename T> template <typename T>
void DoContainer(T& x) void DoContainer(T& x)
{ {
u32 size = (u32)x.size(); u32 size = (u32)x.size();
Do(size); Do(size);
x.resize(size); x.resize(size);
for (auto& elem : x) for (auto& elem : x)
Do(elem); Do(elem);
} }
__forceinline __forceinline void DoVoid(void* data, u32 size)
void DoVoid(void* data, u32 size) {
{ switch (mode)
switch (mode) {
{ case MODE_READ:
case MODE_READ: memcpy(data, *ptr, size);
memcpy(data, *ptr, size); break;
break;
case MODE_WRITE: case MODE_WRITE:
memcpy(*ptr, data, size); memcpy(*ptr, data, size);
break; break;
case MODE_MEASURE: case MODE_MEASURE:
break; break;
case MODE_VERIFY: case MODE_VERIFY:
_dbg_assert_msg_(COMMON, !memcmp(data, *ptr, size), _dbg_assert_msg_(COMMON, !memcmp(data, *ptr, size),
"Savestate verification failure: buf %p != %p (size %u).\n", "Savestate verification failure: buf %p != %p (size %u).\n", data, *ptr,
data, *ptr, size); size);
break; break;
} }
*ptr += size; *ptr += size;
} }
}; };
// NOTE: this class is only used in DolphinWX/ISOFile.cpp for caching loaded // NOTE: this class is only used in DolphinWX/ISOFile.cpp for caching loaded
@ -361,120 +362,119 @@ private:
class CChunkFileReader class CChunkFileReader
{ {
public: public:
// Load file template // Load file template
template<class T> template <class T>
static bool Load(const std::string& _rFilename, u32 _Revision, T& _class) static bool Load(const std::string& _rFilename, u32 _Revision, T& _class)
{ {
INFO_LOG(COMMON, "ChunkReader: Loading %s", _rFilename.c_str()); INFO_LOG(COMMON, "ChunkReader: Loading %s", _rFilename.c_str());
if (!File::Exists(_rFilename)) if (!File::Exists(_rFilename))
return false; return false;
// Check file size // Check file size
const u64 fileSize = File::GetSize(_rFilename); const u64 fileSize = File::GetSize(_rFilename);
static const u64 headerSize = sizeof(SChunkHeader); static const u64 headerSize = sizeof(SChunkHeader);
if (fileSize < headerSize) if (fileSize < headerSize)
{ {
ERROR_LOG(COMMON, "ChunkReader: File too small"); ERROR_LOG(COMMON, "ChunkReader: File too small");
return false; return false;
} }
File::IOFile pFile(_rFilename, "rb"); File::IOFile pFile(_rFilename, "rb");
if (!pFile) if (!pFile)
{ {
ERROR_LOG(COMMON, "ChunkReader: Can't open file for reading"); ERROR_LOG(COMMON, "ChunkReader: Can't open file for reading");
return false; return false;
} }
// read the header // read the header
SChunkHeader header; SChunkHeader header;
if (!pFile.ReadArray(&header, 1)) if (!pFile.ReadArray(&header, 1))
{ {
ERROR_LOG(COMMON, "ChunkReader: Bad header size"); ERROR_LOG(COMMON, "ChunkReader: Bad header size");
return false; return false;
} }
// Check revision // Check revision
if (header.Revision != _Revision) if (header.Revision != _Revision)
{ {
ERROR_LOG(COMMON, "ChunkReader: Wrong file revision, got %d expected %d", ERROR_LOG(COMMON, "ChunkReader: Wrong file revision, got %d expected %d", header.Revision,
header.Revision, _Revision); _Revision);
return false; return false;
} }
// get size // get size
const u32 sz = (u32)(fileSize - headerSize); const u32 sz = (u32)(fileSize - headerSize);
if (header.ExpectedSize != sz) if (header.ExpectedSize != sz)
{ {
ERROR_LOG(COMMON, "ChunkReader: Bad file size, got %d expected %d", ERROR_LOG(COMMON, "ChunkReader: Bad file size, got %d expected %d", sz, header.ExpectedSize);
sz, header.ExpectedSize); return false;
return false; }
}
// read the state // read the state
std::vector<u8> buffer(sz); std::vector<u8> buffer(sz);
if (!pFile.ReadArray(&buffer[0], sz)) if (!pFile.ReadArray(&buffer[0], sz))
{ {
ERROR_LOG(COMMON, "ChunkReader: Error reading file"); ERROR_LOG(COMMON, "ChunkReader: Error reading file");
return false; return false;
} }
u8* ptr = &buffer[0]; u8* ptr = &buffer[0];
PointerWrap p(&ptr, PointerWrap::MODE_READ); PointerWrap p(&ptr, PointerWrap::MODE_READ);
_class.DoState(p); _class.DoState(p);
INFO_LOG(COMMON, "ChunkReader: Done loading %s", _rFilename.c_str()); INFO_LOG(COMMON, "ChunkReader: Done loading %s", _rFilename.c_str());
return true; return true;
} }
// Save file template // Save file template
template<class T> template <class T>
static bool Save(const std::string& _rFilename, u32 _Revision, T& _class) static bool Save(const std::string& _rFilename, u32 _Revision, T& _class)
{ {
INFO_LOG(COMMON, "ChunkReader: Writing %s", _rFilename.c_str()); INFO_LOG(COMMON, "ChunkReader: Writing %s", _rFilename.c_str());
File::IOFile pFile(_rFilename, "wb"); File::IOFile pFile(_rFilename, "wb");
if (!pFile) if (!pFile)
{ {
ERROR_LOG(COMMON, "ChunkReader: Error opening file for write"); ERROR_LOG(COMMON, "ChunkReader: Error opening file for write");
return false; return false;
} }
// Get data // Get data
u8* ptr = nullptr; u8* ptr = nullptr;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
_class.DoState(p); _class.DoState(p);
size_t const sz = (size_t)ptr; size_t const sz = (size_t)ptr;
std::vector<u8> buffer(sz); std::vector<u8> buffer(sz);
ptr = &buffer[0]; ptr = &buffer[0];
p.SetMode(PointerWrap::MODE_WRITE); p.SetMode(PointerWrap::MODE_WRITE);
_class.DoState(p); _class.DoState(p);
// Create header // Create header
SChunkHeader header; SChunkHeader header;
header.Revision = _Revision; header.Revision = _Revision;
header.ExpectedSize = (u32)sz; header.ExpectedSize = (u32)sz;
// Write to file // Write to file
if (!pFile.WriteArray(&header, 1)) if (!pFile.WriteArray(&header, 1))
{ {
ERROR_LOG(COMMON, "ChunkReader: Failed writing header"); ERROR_LOG(COMMON, "ChunkReader: Failed writing header");
return false; return false;
} }
if (!pFile.WriteArray(&buffer[0], sz)) if (!pFile.WriteArray(&buffer[0], sz))
{ {
ERROR_LOG(COMMON, "ChunkReader: Failed writing data"); ERROR_LOG(COMMON, "ChunkReader: Failed writing data");
return false; return false;
} }
INFO_LOG(COMMON, "ChunkReader: Done writing %s", _rFilename.c_str()); INFO_LOG(COMMON, "ChunkReader: Done writing %s", _rFilename.c_str());
return true; return true;
} }
private: private:
struct SChunkHeader struct SChunkHeader
{ {
u32 Revision; u32 Revision;
u32 ExpectedSize; u32 ExpectedSize;
}; };
}; };

View File

@ -13,98 +13,92 @@
// having to prefix them with gen-> or something similar. // having to prefix them with gen-> or something similar.
// Example implementation: // Example implementation:
// class JIT : public CodeBlock<ARMXEmitter> {} // class JIT : public CodeBlock<ARMXEmitter> {}
template<class T> class CodeBlock : public T, NonCopyable template <class T>
class CodeBlock : public T, NonCopyable
{ {
private: private:
// A privately used function to set the executable RAM space to something invalid. // A privately used function to set the executable RAM space to something invalid.
// For debugging usefulness it should be used to set the RAM to a host specific breakpoint instruction // For debugging usefulness it should be used to set the RAM to a host specific breakpoint
virtual void PoisonMemory() = 0; // instruction
virtual void PoisonMemory() = 0;
protected: protected:
u8* region; u8* region;
size_t region_size; size_t region_size;
size_t parent_region_size; size_t parent_region_size;
bool m_has_child; bool m_has_child;
bool m_is_child; bool m_is_child;
CodeBlock* m_child; CodeBlock* m_child;
public: public:
CodeBlock() CodeBlock()
: region(nullptr), region_size(0), parent_region_size(0), : region(nullptr), region_size(0), parent_region_size(0), m_has_child(false),
m_has_child(false), m_is_child(false), m_child(nullptr) m_is_child(false), m_child(nullptr)
{ {
} }
virtual ~CodeBlock() { if (region) FreeCodeSpace(); } virtual ~CodeBlock()
{
if (region)
FreeCodeSpace();
}
// Call this before you generate any code. // Call this before you generate any code.
void AllocCodeSpace(int size, bool need_low = true) void AllocCodeSpace(int size, bool need_low = true)
{ {
region_size = size; region_size = size;
region = (u8*)AllocateExecutableMemory(region_size, need_low); region = (u8*)AllocateExecutableMemory(region_size, need_low);
T::SetCodePtr(region); T::SetCodePtr(region);
} }
// Always clear code space with breakpoints, so that if someone accidentally executes // Always clear code space with breakpoints, so that if someone accidentally executes
// uninitialized, it just breaks into the debugger. // uninitialized, it just breaks into the debugger.
void ClearCodeSpace() void ClearCodeSpace()
{ {
PoisonMemory(); PoisonMemory();
ResetCodePtr(); ResetCodePtr();
} }
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job. // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
void FreeCodeSpace() void FreeCodeSpace()
{ {
FreeMemoryPages(region, region_size); FreeMemoryPages(region, region_size);
region = nullptr; region = nullptr;
region_size = 0; region_size = 0;
parent_region_size = 0; parent_region_size = 0;
if (m_has_child) if (m_has_child)
{ {
m_child->region = nullptr; m_child->region = nullptr;
m_child->region_size = 0; m_child->region_size = 0;
} }
} }
bool IsInSpace(u8* ptr) const bool IsInSpace(u8* ptr) const { return (ptr >= region) && (ptr < (region + region_size)); }
{ // Cannot currently be undone. Will write protect the entire code region.
return (ptr >= region) && (ptr < (region + region_size)); // Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
} void WriteProtect() { WriteProtectMemory(region, region_size, true); }
void ResetCodePtr() { T::SetCodePtr(region); }
size_t GetSpaceLeft() const
{
return (m_has_child ? parent_region_size : region_size) - (T::GetCodePtr() - region);
}
// Cannot currently be undone. Will write protect the entire code region. bool IsAlmostFull() const
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()). {
void WriteProtect() // This should be bigger than the biggest block ever.
{ return GetSpaceLeft() < 0x10000;
WriteProtectMemory(region, region_size, true); }
} void AddChildCodeSpace(CodeBlock* child, size_t size)
{
void ResetCodePtr() _assert_msg_(DYNA_REC, !m_has_child, "Already have a child! Can't have another!");
{ m_child = child;
T::SetCodePtr(region); m_has_child = true;
} m_child->m_is_child = true;
u8* child_region = region + region_size - size;
size_t GetSpaceLeft() const m_child->region = child_region;
{ m_child->region_size = size;
return (m_has_child ? parent_region_size : region_size) - (T::GetCodePtr() - region); m_child->ResetCodePtr();
} parent_region_size = region_size - size;
}
bool IsAlmostFull() const
{
// This should be bigger than the biggest block ever.
return GetSpaceLeft() < 0x10000;
}
void AddChildCodeSpace(CodeBlock* child, size_t size)
{
_assert_msg_(DYNA_REC, !m_has_child, "Already have a child! Can't have another!");
m_child = child;
m_has_child = true;
m_child->m_is_child = true;
u8* child_region = region + region_size - size;
m_child->region = child_region;
m_child->region_size = size;
m_child->ResetCodePtr();
parent_region_size = region_size - size;
}
}; };

View File

@ -7,82 +7,74 @@
namespace ColorUtil namespace ColorUtil
{ {
static const int s_lut5to8[] = {0x00, 0x08, 0x10, 0x18, 0x20, 0x29, 0x31, 0x39, 0x41, 0x4A, 0x52,
0x5A, 0x62, 0x6A, 0x73, 0x7B, 0x83, 0x8B, 0x94, 0x9C, 0xA4, 0xAC,
0xB4, 0xBD, 0xC5, 0xCD, 0xD5, 0xDE, 0xE6, 0xEE, 0xF6, 0xFF};
static const int s_lut5to8[] = { static const int s_lut4to8[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B,
0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD,
0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF
};
static const int s_lut4to8[] = { static const int s_lut3to8[] = {0x00, 0x24, 0x48, 0x6D, 0x91, 0xB6, 0xDA, 0xFF};
0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF
};
static const int s_lut3to8[] = {
0x00,0x24,0x48,0x6D,0x91,0xB6,0xDA,0xFF
};
static u32 Decode5A3(u16 val) static u32 Decode5A3(u16 val)
{ {
const u32 bg_color = 0x00000000; const u32 bg_color = 0x00000000;
int r, g, b, a; int r, g, b, a;
if (val & 0x8000) if (val & 0x8000)
{ {
r = s_lut5to8[(val >> 10) & 0x1f]; r = s_lut5to8[(val >> 10) & 0x1f];
g = s_lut5to8[(val >> 5) & 0x1f]; g = s_lut5to8[(val >> 5) & 0x1f];
b = s_lut5to8[(val) & 0x1f]; b = s_lut5to8[(val)&0x1f];
a = 0xFF; a = 0xFF;
} }
else else
{ {
a = s_lut3to8[(val >> 12) & 0x7]; a = s_lut3to8[(val >> 12) & 0x7];
r = (s_lut4to8[(val >> 8) & 0xf] * a + (bg_color & 0xFF) * (255 - a)) / 255; r = (s_lut4to8[(val >> 8) & 0xf] * a + (bg_color & 0xFF) * (255 - a)) / 255;
g = (s_lut4to8[(val >> 4) & 0xf] * a + ((bg_color >> 8) & 0xFF) * (255 - a)) / 255; g = (s_lut4to8[(val >> 4) & 0xf] * a + ((bg_color >> 8) & 0xFF) * (255 - a)) / 255;
b = (s_lut4to8[(val) & 0xf] * a + ((bg_color >> 16) & 0xFF) * (255 - a)) / 255; b = (s_lut4to8[(val)&0xf] * a + ((bg_color >> 16) & 0xFF) * (255 - a)) / 255;
a = 0xFF; a = 0xFF;
} }
return (a << 24) | (r << 16) | (g << 8) | b; return (a << 24) | (r << 16) | (g << 8) | b;
} }
void decode5A3image(u32* dst, u16* src, int width, int height) void decode5A3image(u32* dst, u16* src, int width, int height)
{ {
for (int y = 0; y < height; y += 4) for (int y = 0; y < height; y += 4)
{ {
for (int x = 0; x < width; x += 4) for (int x = 0; x < width; x += 4)
{ {
for (int iy = 0; iy < 4; iy++, src += 4) for (int iy = 0; iy < 4; iy++, src += 4)
{ {
for (int ix = 0; ix < 4; ix++) for (int ix = 0; ix < 4; ix++)
{ {
u32 RGBA = Decode5A3(Common::swap16(src[ix])); u32 RGBA = Decode5A3(Common::swap16(src[ix]));
dst[(y + iy) * width + (x + ix)] = RGBA; dst[(y + iy) * width + (x + ix)] = RGBA;
} }
} }
} }
} }
} }
void decodeCI8image(u32* dst, u8* src, u16* pal, int width, int height) void decodeCI8image(u32* dst, u8* src, u16* pal, int width, int height)
{ {
for (int y = 0; y < height; y += 4) for (int y = 0; y < height; y += 4)
{ {
for (int x = 0; x < width; x += 8) for (int x = 0; x < width; x += 8)
{ {
for (int iy = 0; iy < 4; iy++, src += 8) for (int iy = 0; iy < 4; iy++, src += 8)
{ {
u32* tdst = dst+(y+iy)*width+x; u32* tdst = dst + (y + iy) * width + x;
for (int ix = 0; ix < 8; ix++) for (int ix = 0; ix < 8; ix++)
{ {
// huh, this seems wrong. CI8, not 5A3, no? // huh, this seems wrong. CI8, not 5A3, no?
tdst[ix] = ColorUtil::Decode5A3(Common::swap16(pal[src[ix]])); tdst[ix] = ColorUtil::Decode5A3(Common::swap16(pal[src[ix]]));
} }
} }
} }
} }
} }
} // namespace } // namespace

View File

@ -8,7 +8,6 @@
namespace ColorUtil namespace ColorUtil
{ {
void decode5A3image(u32* dst, u16* src, int width, int height); void decode5A3image(u32* dst, u16* src, int width, int height);
void decodeCI8image(u32* dst, u8* src, u16* pal, int width, int height); void decodeCI8image(u32* dst, u8* src, u16* pal, int width, int height);

View File

@ -35,25 +35,32 @@ extern const std::string scm_distributor_str;
#if defined _WIN32 #if defined _WIN32
// Memory leak checks // Memory leak checks
#define CHECK_HEAP_INTEGRITY() #define CHECK_HEAP_INTEGRITY()
// Since they are always around on Windows // Since they are always around on Windows
#define HAVE_WX 1 #define HAVE_WX 1
#define HAVE_OPENAL 1 #define HAVE_OPENAL 1
#define HAVE_PORTAUDIO 1 #define HAVE_PORTAUDIO 1
// Debug definitions // Debug definitions
#if defined(_DEBUG) #if defined(_DEBUG)
#include <crtdbg.h> #include <crtdbg.h>
#undef CHECK_HEAP_INTEGRITY #undef CHECK_HEAP_INTEGRITY
#define CHECK_HEAP_INTEGRITY() {if (!_CrtCheckMemory()) PanicAlert("memory corruption detected. see log.");} #define CHECK_HEAP_INTEGRITY() \
// If you want to see how much a pain in the ass singletons are, for example: { \
// {614} normal block at 0x030C5310, 188 bytes long. if (!_CrtCheckMemory()) \
// Data: <Master Log > 4D 61 73 74 65 72 20 4C 6F 67 00 00 00 00 00 00 PanicAlert("memory corruption detected. see log."); \
struct CrtDebugBreak { CrtDebugBreak(int spot) { _CrtSetBreakAlloc(spot); } }; }
//CrtDebugBreak breakAt(614); // If you want to see how much a pain in the ass singletons are, for example:
#endif // end DEBUG/FAST // {614} normal block at 0x030C5310, 188 bytes long.
// Data: <Master Log > 4D 61 73 74 65 72 20 4C 6F 67 00 00 00 00 00 00
struct CrtDebugBreak
{
CrtDebugBreak(int spot) { _CrtSetBreakAlloc(spot); }
};
// CrtDebugBreak breakAt(614);
#endif // end DEBUG/FAST
#endif #endif
@ -80,17 +87,17 @@ extern const std::string scm_distributor_str;
// Host communication. // Host communication.
enum HOST_COMM enum HOST_COMM
{ {
// Begin at 10 in case there is already messages with wParam = 0, 1, 2 and so on // Begin at 10 in case there is already messages with wParam = 0, 1, 2 and so on
WM_USER_STOP = 10, WM_USER_STOP = 10,
WM_USER_CREATE, WM_USER_CREATE,
WM_USER_SETCURSOR, WM_USER_SETCURSOR,
WM_USER_JOB_DISPATCH, WM_USER_JOB_DISPATCH,
}; };
// Used for notification on emulation state // Used for notification on emulation state
enum EMUSTATE_CHANGE enum EMUSTATE_CHANGE
{ {
EMUSTATE_CHANGE_PLAY = 1, EMUSTATE_CHANGE_PLAY = 1,
EMUSTATE_CHANGE_PAUSE, EMUSTATE_CHANGE_PAUSE,
EMUSTATE_CHANGE_STOP EMUSTATE_CHANGE_STOP
}; };

View File

@ -16,15 +16,15 @@
template <typename T, size_t N> template <typename T, size_t N>
constexpr size_t ArraySize(T (&arr)[N]) constexpr size_t ArraySize(T (&arr)[N])
{ {
return N; return N;
} }
#define b2(x) ( (x) | ( (x) >> 1) ) #define b2(x) ((x) | ((x) >> 1))
#define b4(x) ( b2(x) | ( b2(x) >> 2) ) #define b4(x) (b2(x) | (b2(x) >> 2))
#define b8(x) ( b4(x) | ( b4(x) >> 4) ) #define b8(x) (b4(x) | (b4(x) >> 4))
#define b16(x) ( b8(x) | ( b8(x) >> 8) ) #define b16(x) (b8(x) | (b8(x) >> 8))
#define b32(x) (b16(x) | (b16(x) >>16) ) #define b32(x) (b16(x) | (b16(x) >> 16))
#define ROUND_UP_POW2(x) (b32(x - 1) + 1) #define ROUND_UP_POW2(x) (b32(x - 1) + 1)
#ifndef _WIN32 #ifndef _WIN32
@ -36,59 +36,66 @@ constexpr size_t ArraySize(T (&arr)[N])
#endif #endif
// go to debugger mode // go to debugger mode
#define Crash() { __builtin_trap(); } #define Crash() \
{ \
__builtin_trap(); \
}
// GCC 4.8 defines all the rotate functions now // GCC 4.8 defines all the rotate functions now
// Small issue with GCC's lrotl/lrotr intrinsics is they are still 32bit while we require 64bit // Small issue with GCC's lrotl/lrotr intrinsics is they are still 32bit while we require 64bit
#ifndef _rotl #ifndef _rotl
inline u32 _rotl(u32 x, int shift) inline u32 _rotl(u32 x, int shift)
{ {
shift &= 31; shift &= 31;
if (!shift) return x; if (!shift)
return (x << shift) | (x >> (32 - shift)); return x;
return (x << shift) | (x >> (32 - shift));
} }
inline u32 _rotr(u32 x, int shift) inline u32 _rotr(u32 x, int shift)
{ {
shift &= 31; shift &= 31;
if (!shift) return x; if (!shift)
return (x >> shift) | (x << (32 - shift)); return x;
return (x >> shift) | (x << (32 - shift));
} }
#endif #endif
inline u64 _rotl64(u64 x, unsigned int shift) inline u64 _rotl64(u64 x, unsigned int shift)
{ {
unsigned int n = shift % 64; unsigned int n = shift % 64;
return (x << n) | (x >> (64 - n)); return (x << n) | (x >> (64 - n));
} }
inline u64 _rotr64(u64 x, unsigned int shift) inline u64 _rotr64(u64 x, unsigned int shift)
{ {
unsigned int n = shift % 64; unsigned int n = shift % 64;
return (x >> n) | (x << (64 - n)); return (x >> n) | (x << (64 - n));
} }
#else // WIN32 #else // WIN32
// Function Cross-Compatibility // Function Cross-Compatibility
#define strcasecmp _stricmp #define strcasecmp _stricmp
#define strncasecmp _strnicmp #define strncasecmp _strnicmp
#define unlink _unlink #define unlink _unlink
#define vscprintf _vscprintf #define vscprintf _vscprintf
// 64 bit offsets for Windows // 64 bit offsets for Windows
#define fseeko _fseeki64 #define fseeko _fseeki64
#define ftello _ftelli64 #define ftello _ftelli64
#define atoll _atoi64 #define atoll _atoi64
#define stat64 _stat64 #define stat64 _stat64
#define fstat64 _fstat64 #define fstat64 _fstat64
#define fileno _fileno #define fileno _fileno
extern "C" extern "C" {
{ __declspec(dllimport) void __stdcall DebugBreak(void);
__declspec(dllimport) void __stdcall DebugBreak(void);
} }
#define Crash() {DebugBreak();} #define Crash() \
#endif // WIN32 ndef { \
DebugBreak(); \
}
#endif // WIN32 ndef
// Generic function to get last error message. // Generic function to get last error message.
// Call directly after the command or use the error num. // Call directly after the command or use the error num.
@ -98,8 +105,14 @@ std::string GetLastErrorMsg();
namespace Common namespace Common
{ {
inline u8 swap8(u8 _data) {return _data;} inline u8 swap8(u8 _data)
inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) | _data[2];} {
return _data;
}
inline u32 swap24(const u8* _data)
{
return (_data[0] << 16) | (_data[1] << 8) | _data[2];
}
#ifdef ANDROID #ifdef ANDROID
#undef swap16 #undef swap16
@ -108,69 +121,121 @@ inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) |
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);} inline u16 swap16(u16 _data)
inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);} {
inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);} return _byteswap_ushort(_data);
}
inline u32 swap32(u32 _data)
{
return _byteswap_ulong(_data);
}
inline u64 swap64(u64 _data)
{
return _byteswap_uint64(_data);
}
#elif __linux__ && !(ANDROID && _M_ARM_64) #elif __linux__ && !(ANDROID && _M_ARM_64)
// Android NDK r10c has broken builtin byte swap routines // Android NDK r10c has broken builtin byte swap routines
// Disabled for now. // Disabled for now.
inline u16 swap16(u16 _data) {return bswap_16(_data);} inline u16 swap16(u16 _data)
inline u32 swap32(u32 _data) {return bswap_32(_data);} {
inline u64 swap64(u64 _data) {return bswap_64(_data);} return bswap_16(_data);
}
inline u32 swap32(u32 _data)
{
return bswap_32(_data);
}
inline u64 swap64(u64 _data)
{
return bswap_64(_data);
}
#elif __APPLE__ #elif __APPLE__
inline __attribute__((always_inline)) u16 swap16(u16 _data) inline __attribute__((always_inline)) u16 swap16(u16 _data)
{return OSSwapInt16(_data);} {
return OSSwapInt16(_data);
}
inline __attribute__((always_inline)) u32 swap32(u32 _data) inline __attribute__((always_inline)) u32 swap32(u32 _data)
{return OSSwapInt32(_data);} {
return OSSwapInt32(_data);
}
inline __attribute__((always_inline)) u64 swap64(u64 _data) inline __attribute__((always_inline)) u64 swap64(u64 _data)
{return OSSwapInt64(_data);} {
return OSSwapInt64(_data);
}
#elif __FreeBSD__ #elif __FreeBSD__
inline u16 swap16(u16 _data) {return bswap16(_data);} inline u16 swap16(u16 _data)
inline u32 swap32(u32 _data) {return bswap32(_data);} {
inline u64 swap64(u64 _data) {return bswap64(_data);} return bswap16(_data);
}
inline u32 swap32(u32 _data)
{
return bswap32(_data);
}
inline u64 swap64(u64 _data)
{
return bswap64(_data);
}
#else #else
// Slow generic implementation. // Slow generic implementation.
inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);} inline u16 swap16(u16 data)
inline u32 swap32(u32 data) {return (swap16(data) << 16) | swap16(data >> 16);} {
inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 32);} return (data >> 8) | (data << 8);
}
inline u32 swap32(u32 data)
{
return (swap16(data) << 16) | swap16(data >> 16);
}
inline u64 swap64(u64 data)
{
return ((u64)swap32(data) << 32) | swap32(data >> 32);
}
#endif #endif
inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);} inline u16 swap16(const u8* _pData)
inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);} {
inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);} return swap16(*(const u16*)_pData);
}
inline u32 swap32(const u8* _pData)
{
return swap32(*(const u32*)_pData);
}
inline u64 swap64(const u8* _pData)
{
return swap64(*(const u64*)_pData);
}
template <int count> template <int count>
void swap(u8*); void swap(u8*);
template <> template <>
inline void swap<1>(u8* data) inline void swap<1>(u8* data)
{} {
}
template <> template <>
inline void swap<2>(u8* data) inline void swap<2>(u8* data)
{ {
*reinterpret_cast<u16*>(data) = swap16(data); *reinterpret_cast<u16*>(data) = swap16(data);
} }
template <> template <>
inline void swap<4>(u8* data) inline void swap<4>(u8* data)
{ {
*reinterpret_cast<u32*>(data) = swap32(data); *reinterpret_cast<u32*>(data) = swap32(data);
} }
template <> template <>
inline void swap<8>(u8* data) inline void swap<8>(u8* data)
{ {
*reinterpret_cast<u64*>(data) = swap64(data); *reinterpret_cast<u64*>(data) = swap64(data);
} }
template <typename T> template <typename T>
inline T FromBigEndian(T data) inline T FromBigEndian(T data)
{ {
static_assert(std::is_arithmetic<T>::value, "function only makes sense with arithmetic types"); static_assert(std::is_arithmetic<T>::value, "function only makes sense with arithmetic types");
swap<sizeof(data)>(reinterpret_cast<u8*>(&data)); swap<sizeof(data)>(reinterpret_cast<u8*>(&data));
return data; return data;
} }
} // Namespace Common } // Namespace Common

View File

@ -11,35 +11,35 @@
// The user data dir // The user data dir
#define ROOT_DIR "." #define ROOT_DIR "."
#ifdef _WIN32 #ifdef _WIN32
#define USERDATA_DIR "User" #define USERDATA_DIR "User"
#define DOLPHIN_DATA_DIR "Dolphin" #define DOLPHIN_DATA_DIR "Dolphin"
#elif defined __APPLE__ #elif defined __APPLE__
// On OS X, USERDATA_DIR exists within the .app, but *always* reference // On OS X, USERDATA_DIR exists within the .app, but *always* reference
// the copy in Application Support instead! (Copied on first run) // the copy in Application Support instead! (Copied on first run)
// You can use the File::GetUserPath() util for this // You can use the File::GetUserPath() util for this
#define USERDATA_DIR "Contents/Resources/User" #define USERDATA_DIR "Contents/Resources/User"
#define DOLPHIN_DATA_DIR "Library/Application Support/Dolphin" #define DOLPHIN_DATA_DIR "Library/Application Support/Dolphin"
#elif defined ANDROID #elif defined ANDROID
#define USERDATA_DIR "user" #define USERDATA_DIR "user"
#define DOLPHIN_DATA_DIR "/sdcard/dolphin-emu" #define DOLPHIN_DATA_DIR "/sdcard/dolphin-emu"
#else #else
#define USERDATA_DIR "user" #define USERDATA_DIR "user"
#define DOLPHIN_DATA_DIR "dolphin-emu" #define DOLPHIN_DATA_DIR "dolphin-emu"
#endif #endif
// Shared data dirs (Sys and shared User for Linux) // Shared data dirs (Sys and shared User for Linux)
#if defined(_WIN32) || defined(LINUX_LOCAL_DEV) #if defined(_WIN32) || defined(LINUX_LOCAL_DEV)
#define SYSDATA_DIR "Sys" #define SYSDATA_DIR "Sys"
#elif defined __APPLE__ #elif defined __APPLE__
#define SYSDATA_DIR "Contents/Resources/Sys" #define SYSDATA_DIR "Contents/Resources/Sys"
#elif defined ANDROID #elif defined ANDROID
#define SYSDATA_DIR "/sdcard/dolphin-emu" #define SYSDATA_DIR "/sdcard/dolphin-emu"
#else #else
#ifdef DATA_DIR #ifdef DATA_DIR
#define SYSDATA_DIR DATA_DIR "sys" #define SYSDATA_DIR DATA_DIR "sys"
#else #else
#define SYSDATA_DIR "sys" #define SYSDATA_DIR "sys"
#endif #endif
#endif #endif
// Dirs in both User and Sys // Dirs in both User and Sys
@ -48,72 +48,72 @@
#define JAP_DIR "JAP" #define JAP_DIR "JAP"
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX) // Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
#define GC_USER_DIR "GC" #define GC_USER_DIR "GC"
#define WII_USER_DIR "Wii" #define WII_USER_DIR "Wii"
#define CONFIG_DIR "Config" #define CONFIG_DIR "Config"
#define GAMESETTINGS_DIR "GameSettings" #define GAMESETTINGS_DIR "GameSettings"
#define MAPS_DIR "Maps" #define MAPS_DIR "Maps"
#define CACHE_DIR "Cache" #define CACHE_DIR "Cache"
#define SHADERCACHE_DIR "Shaders" #define SHADERCACHE_DIR "Shaders"
#define STATESAVES_DIR "StateSaves" #define STATESAVES_DIR "StateSaves"
#define SCREENSHOTS_DIR "ScreenShots" #define SCREENSHOTS_DIR "ScreenShots"
#define LOAD_DIR "Load" #define LOAD_DIR "Load"
#define HIRES_TEXTURES_DIR "Textures" #define HIRES_TEXTURES_DIR "Textures"
#define DUMP_DIR "Dump" #define DUMP_DIR "Dump"
#define DUMP_TEXTURES_DIR "Textures" #define DUMP_TEXTURES_DIR "Textures"
#define DUMP_FRAMES_DIR "Frames" #define DUMP_FRAMES_DIR "Frames"
#define DUMP_AUDIO_DIR "Audio" #define DUMP_AUDIO_DIR "Audio"
#define DUMP_DSP_DIR "DSP" #define DUMP_DSP_DIR "DSP"
#define LOGS_DIR "Logs" #define LOGS_DIR "Logs"
#define MAIL_LOGS_DIR "Mail" #define MAIL_LOGS_DIR "Mail"
#define SHADERS_DIR "Shaders" #define SHADERS_DIR "Shaders"
#define WII_SYSCONF_DIR "shared2" DIR_SEP "sys" #define WII_SYSCONF_DIR "shared2" DIR_SEP "sys"
#define WII_WC24CONF_DIR "shared2" DIR_SEP "wc24" #define WII_WC24CONF_DIR "shared2" DIR_SEP "wc24"
#define RESOURCES_DIR "Resources" #define RESOURCES_DIR "Resources"
#define THEMES_DIR "Themes" #define THEMES_DIR "Themes"
#define ANAGLYPH_DIR "Anaglyph" #define ANAGLYPH_DIR "Anaglyph"
#define PIPES_DIR "Pipes" #define PIPES_DIR "Pipes"
#define MEMORYWATCHER_DIR "MemoryWatcher" #define MEMORYWATCHER_DIR "MemoryWatcher"
// This one is only used to remove it if it was present // This one is only used to remove it if it was present
#define SHADERCACHE_LEGACY_DIR "ShaderCache" #define SHADERCACHE_LEGACY_DIR "ShaderCache"
// Filenames // Filenames
// Files in the directory returned by GetUserPath(D_CONFIG_IDX) // Files in the directory returned by GetUserPath(D_CONFIG_IDX)
#define DOLPHIN_CONFIG "Dolphin.ini" #define DOLPHIN_CONFIG "Dolphin.ini"
#define DEBUGGER_CONFIG "Debugger.ini" #define DEBUGGER_CONFIG "Debugger.ini"
#define LOGGER_CONFIG "Logger.ini" #define LOGGER_CONFIG "Logger.ini"
// Files in the directory returned by GetUserPath(D_LOGS_IDX) // Files in the directory returned by GetUserPath(D_LOGS_IDX)
#define MAIN_LOG "dolphin.log" #define MAIN_LOG "dolphin.log"
// Files in the directory returned by GetUserPath(D_WIISYSCONF_IDX) // Files in the directory returned by GetUserPath(D_WIISYSCONF_IDX)
#define WII_SYSCONF "SYSCONF" #define WII_SYSCONF "SYSCONF"
// Files in the directory returned by GetUserPath(D_DUMP_IDX) // Files in the directory returned by GetUserPath(D_DUMP_IDX)
#define RAM_DUMP "ram.raw" #define RAM_DUMP "ram.raw"
#define ARAM_DUMP "aram.raw" #define ARAM_DUMP "aram.raw"
#define FAKEVMEM_DUMP "fakevmem.raw" #define FAKEVMEM_DUMP "fakevmem.raw"
// Files in the directory returned by GetUserPath(D_MEMORYWATCHER_IDX) // Files in the directory returned by GetUserPath(D_MEMORYWATCHER_IDX)
#define MEMORYWATCHER_LOCATIONS "Locations.txt" #define MEMORYWATCHER_LOCATIONS "Locations.txt"
#define MEMORYWATCHER_SOCKET "MemoryWatcher" #define MEMORYWATCHER_SOCKET "MemoryWatcher"
// Sys files // Sys files
#define TOTALDB "totaldb.dsy" #define TOTALDB "totaldb.dsy"
#define FONT_ANSI "font_ansi.bin" #define FONT_ANSI "font_ansi.bin"
#define FONT_SJIS "font_sjis.bin" #define FONT_SJIS "font_sjis.bin"
#define DSP_IROM "dsp_rom.bin" #define DSP_IROM "dsp_rom.bin"
#define DSP_COEF "dsp_coef.bin" #define DSP_COEF "dsp_coef.bin"
#define GC_IPL "IPL.bin" #define GC_IPL "IPL.bin"
#define GC_SRAM "SRAM.raw" #define GC_SRAM "SRAM.raw"
#define GC_MEMCARDA "MemoryCardA" #define GC_MEMCARDA "MemoryCardA"
#define GC_MEMCARDB "MemoryCardB" #define GC_MEMCARDB "MemoryCardB"
#define WII_STATE "state.dat" #define WII_STATE "state.dat"
#define WII_SETTING "setting.txt" #define WII_SETTING "setting.txt"

View File

@ -2,7 +2,6 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
// This header contains type definitions that are shared between the Dolphin core and // This header contains type definitions that are shared between the Dolphin core and
// other parts of the code. Any definitions that are only used by the core should be // other parts of the code. Any definitions that are only used by the core should be
// placed in "Common.h" instead. // placed in "Common.h" instead.
@ -19,12 +18,12 @@
#define LONG int #define LONG int
#endif #endif
typedef uint8_t u8; typedef uint8_t u8;
typedef uint16_t u16; typedef uint16_t u16;
typedef uint32_t u32; typedef uint32_t u32;
typedef uint64_t u64; typedef uint64_t u64;
typedef int8_t s8; typedef int8_t s8;
typedef int16_t s16; typedef int16_t s16;
typedef int32_t s32; typedef int32_t s32;
typedef int64_t s64; typedef int64_t s64;

View File

@ -10,103 +10,108 @@
static void bn_zero(u8* d, u32 n) static void bn_zero(u8* d, u32 n)
{ {
memset(d, 0, n); memset(d, 0, n);
} }
static void bn_copy(u8* d, const u8* a, u32 n) static void bn_copy(u8* d, const u8* a, u32 n)
{ {
memcpy(d, a, n); memcpy(d, a, n);
} }
int bn_compare(const u8* a, const u8* b, u32 n) int bn_compare(const u8* a, const u8* b, u32 n)
{ {
u32 i; u32 i;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++)
if (a[i] < b[i]) {
return -1; if (a[i] < b[i])
if (a[i] > b[i]) return -1;
return 1; if (a[i] > b[i])
} return 1;
}
return 0; return 0;
} }
void bn_sub_modulus(u8* a, const u8* N, u32 n) void bn_sub_modulus(u8* a, const u8* N, u32 n)
{ {
u32 i; u32 i;
u32 dig; u32 dig;
u8 c; u8 c;
c = 0; c = 0;
for (i = n - 1; i < n; i--) { for (i = n - 1; i < n; i--)
dig = N[i] + c; {
c = (a[i] < dig); dig = N[i] + c;
a[i] -= dig; c = (a[i] < dig);
} a[i] -= dig;
}
} }
void bn_add(u8* d, const u8* a, const u8* b, const u8* N, u32 n) void bn_add(u8* d, const u8* a, const u8* b, const u8* N, u32 n)
{ {
u32 i; u32 i;
u32 dig; u32 dig;
u8 c; u8 c;
c = 0; c = 0;
for (i = n - 1; i < n; i--) { for (i = n - 1; i < n; i--)
dig = a[i] + b[i] + c; {
c = (dig >= 0x100); dig = a[i] + b[i] + c;
d[i] = dig; c = (dig >= 0x100);
} d[i] = dig;
}
if (c) if (c)
bn_sub_modulus(d, N, n); bn_sub_modulus(d, N, n);
if (bn_compare(d, N, n) >= 0) if (bn_compare(d, N, n) >= 0)
bn_sub_modulus(d, N, n); bn_sub_modulus(d, N, n);
} }
void bn_mul(u8* d, const u8* a, const u8* b, const u8* N, u32 n) void bn_mul(u8* d, const u8* a, const u8* b, const u8* N, u32 n)
{ {
u32 i; u32 i;
u8 mask; u8 mask;
bn_zero(d, n); bn_zero(d, n);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
for (mask = 0x80; mask != 0; mask >>= 1) { for (mask = 0x80; mask != 0; mask >>= 1)
bn_add(d, d, d, N, n); {
if ((a[i] & mask) != 0) bn_add(d, d, d, N, n);
bn_add(d, d, b, N, n); if ((a[i] & mask) != 0)
} bn_add(d, d, b, N, n);
}
} }
void bn_exp(u8* d, const u8* a, const u8* N, u32 n, const u8* e, u32 en) void bn_exp(u8* d, const u8* a, const u8* N, u32 n, const u8* e, u32 en)
{ {
u8 t[512]; u8 t[512];
u32 i; u32 i;
u8 mask; u8 mask;
bn_zero(d, n); bn_zero(d, n);
d[n-1] = 1; d[n - 1] = 1;
for (i = 0; i < en; i++) for (i = 0; i < en; i++)
for (mask = 0x80; mask != 0; mask >>= 1) { for (mask = 0x80; mask != 0; mask >>= 1)
bn_mul(t, d, d, N, n); {
if ((e[i] & mask) != 0) bn_mul(t, d, d, N, n);
bn_mul(d, t, a, N, n); if ((e[i] & mask) != 0)
else bn_mul(d, t, a, N, n);
bn_copy(d, t, n); else
} bn_copy(d, t, n);
}
} }
// only for prime N -- stupid but lazy, see if I care // only for prime N -- stupid but lazy, see if I care
void bn_inv(u8* d, const u8* a, const u8* N, u32 n) void bn_inv(u8* d, const u8* a, const u8* N, u32 n)
{ {
u8 t[512], s[512]; u8 t[512], s[512];
bn_copy(t, N, n); bn_copy(t, N, n);
bn_zero(s, n); bn_zero(s, n);
s[n-1] = 2; s[n - 1] = 2;
bn_sub_modulus(t, s, n); bn_sub_modulus(t, s, n);
bn_exp(d, a, N, n, t, n); bn_exp(d, a, N, n, t, n);
} }

View File

@ -12,5 +12,5 @@ int bn_compare(const u8* a, const u8* b, u32 n);
void bn_sub_modulus(u8* a, const u8* N, u32 n); void bn_sub_modulus(u8* a, const u8* N, u32 n);
void bn_add(u8* d, const u8* a, const u8* b, const u8* N, u32 n); void bn_add(u8* d, const u8* a, const u8* b, const u8* N, u32 n);
void bn_mul(u8* d, const u8* a, const u8* b, const u8* N, u32 n); void bn_mul(u8* d, const u8* a, const u8* b, const u8* N, u32 n);
void bn_inv(u8* d, const u8* a, const u8* N, u32 n); // only for prime N void bn_inv(u8* d, const u8* a, const u8* N, u32 n); // only for prime N
void bn_exp(u8* d, const u8* a, const u8* N, u32 n, const u8* e, u32 en); void bn_exp(u8* d, const u8* a, const u8* N, u32 n, const u8* e, u32 en);

View File

@ -16,379 +16,391 @@
#include "Common/Crypto/ec.h" #include "Common/Crypto/ec.h"
// y**2 + x*y = x**3 + x + b // y**2 + x*y = x**3 + x + b
UNUSED static const u8 ec_b[30] = UNUSED static const u8 ec_b[30] = {0x00, 0x66, 0x64, 0x7e, 0xde, 0x6c, 0x33, 0x2c, 0x7f, 0x8c,
{0x00,0x66,0x64,0x7e,0xde,0x6c,0x33,0x2c,0x7f,0x8c,0x09,0x23,0xbb,0x58,0x21 0x09, 0x23, 0xbb, 0x58, 0x21, 0x3b, 0x33, 0x3b, 0x20, 0xe9,
,0x3b,0x33,0x3b,0x20,0xe9,0xce,0x42,0x81,0xfe,0x11,0x5f,0x7d,0x8f,0x90,0xad}; 0xce, 0x42, 0x81, 0xfe, 0x11, 0x5f, 0x7d, 0x8f, 0x90, 0xad};
// order of the addition group of points // order of the addition group of points
static const u8 ec_N[30] = static const u8 ec_N[30] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xe9, 0x74, 0xe7, 0x2f,
,0x13,0xe9,0x74,0xe7,0x2f,0x8a,0x69,0x22,0x03,0x1d,0x26,0x03,0xcf,0xe0,0xd7}; 0x8a, 0x69, 0x22, 0x03, 0x1d, 0x26, 0x03, 0xcf, 0xe0, 0xd7};
// base point // base point
static const u8 ec_G[60] = static const u8 ec_G[60] = {0x00, 0xfa, 0xc9, 0xdf, 0xcb, 0xac, 0x83, 0x13, 0xbb, 0x21, 0x39, 0xf1,
{0x00,0xfa,0xc9,0xdf,0xcb,0xac,0x83,0x13,0xbb,0x21,0x39,0xf1,0xbb,0x75,0x5f 0xbb, 0x75, 0x5f, 0xef, 0x65, 0xbc, 0x39, 0x1f, 0x8b, 0x36, 0xf8, 0xf8,
,0xef,0x65,0xbc,0x39,0x1f,0x8b,0x36,0xf8,0xf8,0xeb,0x73,0x71,0xfd,0x55,0x8b 0xeb, 0x73, 0x71, 0xfd, 0x55, 0x8b, 0x01, 0x00, 0x6a, 0x08, 0xa4, 0x19,
,0x01,0x00,0x6a,0x08,0xa4,0x19,0x03,0x35,0x06,0x78,0xe5,0x85,0x28,0xbe,0xbf 0x03, 0x35, 0x06, 0x78, 0xe5, 0x85, 0x28, 0xbe, 0xbf, 0x8a, 0x0b, 0xef,
,0x8a,0x0b,0xef,0xf8,0x67,0xa7,0xca,0x36,0x71,0x6f,0x7e,0x01,0xf8,0x10,0x52}; 0xf8, 0x67, 0xa7, 0xca, 0x36, 0x71, 0x6f, 0x7e, 0x01, 0xf8, 0x10, 0x52};
static void elt_copy(u8* d, const u8* a) static void elt_copy(u8* d, const u8* a)
{ {
memcpy(d, a, 30); memcpy(d, a, 30);
} }
static void elt_zero(u8* d) static void elt_zero(u8* d)
{ {
memset(d, 0, 30); memset(d, 0, 30);
} }
static int elt_is_zero(const u8* d) static int elt_is_zero(const u8* d)
{ {
u32 i; u32 i;
for (i = 0; i < 30; i++) for (i = 0; i < 30; i++)
if (d[i] != 0) if (d[i] != 0)
return 0; return 0;
return 1; return 1;
} }
static void elt_add(u8* d, const u8* a, const u8* b) static void elt_add(u8* d, const u8* a, const u8* b)
{ {
u32 i; u32 i;
for (i = 0; i < 30; i++) for (i = 0; i < 30; i++)
d[i] = a[i] ^ b[i]; d[i] = a[i] ^ b[i];
} }
static void elt_mul_x(u8* d, const u8* a) static void elt_mul_x(u8* d, const u8* a)
{ {
u8 carry, x, y; u8 carry, x, y;
u32 i; u32 i;
carry = a[0] & 1; carry = a[0] & 1;
x = 0; x = 0;
for (i = 0; i < 29; i++) { for (i = 0; i < 29; i++)
y = a[i + 1]; {
d[i] = x ^ (y >> 7); y = a[i + 1];
x = y << 1; d[i] = x ^ (y >> 7);
} x = y << 1;
d[29] = x ^ carry; }
d[29] = x ^ carry;
d[20] ^= carry << 2; d[20] ^= carry << 2;
} }
static void elt_mul(u8* d, const u8* a, const u8* b) static void elt_mul(u8* d, const u8* a, const u8* b)
{ {
u32 i, n; u32 i, n;
u8 mask; u8 mask;
elt_zero(d); elt_zero(d);
i = 0; i = 0;
mask = 1; mask = 1;
for (n = 0; n < 233; n++) { for (n = 0; n < 233; n++)
elt_mul_x(d, d); {
elt_mul_x(d, d);
if ((a[i] & mask) != 0) if ((a[i] & mask) != 0)
elt_add(d, d, b); elt_add(d, d, b);
mask >>= 1; mask >>= 1;
if (mask == 0) { if (mask == 0)
mask = 0x80; {
i++; mask = 0x80;
} i++;
} }
}
} }
static const u8 square[16] = static const u8 square[16] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15,
{0x00,0x01,0x04,0x05,0x10,0x11,0x14,0x15,0x40,0x41,0x44,0x45,0x50,0x51,0x54,0x55}; 0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55};
static void elt_square_to_wide(u8* d, const u8* a) static void elt_square_to_wide(u8* d, const u8* a)
{ {
u32 i; u32 i;
for (i = 0; i < 30; i++) { for (i = 0; i < 30; i++)
d[2*i] = square[a[i] >> 4]; {
d[2*i + 1] = square[a[i] & 15]; d[2 * i] = square[a[i] >> 4];
} d[2 * i + 1] = square[a[i] & 15];
}
} }
static void wide_reduce(u8* d) static void wide_reduce(u8* d)
{ {
u32 i; u32 i;
u8 x; u8 x;
for (i = 0; i < 30; i++) { for (i = 0; i < 30; i++)
x = d[i]; {
x = d[i];
d[i + 19] ^= x >> 7; d[i + 19] ^= x >> 7;
d[i + 20] ^= x << 1; d[i + 20] ^= x << 1;
d[i + 29] ^= x >> 1; d[i + 29] ^= x >> 1;
d[i + 30] ^= x << 7; d[i + 30] ^= x << 7;
} }
x = d[30] & ~1; x = d[30] & ~1;
d[49] ^= x >> 7; d[49] ^= x >> 7;
d[50] ^= x << 1; d[50] ^= x << 1;
d[59] ^= x >> 1; d[59] ^= x >> 1;
d[30] &= 1; d[30] &= 1;
} }
static void elt_square(u8* d, const u8* a) static void elt_square(u8* d, const u8* a)
{ {
u8 wide[60]; u8 wide[60];
elt_square_to_wide(wide, a); elt_square_to_wide(wide, a);
wide_reduce(wide); wide_reduce(wide);
elt_copy(d, wide + 30); elt_copy(d, wide + 30);
} }
static void itoh_tsujii(u8* d, const u8* a, const u8* b, u32 j) static void itoh_tsujii(u8* d, const u8* a, const u8* b, u32 j)
{ {
u8 t[30]; u8 t[30];
elt_copy(t, a); elt_copy(t, a);
while (j--) { while (j--)
elt_square(d, t); {
elt_copy(t, d); elt_square(d, t);
} elt_copy(t, d);
}
elt_mul(d, t, b); elt_mul(d, t, b);
} }
static void elt_inv(u8* d, const u8* a) static void elt_inv(u8* d, const u8* a)
{ {
u8 t[30]; u8 t[30];
u8 s[30]; u8 s[30];
itoh_tsujii(t, a, a, 1); itoh_tsujii(t, a, a, 1);
itoh_tsujii(s, t, a, 1); itoh_tsujii(s, t, a, 1);
itoh_tsujii(t, s, s, 3); itoh_tsujii(t, s, s, 3);
itoh_tsujii(s, t, a, 1); itoh_tsujii(s, t, a, 1);
itoh_tsujii(t, s, s, 7); itoh_tsujii(t, s, s, 7);
itoh_tsujii(s, t, t, 14); itoh_tsujii(s, t, t, 14);
itoh_tsujii(t, s, a, 1); itoh_tsujii(t, s, a, 1);
itoh_tsujii(s, t, t, 29); itoh_tsujii(s, t, t, 29);
itoh_tsujii(t, s, s, 58); itoh_tsujii(t, s, s, 58);
itoh_tsujii(s, t, t, 116); itoh_tsujii(s, t, t, 116);
elt_square(d, s); elt_square(d, s);
} }
UNUSED static int point_is_on_curve(u8* p) UNUSED static int point_is_on_curve(u8* p)
{ {
u8 s[30], t[30]; u8 s[30], t[30];
u8* x, *y; u8 *x, *y;
x = p; x = p;
y = p + 30; y = p + 30;
elt_square(t, x); elt_square(t, x);
elt_mul(s, t, x); elt_mul(s, t, x);
elt_add(s, s, t); elt_add(s, s, t);
elt_square(t, y); elt_square(t, y);
elt_add(s, s, t); elt_add(s, s, t);
elt_mul(t, x, y); elt_mul(t, x, y);
elt_add(s, s, t); elt_add(s, s, t);
elt_add(s, s, ec_b); elt_add(s, s, ec_b);
return elt_is_zero(s); return elt_is_zero(s);
} }
static int point_is_zero(const u8* p) static int point_is_zero(const u8* p)
{ {
return elt_is_zero(p) && elt_is_zero(p + 30); return elt_is_zero(p) && elt_is_zero(p + 30);
} }
static void point_double(u8* r, const u8* p) static void point_double(u8* r, const u8* p)
{ {
u8 s[30], t[30]; u8 s[30], t[30];
const u8* px, *py; const u8 *px, *py;
u8* rx, *ry; u8 *rx, *ry;
px = p; px = p;
py = p + 30; py = p + 30;
rx = r; rx = r;
ry = r + 30; ry = r + 30;
if (elt_is_zero(px)) { if (elt_is_zero(px))
elt_zero(rx); {
elt_zero(ry); elt_zero(rx);
elt_zero(ry);
return; return;
} }
elt_inv(t, px); elt_inv(t, px);
elt_mul(s, py, t); elt_mul(s, py, t);
elt_add(s, s, px); elt_add(s, s, px);
elt_square(t, px); elt_square(t, px);
elt_square(rx, s); elt_square(rx, s);
elt_add(rx, rx, s); elt_add(rx, rx, s);
rx[29] ^= 1; rx[29] ^= 1;
elt_mul(ry, s, rx); elt_mul(ry, s, rx);
elt_add(ry, ry, rx); elt_add(ry, ry, rx);
elt_add(ry, ry, t); elt_add(ry, ry, t);
} }
static void point_add(u8* r, const u8* p, const u8* q) static void point_add(u8* r, const u8* p, const u8* q)
{ {
u8 s[30], t[30], u[30]; u8 s[30], t[30], u[30];
const u8* px, *py, *qx, *qy; const u8 *px, *py, *qx, *qy;
u8* rx, *ry; u8 *rx, *ry;
px = p; px = p;
py = p + 30; py = p + 30;
qx = q; qx = q;
qy = q + 30; qy = q + 30;
rx = r; rx = r;
ry = r + 30; ry = r + 30;
if (point_is_zero(p)) { if (point_is_zero(p))
elt_copy(rx, qx); {
elt_copy(ry, qy); elt_copy(rx, qx);
return; elt_copy(ry, qy);
} return;
}
if (point_is_zero(q)) { if (point_is_zero(q))
elt_copy(rx, px); {
elt_copy(ry, py); elt_copy(rx, px);
return; elt_copy(ry, py);
} return;
}
elt_add(u, px, qx); elt_add(u, px, qx);
if (elt_is_zero(u)) { if (elt_is_zero(u))
elt_add(u, py, qy); {
if (elt_is_zero(u)) elt_add(u, py, qy);
point_double(r, p); if (elt_is_zero(u))
else { point_double(r, p);
elt_zero(rx); else
elt_zero(ry); {
} elt_zero(rx);
elt_zero(ry);
}
return; return;
} }
elt_inv(t, u); elt_inv(t, u);
elt_add(u, py, qy); elt_add(u, py, qy);
elt_mul(s, t, u); elt_mul(s, t, u);
elt_square(t, s); elt_square(t, s);
elt_add(t, t, s); elt_add(t, t, s);
elt_add(t, t, qx); elt_add(t, t, qx);
t[29] ^= 1; t[29] ^= 1;
elt_mul(u, s, t); elt_mul(u, s, t);
elt_add(s, u, py); elt_add(s, u, py);
elt_add(rx, t, px); elt_add(rx, t, px);
elt_add(ry, s, rx); elt_add(ry, s, rx);
} }
static void point_mul(u8* d, const u8* a, const u8* b) // a is bignum static void point_mul(u8* d, const u8* a, const u8* b) // a is bignum
{ {
u32 i; u32 i;
u8 mask; u8 mask;
elt_zero(d); elt_zero(d);
elt_zero(d + 30); elt_zero(d + 30);
for (i = 0; i < 30; i++) for (i = 0; i < 30; i++)
for (mask = 0x80; mask != 0; mask >>= 1) { for (mask = 0x80; mask != 0; mask >>= 1)
point_double(d, d); {
if ((a[i] & mask) != 0) point_double(d, d);
point_add(d, d, b); if ((a[i] & mask) != 0)
} point_add(d, d, b);
}
} }
static void silly_random(u8 * rndArea, u8 count) static void silly_random(u8* rndArea, u8 count)
{ {
u16 i; u16 i;
srand((unsigned) (time(nullptr))); srand((unsigned)(time(nullptr)));
for (i=0;i<count;i++) for (i = 0; i < count; i++)
{ {
rndArea[i]=rand(); rndArea[i] = rand();
} }
} }
void generate_ecdsa(u8* R, u8* S, const u8* k, const u8* hash) void generate_ecdsa(u8* R, u8* S, const u8* k, const u8* hash)
{ {
u8 e[30]; u8 e[30];
u8 kk[30]; u8 kk[30];
u8 m[30]; u8 m[30];
u8 minv[30]; u8 minv[30];
u8 mG[60]; u8 mG[60];
//FILE *fp; // FILE *fp;
elt_zero(e); elt_zero(e);
memcpy(e + 10, hash, 20); memcpy(e + 10, hash, 20);
//Changing random number generator to a lame one... // Changing random number generator to a lame one...
silly_random(m, sizeof(m)); silly_random(m, sizeof(m));
//fp = fopen("/dev/random", "rb"); // fp = fopen("/dev/random", "rb");
//if (fread(m, sizeof m, 1, fp) != 1) // if (fread(m, sizeof m, 1, fp) != 1)
// fatal("reading random"); // fatal("reading random");
//fclose(fp); // fclose(fp);
m[0] = 0; m[0] = 0;
// R = (mG).x // R = (mG).x
point_mul(mG, m, ec_G); point_mul(mG, m, ec_G);
elt_copy(R, mG); elt_copy(R, mG);
if (bn_compare(R, ec_N, 30) >= 0) if (bn_compare(R, ec_N, 30) >= 0)
bn_sub_modulus(R, ec_N, 30); bn_sub_modulus(R, ec_N, 30);
// S = m**-1*(e + Rk) (mod N) // S = m**-1*(e + Rk) (mod N)
elt_copy(kk, k); elt_copy(kk, k);
if (bn_compare(kk, ec_N, 30) >= 0) if (bn_compare(kk, ec_N, 30) >= 0)
bn_sub_modulus(kk, ec_N, 30); bn_sub_modulus(kk, ec_N, 30);
bn_mul(S, R, kk, ec_N, 30); bn_mul(S, R, kk, ec_N, 30);
bn_add(kk, S, e, ec_N, 30); bn_add(kk, S, e, ec_N, 30);
bn_inv(minv, m, ec_N, 30); bn_inv(minv, m, ec_N, 30);
bn_mul(S, minv, kk, ec_N, 30); bn_mul(S, minv, kk, ec_N, 30);
} }
UNUSED static int check_ecdsa(u8* Q, u8* R, u8* S, const u8* hash) UNUSED static int check_ecdsa(u8* Q, u8* R, u8* S, const u8* hash)
{ {
u8 Sinv[30]; u8 Sinv[30];
u8 e[30]; u8 e[30];
u8 w1[30], w2[30]; u8 w1[30], w2[30];
u8 r1[60], r2[60]; u8 r1[60], r2[60];
bn_inv(Sinv, S, ec_N, 30); bn_inv(Sinv, S, ec_N, 30);
elt_zero(e); elt_zero(e);
memcpy(e + 10, hash, 20); memcpy(e + 10, hash, 20);
bn_mul(w1, e, Sinv, ec_N, 30); bn_mul(w1, e, Sinv, ec_N, 30);
bn_mul(w2, R, Sinv, ec_N, 30); bn_mul(w2, R, Sinv, ec_N, 30);
point_mul(r1, w1, ec_G); point_mul(r1, w1, ec_G);
point_mul(r2, w2, Q); point_mul(r2, w2, Q);
point_add(r1, r1, r2); point_add(r1, r1, r2);
if (bn_compare(r1, ec_N, 30) >= 0) if (bn_compare(r1, ec_N, 30) >= 0)
bn_sub_modulus(r1, ec_N, 30); bn_sub_modulus(r1, ec_N, 30);
return (bn_compare(r1, R, 30) == 0); return (bn_compare(r1, R, 30) == 0);
} }
void ec_priv_to_pub(const u8* k, u8* Q) void ec_priv_to_pub(const u8* k, u8* Q)
{ {
point_mul(Q, k, ec_G); point_mul(Q, k, ec_G);
} }

View File

@ -10,31 +10,34 @@
class DebugInterface class DebugInterface
{ {
protected: protected:
virtual ~DebugInterface() {} virtual ~DebugInterface() {}
public: public:
virtual std::string Disassemble(unsigned int /*address*/) { return "NODEBUGGER"; } virtual std::string Disassemble(unsigned int /*address*/) { return "NODEBUGGER"; }
virtual void GetRawMemoryString(int /*memory*/, unsigned int /*address*/, char* dest, int /*max_size*/) {strcpy(dest, "NODEBUGGER");} virtual void GetRawMemoryString(int /*memory*/, unsigned int /*address*/, char* dest,
virtual int GetInstructionSize(int /*instruction*/) {return 1;} int /*max_size*/)
virtual bool IsAlive() {return true;} {
virtual bool IsBreakpoint(unsigned int /*address*/) {return false;} strcpy(dest, "NODEBUGGER");
virtual void SetBreakpoint(unsigned int /*address*/){} }
virtual void ClearBreakpoint(unsigned int /*address*/){} virtual int GetInstructionSize(int /*instruction*/) { return 1; }
virtual void ClearAllBreakpoints() {} virtual bool IsAlive() { return true; }
virtual void ToggleBreakpoint(unsigned int /*address*/){} virtual bool IsBreakpoint(unsigned int /*address*/) { return false; }
virtual void AddWatch(unsigned int /*address*/){} virtual void SetBreakpoint(unsigned int /*address*/) {}
virtual void ClearAllMemChecks() {} virtual void ClearBreakpoint(unsigned int /*address*/) {}
virtual bool IsMemCheck(unsigned int /*address*/) {return false;} virtual void ClearAllBreakpoints() {}
virtual void ToggleMemCheck(unsigned int /*address*/){} virtual void ToggleBreakpoint(unsigned int /*address*/) {}
virtual unsigned int ReadMemory(unsigned int /*address*/){return 0;} virtual void AddWatch(unsigned int /*address*/) {}
virtual void WriteExtraMemory(int /*memory*/, unsigned int /*value*/, unsigned int /*address*/) {} virtual void ClearAllMemChecks() {}
virtual unsigned int ReadExtraMemory(int /*memory*/, unsigned int /*address*/){return 0;} virtual bool IsMemCheck(unsigned int /*address*/) { return false; }
virtual unsigned int ReadInstruction(unsigned int /*address*/){return 0;} virtual void ToggleMemCheck(unsigned int /*address*/) {}
virtual unsigned int GetPC() {return 0;} virtual unsigned int ReadMemory(unsigned int /*address*/) { return 0; }
virtual void SetPC(unsigned int /*address*/) {} virtual void WriteExtraMemory(int /*memory*/, unsigned int /*value*/, unsigned int /*address*/) {}
virtual void Step() {} virtual unsigned int ReadExtraMemory(int /*memory*/, unsigned int /*address*/) { return 0; }
virtual void RunToBreakpoint() {} virtual unsigned int ReadInstruction(unsigned int /*address*/) { return 0; }
virtual void InsertBLR(unsigned int /*address*/, unsigned int /*value*/) {} virtual unsigned int GetPC() { return 0; }
virtual int GetColor(unsigned int /*address*/){return 0xFFFFFFFF;} virtual void SetPC(unsigned int /*address*/) {}
virtual std::string GetDescription(unsigned int /*address*/) = 0; virtual void Step() {}
virtual void RunToBreakpoint() {}
virtual void InsertBLR(unsigned int /*address*/, unsigned int /*value*/) {}
virtual int GetColor(unsigned int /*address*/) { return 0xFFFFFFFF; }
virtual std::string GetDescription(unsigned int /*address*/) = 0;
}; };

View File

@ -8,35 +8,32 @@
namespace ENetUtil namespace ENetUtil
{ {
void WakeupThread(ENetHost* host) void WakeupThread(ENetHost* host)
{ {
// Send ourselves a spurious message. This is hackier than it should be. // Send ourselves a spurious message. This is hackier than it should be.
// comex reported this as https://github.com/lsalzman/enet/issues/23, so // comex reported this as https://github.com/lsalzman/enet/issues/23, so
// hopefully there will be a better way to do it in the future. // hopefully there will be a better way to do it in the future.
ENetAddress address; ENetAddress address;
if (host->address.port != 0) if (host->address.port != 0)
address.port = host->address.port; address.port = host->address.port;
else else
enet_socket_get_address(host->socket, &address); enet_socket_get_address(host->socket, &address);
address.host = 0x0100007f; // localhost address.host = 0x0100007f; // localhost
u8 byte = 0; u8 byte = 0;
ENetBuffer buf; ENetBuffer buf;
buf.data = &byte; buf.data = &byte;
buf.dataLength = 1; buf.dataLength = 1;
enet_socket_send(host->socket, &address, &buf, 1); enet_socket_send(host->socket, &address, &buf, 1);
} }
int ENET_CALLBACK InterceptCallback(ENetHost* host, ENetEvent* event) int ENET_CALLBACK InterceptCallback(ENetHost* host, ENetEvent* event)
{ {
// wakeup packet received // wakeup packet received
if (host->receivedDataLength == 1 && host->receivedData[0] == 0) if (host->receivedDataLength == 1 && host->receivedData[0] == 0)
{ {
event->type = (ENetEventType) 42; event->type = (ENetEventType)42;
return 1; return 1;
} }
return 0; return 0;
} }
} }

View File

@ -8,8 +8,6 @@
namespace ENetUtil namespace ENetUtil
{ {
void WakeupThread(ENetHost* host); void WakeupThread(ENetHost* host);
int ENET_CALLBACK InterceptCallback(ENetHost* host, ENetEvent* event); int ENET_CALLBACK InterceptCallback(ENetHost* host, ENetEvent* event);
} }

View File

@ -22,54 +22,53 @@
#include "Common/Flag.h" #include "Common/Flag.h"
namespace Common { namespace Common
{
class Event final class Event final
{ {
public: public:
void Set() void Set()
{ {
if (m_flag.TestAndSet()) if (m_flag.TestAndSet())
{ {
std::lock_guard<std::mutex> lk(m_mutex); std::lock_guard<std::mutex> lk(m_mutex);
m_condvar.notify_one(); m_condvar.notify_one();
} }
} }
void Wait() void Wait()
{ {
if (m_flag.TestAndClear()) if (m_flag.TestAndClear())
return; return;
std::unique_lock<std::mutex> lk(m_mutex); std::unique_lock<std::mutex> lk(m_mutex);
m_condvar.wait(lk, [&]{ return m_flag.TestAndClear(); }); m_condvar.wait(lk, [&] { return m_flag.TestAndClear(); });
} }
template<class Rep, class Period> template <class Rep, class Period>
bool WaitFor(const std::chrono::duration<Rep, Period>& rel_time) bool WaitFor(const std::chrono::duration<Rep, Period>& rel_time)
{ {
if (m_flag.TestAndClear()) if (m_flag.TestAndClear())
return true; return true;
std::unique_lock<std::mutex> lk(m_mutex); std::unique_lock<std::mutex> lk(m_mutex);
bool signaled = m_condvar.wait_for(lk, rel_time, bool signaled = m_condvar.wait_for(lk, rel_time, [&] { return m_flag.TestAndClear(); });
[&]{ return m_flag.TestAndClear(); });
return signaled; return signaled;
} }
void Reset() void Reset()
{ {
// no other action required, since wait loops on // no other action required, since wait loops on
// the predicate and any lingering signal will get // the predicate and any lingering signal will get
// cleared on the first iteration // cleared on the first iteration
m_flag.Clear(); m_flag.Clear();
} }
private: private:
Flag m_flag; Flag m_flag;
std::condition_variable m_condvar; std::condition_variable m_condvar;
std::mutex m_mutex; std::mutex m_mutex;
}; };
} // namespace Common } // namespace Common

View File

@ -8,29 +8,30 @@
namespace FPURoundMode namespace FPURoundMode
{ {
// TODO: MSVC currently produces broken code: // TODO: MSVC currently produces broken code:
// https://connect.microsoft.com/VisualStudio/feedback/details/828892/vc-2013-miscompilation-with-enums-and-bit-fields // https://connect.microsoft.com/VisualStudio/feedback/details/828892/vc-2013-miscompilation-with-enums-and-bit-fields
// Once that is fixed, change types in SetRoundMode(), SetSIMDMode(), and in UReg_FPSCR to 'RoundMode'. // Once that is fixed, change types in SetRoundMode(), SetSIMDMode(), and in UReg_FPSCR to
// 'RoundMode'.
enum RoundMode enum RoundMode
{ {
ROUND_NEAR = 0, ROUND_NEAR = 0,
ROUND_CHOP = 1, ROUND_CHOP = 1,
ROUND_UP = 2, ROUND_UP = 2,
ROUND_DOWN = 3 ROUND_DOWN = 3
}; };
enum PrecisionMode enum PrecisionMode
{ {
PREC_24 = 0, PREC_24 = 0,
PREC_53 = 1, PREC_53 = 1,
PREC_64 = 2 PREC_64 = 2
}; };
void SetRoundMode(int mode); void SetRoundMode(int mode);
void SetPrecisionMode(PrecisionMode mode); void SetPrecisionMode(PrecisionMode mode);
void SetSIMDMode(int rounding_mode, bool non_ieee_mode); void SetSIMDMode(int rounding_mode, bool non_ieee_mode);
/* /*
* There are two different flavors of float to int conversion: * There are two different flavors of float to int conversion:
@ -39,7 +40,7 @@ namespace FPURoundMode
* The first rounds according to the MXCSR rounding bits. * The first rounds according to the MXCSR rounding bits.
* The second one always uses round towards zero. * The second one always uses round towards zero.
*/ */
void SaveSIMDState(); void SaveSIMDState();
void LoadSIMDState(); void LoadSIMDState();
void LoadDefaultSIMDState(); void LoadDefaultSIMDState();
} }

View File

@ -15,111 +15,96 @@
namespace Common namespace Common
{ {
template <typename T, bool NeedSize = true> template <typename T, bool NeedSize = true>
class FifoQueue class FifoQueue
{ {
public: public:
FifoQueue() : m_size(0) FifoQueue() : m_size(0) { m_write_ptr = m_read_ptr = new ElementPtr(); }
{ ~FifoQueue()
m_write_ptr = m_read_ptr = new ElementPtr(); {
} // this will empty out the whole queue
delete m_read_ptr;
}
~FifoQueue() u32 Size() const
{ {
// this will empty out the whole queue static_assert(NeedSize, "using Size() on FifoQueue without NeedSize");
delete m_read_ptr; return m_size.load();
} }
u32 Size() const bool Empty() const { return !m_read_ptr->next.load(); }
{ T& Front() const { return m_read_ptr->current; }
static_assert(NeedSize, "using Size() on FifoQueue without NeedSize"); template <typename Arg>
return m_size.load(); void Push(Arg&& t)
} {
// create the element, add it to the queue
m_write_ptr->current = std::forward<Arg>(t);
// set the next pointer to a new element ptr
// then advance the write pointer
ElementPtr* new_ptr = new ElementPtr();
m_write_ptr->next.store(new_ptr, std::memory_order_release);
m_write_ptr = new_ptr;
if (NeedSize)
m_size++;
}
bool Empty() const void Pop()
{ {
return !m_read_ptr->next.load(); if (NeedSize)
} m_size--;
ElementPtr* tmpptr = m_read_ptr;
// advance the read pointer
m_read_ptr = tmpptr->next.load();
// set the next element to nullptr to stop the recursive deletion
tmpptr->next.store(nullptr);
delete tmpptr; // this also deletes the element
}
T& Front() const bool Pop(T& t)
{ {
return m_read_ptr->current; if (Empty())
} return false;
template <typename Arg> if (NeedSize)
void Push(Arg&& t) m_size--;
{
// create the element, add it to the queue
m_write_ptr->current = std::forward<Arg>(t);
// set the next pointer to a new element ptr
// then advance the write pointer
ElementPtr* new_ptr = new ElementPtr();
m_write_ptr->next.store(new_ptr, std::memory_order_release);
m_write_ptr = new_ptr;
if (NeedSize)
m_size++;
}
void Pop() ElementPtr* tmpptr = m_read_ptr;
{ m_read_ptr = tmpptr->next.load(std::memory_order_acquire);
if (NeedSize) t = std::move(tmpptr->current);
m_size--; tmpptr->next.store(nullptr);
ElementPtr* tmpptr = m_read_ptr; delete tmpptr;
// advance the read pointer return true;
m_read_ptr = tmpptr->next.load(); }
// set the next element to nullptr to stop the recursive deletion
tmpptr->next.store(nullptr);
delete tmpptr; // this also deletes the element
}
bool Pop(T& t) // not thread-safe
{ void Clear()
if (Empty()) {
return false; m_size.store(0);
delete m_read_ptr;
if (NeedSize) m_write_ptr = m_read_ptr = new ElementPtr();
m_size--; }
ElementPtr* tmpptr = m_read_ptr;
m_read_ptr = tmpptr->next.load(std::memory_order_acquire);
t = std::move(tmpptr->current);
tmpptr->next.store(nullptr);
delete tmpptr;
return true;
}
// not thread-safe
void Clear()
{
m_size.store(0);
delete m_read_ptr;
m_write_ptr = m_read_ptr = new ElementPtr();
}
private: private:
// stores a pointer to element // stores a pointer to element
// and a pointer to the next ElementPtr // and a pointer to the next ElementPtr
class ElementPtr class ElementPtr
{ {
public: public:
ElementPtr() : next(nullptr) {} ElementPtr() : next(nullptr) {}
~ElementPtr()
{
ElementPtr* next_ptr = next.load();
~ElementPtr() if (next_ptr)
{ delete next_ptr;
ElementPtr* next_ptr = next.load(); }
if (next_ptr) T current;
delete next_ptr; std::atomic<ElementPtr*> next;
} };
T current; ElementPtr* m_write_ptr;
std::atomic<ElementPtr*> next; ElementPtr* m_read_ptr;
}; std::atomic<u32> m_size;
ElementPtr* m_write_ptr;
ElementPtr* m_read_ptr;
std::atomic<u32> m_size;
}; };
} }

View File

@ -9,47 +9,51 @@
#include "Common/FileSearch.h" #include "Common/FileSearch.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
static std::vector<std::string> FileSearchWithTest(const std::vector<std::string>& directories, bool recursive, std::function<bool(const File::FSTEntry &)> callback) static std::vector<std::string>
FileSearchWithTest(const std::vector<std::string>& directories, bool recursive,
std::function<bool(const File::FSTEntry&)> callback)
{ {
std::vector<std::string> result; std::vector<std::string> result;
for (const std::string& directory : directories) for (const std::string& directory : directories)
{ {
File::FSTEntry top = File::ScanDirectoryTree(directory, recursive); File::FSTEntry top = File::ScanDirectoryTree(directory, recursive);
std::function<void(File::FSTEntry&)> DoEntry; std::function<void(File::FSTEntry&)> DoEntry;
DoEntry = [&](File::FSTEntry& entry) { DoEntry = [&](File::FSTEntry& entry) {
if (callback(entry)) if (callback(entry))
result.push_back(entry.physicalName); result.push_back(entry.physicalName);
for (auto& child : entry.children) for (auto& child : entry.children)
DoEntry(child); DoEntry(child);
}; };
for (auto& child : top.children) for (auto& child : top.children)
DoEntry(child); DoEntry(child);
} }
// remove duplicates // remove duplicates
std::sort(result.begin(), result.end()); std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end()); result.erase(std::unique(result.begin(), result.end()), result.end());
return result; return result;
} }
std::vector<std::string> DoFileSearch(const std::vector<std::string>& exts, const std::vector<std::string>& directories, bool recursive) std::vector<std::string> DoFileSearch(const std::vector<std::string>& exts,
const std::vector<std::string>& directories, bool recursive)
{ {
bool accept_all = std::find(exts.begin(), exts.end(), "") != exts.end(); bool accept_all = std::find(exts.begin(), exts.end(), "") != exts.end();
return FileSearchWithTest(directories, recursive, [&](const File::FSTEntry& entry) { return FileSearchWithTest(directories, recursive, [&](const File::FSTEntry& entry) {
if (accept_all) if (accept_all)
return true; return true;
std::string name = entry.virtualName; std::string name = entry.virtualName;
std::transform(name.begin(), name.end(), name.begin(), ::tolower); std::transform(name.begin(), name.end(), name.begin(), ::tolower);
return std::any_of(exts.begin(), exts.end(), [&](const std::string& ext) { return std::any_of(exts.begin(), exts.end(), [&](const std::string& ext) {
return name.length() >= ext.length() && name.compare(name.length() - ext.length(), ext.length(), ext) == 0; return name.length() >= ext.length() &&
}); name.compare(name.length() - ext.length(), ext.length(), ext) == 0;
}); });
});
} }
// Result includes the passed directories themselves as well as their subdirectories. // Result includes the passed directories themselves as well as their subdirectories.
std::vector<std::string> FindSubdirectories(const std::vector<std::string>& directories, bool recursive) std::vector<std::string> FindSubdirectories(const std::vector<std::string>& directories,
bool recursive)
{ {
return FileSearchWithTest(directories, true, [&](const File::FSTEntry& entry) { return FileSearchWithTest(directories, true,
return entry.isDirectory; [&](const File::FSTEntry& entry) { return entry.isDirectory; });
});
} }

View File

@ -7,5 +7,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
std::vector<std::string> DoFileSearch(const std::vector<std::string>& exts, const std::vector<std::string>& directories, bool recursive = false); std::vector<std::string> DoFileSearch(const std::vector<std::string>& exts,
std::vector<std::string> FindSubdirectories(const std::vector<std::string>& directories, bool recursive); const std::vector<std::string>& directories,
bool recursive = false);
std::vector<std::string> FindSubdirectories(const std::vector<std::string>& directories,
bool recursive);

File diff suppressed because it is too large Load Diff

View File

@ -18,55 +18,56 @@
#endif #endif
// User directory indices for GetUserPath // User directory indices for GetUserPath
enum { enum
D_USER_IDX, {
D_GCUSER_IDX, D_USER_IDX,
D_WIIROOT_IDX, // always points to User/Wii or global user-configured directory D_GCUSER_IDX,
D_SESSION_WIIROOT_IDX, // may point to minimal temporary directory for determinism D_WIIROOT_IDX, // always points to User/Wii or global user-configured directory
D_CONFIG_IDX, // global settings D_SESSION_WIIROOT_IDX, // may point to minimal temporary directory for determinism
D_GAMESETTINGS_IDX, // user-specified settings which override both the global and the default settings (per game) D_CONFIG_IDX, // global settings
D_MAPS_IDX, D_GAMESETTINGS_IDX, // user-specified settings which override both the global and the default
D_CACHE_IDX, // settings (per game)
D_SHADERCACHE_IDX, D_MAPS_IDX,
D_SHADERS_IDX, D_CACHE_IDX,
D_STATESAVES_IDX, D_SHADERCACHE_IDX,
D_SCREENSHOTS_IDX, D_SHADERS_IDX,
D_HIRESTEXTURES_IDX, D_STATESAVES_IDX,
D_DUMP_IDX, D_SCREENSHOTS_IDX,
D_DUMPFRAMES_IDX, D_HIRESTEXTURES_IDX,
D_DUMPAUDIO_IDX, D_DUMP_IDX,
D_DUMPTEXTURES_IDX, D_DUMPFRAMES_IDX,
D_DUMPDSP_IDX, D_DUMPAUDIO_IDX,
D_LOAD_IDX, D_DUMPTEXTURES_IDX,
D_LOGS_IDX, D_DUMPDSP_IDX,
D_MAILLOGS_IDX, D_LOAD_IDX,
D_THEMES_IDX, D_LOGS_IDX,
D_PIPES_IDX, D_MAILLOGS_IDX,
D_MEMORYWATCHER_IDX, D_THEMES_IDX,
F_DOLPHINCONFIG_IDX, D_PIPES_IDX,
F_DEBUGGERCONFIG_IDX, D_MEMORYWATCHER_IDX,
F_LOGGERCONFIG_IDX, F_DOLPHINCONFIG_IDX,
F_MAINLOG_IDX, F_DEBUGGERCONFIG_IDX,
F_RAMDUMP_IDX, F_LOGGERCONFIG_IDX,
F_ARAMDUMP_IDX, F_MAINLOG_IDX,
F_FAKEVMEMDUMP_IDX, F_RAMDUMP_IDX,
F_GCSRAM_IDX, F_ARAMDUMP_IDX,
F_MEMORYWATCHERLOCATIONS_IDX, F_FAKEVMEMDUMP_IDX,
F_MEMORYWATCHERSOCKET_IDX, F_GCSRAM_IDX,
NUM_PATH_INDICES F_MEMORYWATCHERLOCATIONS_IDX,
F_MEMORYWATCHERSOCKET_IDX,
NUM_PATH_INDICES
}; };
namespace File namespace File
{ {
// FileSystem tree node/ // FileSystem tree node/
struct FSTEntry struct FSTEntry
{ {
bool isDirectory; bool isDirectory;
u64 size; // File length, or for directories, recursive count of children u64 size; // File length, or for directories, recursive count of children
std::string physicalName; // Name on disk std::string physicalName; // Name on disk
std::string virtualName; // Name in FST names table std::string virtualName; // Name in FST names table
std::vector<FSTEntry> children; std::vector<FSTEntry> children;
}; };
// Returns true if file filename exists // Returns true if file filename exists
@ -159,78 +160,80 @@ bool ReadFileToString(const std::string& filename, std::string& str);
class IOFile : public NonCopyable class IOFile : public NonCopyable
{ {
public: public:
IOFile(); IOFile();
IOFile(std::FILE* file); IOFile(std::FILE* file);
IOFile(const std::string& filename, const char openmode[]); IOFile(const std::string& filename, const char openmode[]);
~IOFile(); ~IOFile();
IOFile(IOFile&& other); IOFile(IOFile&& other);
IOFile& operator=(IOFile&& other); IOFile& operator=(IOFile&& other);
void Swap(IOFile& other); void Swap(IOFile& other);
bool Open(const std::string& filename, const char openmode[]); bool Open(const std::string& filename, const char openmode[]);
bool Close(); bool Close();
template <typename T> template <typename T>
bool ReadArray(T* data, size_t length, size_t* pReadBytes = nullptr) bool ReadArray(T* data, size_t length, size_t* pReadBytes = nullptr)
{ {
size_t read_bytes = 0; size_t read_bytes = 0;
if (!IsOpen() || length != (read_bytes = std::fread(data, sizeof(T), length, m_file))) if (!IsOpen() || length != (read_bytes = std::fread(data, sizeof(T), length, m_file)))
m_good = false; m_good = false;
if (pReadBytes) if (pReadBytes)
*pReadBytes = read_bytes; *pReadBytes = read_bytes;
return m_good; return m_good;
} }
template <typename T> template <typename T>
bool WriteArray(const T* data, size_t length) bool WriteArray(const T* data, size_t length)
{ {
if (!IsOpen() || length != std::fwrite(data, sizeof(T), length, m_file)) if (!IsOpen() || length != std::fwrite(data, sizeof(T), length, m_file))
m_good = false; m_good = false;
return m_good; return m_good;
} }
bool ReadBytes(void* data, size_t length) bool ReadBytes(void* data, size_t length)
{ {
return ReadArray(reinterpret_cast<char*>(data), length); return ReadArray(reinterpret_cast<char*>(data), length);
} }
bool WriteBytes(const void* data, size_t length) bool WriteBytes(const void* data, size_t length)
{ {
return WriteArray(reinterpret_cast<const char*>(data), length); return WriteArray(reinterpret_cast<const char*>(data), length);
} }
bool IsOpen() const { return nullptr != m_file; } bool IsOpen() const { return nullptr != m_file; }
// m_good is set to false when a read, write or other function fails
bool IsGood() const { return m_good; }
operator void*() { return m_good ? m_file : nullptr; }
std::FILE* ReleaseHandle();
// m_good is set to false when a read, write or other function fails std::FILE* GetHandle() { return m_file; }
bool IsGood() const { return m_good; } void SetHandle(std::FILE* file);
operator void*() { return m_good ? m_file : nullptr; }
std::FILE* ReleaseHandle(); bool Seek(s64 off, int origin);
u64 Tell() const;
u64 GetSize();
bool Resize(u64 size);
bool Flush();
std::FILE* GetHandle() { return m_file; } // clear error state
void Clear()
{
m_good = true;
std::clearerr(m_file);
}
void SetHandle(std::FILE* file); std::FILE* m_file;
bool m_good;
bool Seek(s64 off, int origin);
u64 Tell() const;
u64 GetSize();
bool Resize(u64 size);
bool Flush();
// clear error state
void Clear() { m_good = true; std::clearerr(m_file); }
std::FILE* m_file;
bool m_good;
private: private:
IOFile(IOFile&); IOFile(IOFile&);
IOFile& operator=(IOFile& other); IOFile& operator=(IOFile& other);
}; };
} // namespace } // namespace
@ -240,8 +243,8 @@ template <typename T>
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode)
{ {
#ifdef _WIN32 #ifdef _WIN32
fstream.open(UTF8ToTStr(filename).c_str(), openmode); fstream.open(UTF8ToTStr(filename).c_str(), openmode);
#else #else
fstream.open(filename.c_str(), openmode); fstream.open(filename.c_str(), openmode);
#endif #endif
} }

View File

@ -16,62 +16,53 @@
template <class T, int N> template <class T, int N>
class FixedSizeQueue class FixedSizeQueue
{ {
T* storage; T* storage;
int head; int head;
int tail; int tail;
int count; // sacrifice 4 bytes for a simpler implementation. may optimize away in the future. int count; // sacrifice 4 bytes for a simpler implementation. may optimize away in the future.
// Make copy constructor private for now.
FixedSizeQueue(FixedSizeQueue& other) {}
// Make copy constructor private for now.
FixedSizeQueue(FixedSizeQueue& other) {}
public: public:
FixedSizeQueue() FixedSizeQueue()
{ {
storage = new T[N]; storage = new T[N];
clear(); clear();
} }
~FixedSizeQueue() ~FixedSizeQueue() { delete[] storage; }
{ void clear()
delete[] storage; {
} head = 0;
tail = 0;
count = 0;
}
void clear() void push(T t)
{ {
head = 0; storage[tail] = t;
tail = 0; tail++;
count = 0; if (tail == N)
} tail = 0;
count++;
}
void push(T t) void pop()
{ {
storage[tail] = t; head++;
tail++; if (head == N)
if (tail == N) head = 0;
tail = 0; count--;
count++; }
}
void pop() T pop_front()
{ {
head++; const T& temp = storage[head];
if (head == N) pop();
head = 0; return temp;
count--; }
}
T pop_front() T& front() { return storage[head]; }
{ const T& front() const { return storage[head]; }
const T& temp = storage[head]; size_t size() const { return count; }
pop();
return temp;
}
T& front() { return storage[head]; }
const T& front() const { return storage[head]; }
size_t size() const
{
return count;
}
}; };

View File

@ -19,44 +19,27 @@
#include <atomic> #include <atomic>
namespace Common { namespace Common
{
class Flag final class Flag final
{ {
public: public:
// Declared as explicit since we do not want "= true" to work on a flag // Declared as explicit since we do not want "= true" to work on a flag
// object - it should be made explicit that a flag is *not* a normal // object - it should be made explicit that a flag is *not* a normal
// variable. // variable.
explicit Flag(bool initial_value = false) : m_val(initial_value) {} explicit Flag(bool initial_value = false) : m_val(initial_value) {}
void Set(bool val = true) { m_val.store(val); }
void Set(bool val = true) void Clear() { Set(false); }
{ bool IsSet() const { return m_val.load(); }
m_val.store(val); bool TestAndSet(bool val = true)
} {
bool expected = !val;
void Clear() return m_val.compare_exchange_strong(expected, val);
{ }
Set(false);
}
bool IsSet() const
{
return m_val.load();
}
bool TestAndSet(bool val = true)
{
bool expected = !val;
return m_val.compare_exchange_strong(expected, val);
}
bool TestAndClear()
{
return TestAndSet(false);
}
bool TestAndClear() { return TestAndSet(false); }
private: private:
std::atomic_bool m_val; std::atomic_bool m_val;
}; };
} // namespace Common } // namespace Common

View File

@ -24,4 +24,3 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160 #define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160

View File

@ -23,11 +23,14 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP PFNDOLRELEASESHADERCOMPILERPROC) (void); typedef void(APIENTRYP PFNDOLRELEASESHADERCOMPILERPROC)(void);
typedef void (APIENTRYP PFNDOLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); typedef void(APIENTRYP PFNDOLSHADERBINARYPROC)(GLsizei count, const GLuint* shaders,
typedef void (APIENTRYP PFNDOLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); GLenum binaryformat, const void* binary,
typedef void (APIENTRYP PFNDOLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); GLsizei length);
typedef void (APIENTRYP PFNDOLCLEARDEPTHFPROC) (GLfloat d); typedef void(APIENTRYP PFNDOLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype,
GLint* range, GLint* precision);
typedef void(APIENTRYP PFNDOLDEPTHRANGEFPROC)(GLfloat n, GLfloat f);
typedef void(APIENTRYP PFNDOLCLEARDEPTHFPROC)(GLfloat d);
extern PFNDOLCLEARDEPTHFPROC dolClearDepthf; extern PFNDOLCLEARDEPTHFPROC dolClearDepthf;
extern PFNDOLDEPTHRANGEFPROC dolDepthRangef; extern PFNDOLDEPTHRANGEFPROC dolDepthRangef;

View File

@ -28,8 +28,9 @@
#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB #define GL_ONE_MINUS_SRC1_ALPHA 0x88FB
#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC #define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC
typedef void (APIENTRYP PFNDOLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); typedef void(APIENTRYP PFNDOLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber,
typedef GLint (APIENTRYP PFNDOLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); GLuint index, const GLchar* name);
typedef GLint(APIENTRYP PFNDOLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar* name);
extern PFNDOLBINDFRAGDATALOCATIONINDEXEDPROC dolBindFragDataLocationIndexed; extern PFNDOLBINDFRAGDATALOCATIONINDEXEDPROC dolBindFragDataLocationIndexed;
extern PFNDOLGETFRAGDATAINDEXPROC dolGetFragDataIndex; extern PFNDOLGETFRAGDATAINDEXPROC dolGetFragDataIndex;

View File

@ -33,8 +33,10 @@
#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F #define GL_BUFFER_IMMUTABLE_STORAGE 0x821F
#define GL_BUFFER_STORAGE_FLAGS 0x8220 #define GL_BUFFER_STORAGE_FLAGS 0x8220
typedef void (APIENTRYP PFNDOLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); typedef void(APIENTRYP PFNDOLBUFFERSTORAGEPROC)(GLenum target, GLsizeiptr size, const void* data,
typedef void (APIENTRYP PFNDOLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); GLbitfield flags);
typedef void(APIENTRYP PFNDOLNAMEDBUFFERSTORAGEEXTPROC)(GLuint buffer, GLsizeiptr size,
const void* data, GLbitfield flags);
extern PFNDOLBUFFERSTORAGEPROC dolBufferStorage; extern PFNDOLBUFFERSTORAGEPROC dolBufferStorage;

View File

@ -23,12 +23,12 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
#define GL_NEGATIVE_ONE_TO_ONE 0x935E #define GL_NEGATIVE_ONE_TO_ONE 0x935E
#define GL_ZERO_TO_ONE 0x935F #define GL_ZERO_TO_ONE 0x935F
#define GL_CLIP_ORIGIN 0x935C #define GL_CLIP_ORIGIN 0x935C
#define GL_CLIP_DEPTH_MODE 0x935D #define GL_CLIP_DEPTH_MODE 0x935D
typedef void (APIENTRYP PFNDOLCLIPCONTROLPROC) (GLenum origin, GLenum depth); typedef void(APIENTRYP PFNDOLCLIPCONTROLPROC)(GLenum origin, GLenum depth);
extern PFNDOLCLIPCONTROLPROC dolClipControl; extern PFNDOLCLIPCONTROLPROC dolClipControl;

View File

@ -23,7 +23,12 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP PFNDOLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); typedef void(APIENTRYP PFNDOLCOPYIMAGESUBDATAPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel,
GLint srcX, GLint srcY, GLint srcZ,
GLuint dstName, GLenum dstTarget, GLint dstLevel,
GLint dstX, GLint dstY, GLint dstZ,
GLsizei srcWidth, GLsizei srcHeight,
GLsizei srcDepth);
extern PFNDOLCOPYIMAGESUBDATAPROC dolCopyImageSubData; extern PFNDOLCOPYIMAGESUBDATAPROC dolCopyImageSubData;

View File

@ -23,34 +23,44 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); typedef void(APIENTRYP GLDEBUGPROCARB)(GLenum source, GLenum type, GLuint id, GLenum severity,
#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 GLsizei length, const GLchar* message,
const void* userParam);
#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242
#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 #define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243
#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 #define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244
#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 #define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245
#define GL_DEBUG_SOURCE_API_ARB 0x8246 #define GL_DEBUG_SOURCE_API_ARB 0x8246
#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 #define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247
#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 #define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248
#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 #define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249
#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A #define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A
#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B #define GL_DEBUG_SOURCE_OTHER_ARB 0x824B
#define GL_DEBUG_TYPE_ERROR_ARB 0x824C #define GL_DEBUG_TYPE_ERROR_ARB 0x824C
#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D
#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E
#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F #define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F
#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 #define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250
#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 #define GL_DEBUG_TYPE_OTHER_ARB 0x8251
#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 #define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143
#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 #define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144
#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 #define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145
#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 #define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146
#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 #define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147
#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 #define GL_DEBUG_SEVERITY_LOW_ARB 0x9148
typedef void (APIENTRYP PFNDOLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); typedef void(APIENTRYP PFNDOLDEBUGMESSAGECONTROLARBPROC)(GLenum source, GLenum type,
typedef void (APIENTRYP PFNDOLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); GLenum severity, GLsizei count,
typedef void (APIENTRYP PFNDOLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam); const GLuint* ids, GLboolean enabled);
typedef GLuint (APIENTRYP PFNDOLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); typedef void(APIENTRYP PFNDOLDEBUGMESSAGEINSERTARBPROC)(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length,
const GLchar* buf);
typedef void(APIENTRYP PFNDOLDEBUGMESSAGECALLBACKARBPROC)(GLDEBUGPROCARB callback,
const void* userParam);
typedef GLuint(APIENTRYP PFNDOLGETDEBUGMESSAGELOGARBPROC)(GLuint count, GLsizei bufSize,
GLenum* sources, GLenum* types,
GLuint* ids, GLenum* severities,
GLsizei* lengths, GLchar* messageLog);
extern PFNDOLDEBUGMESSAGECALLBACKARBPROC dolDebugMessageCallbackARB; extern PFNDOLDEBUGMESSAGECALLBACKARBPROC dolDebugMessageCallbackARB;
extern PFNDOLDEBUGMESSAGECONTROLARBPROC dolDebugMessageControlARB; extern PFNDOLDEBUGMESSAGECONTROLARBPROC dolDebugMessageControlARB;

View File

@ -23,10 +23,21 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP PFNDOLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); typedef void(APIENTRYP PFNDOLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type,
typedef void (APIENTRYP PFNDOLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); const void* indices, GLint basevertex);
typedef void (APIENTRYP PFNDOLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); typedef void(APIENTRYP PFNDOLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end,
typedef void (APIENTRYP PFNDOLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); GLsizei count, GLenum type,
const void* indices,
GLint basevertex);
typedef void(APIENTRYP PFNDOLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count,
GLenum type, const void* indices,
GLsizei instancecount,
GLint basevertex);
typedef void(APIENTRYP PFNDOLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei* count,
GLenum type,
const void* const* indices,
GLsizei drawcount,
const GLint* basevertex);
extern PFNDOLDRAWELEMENTSBASEVERTEXPROC dolDrawElementsBaseVertex; extern PFNDOLDRAWELEMENTSBASEVERTEXPROC dolDrawElementsBaseVertex;
extern PFNDOLDRAWELEMENTSINSTANCEDBASEVERTEXPROC dolDrawElementsInstancedBaseVertex; extern PFNDOLDRAWELEMENTSINSTANCEDBASEVERTEXPROC dolDrawElementsInstancedBaseVertex;

View File

@ -23,26 +23,44 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP PFNDOLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); typedef void(APIENTRYP PFNDOLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer);
typedef void (APIENTRYP PFNDOLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); typedef void(APIENTRYP PFNDOLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer);
typedef void (APIENTRYP PFNDOLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); typedef void(APIENTRYP PFNDOLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1,
typedef GLenum (APIENTRYP PFNDOLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); GLint srcY1, GLint dstX0, GLint dstY0,
typedef void (APIENTRYP PFNDOLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); GLint dstX1, GLint dstY1, GLbitfield mask,
typedef void (APIENTRYP PFNDOLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); GLenum filter);
typedef void (APIENTRYP PFNDOLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); typedef GLenum(APIENTRYP PFNDOLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target);
typedef void (APIENTRYP PFNDOLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void(APIENTRYP PFNDOLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint* framebuffers);
typedef void (APIENTRYP PFNDOLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void(APIENTRYP PFNDOLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint* renderbuffers);
typedef void (APIENTRYP PFNDOLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); typedef void(APIENTRYP PFNDOLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment,
typedef void (APIENTRYP PFNDOLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); GLenum renderbuffertarget,
typedef void (APIENTRYP PFNDOLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); GLuint renderbuffer);
typedef void (APIENTRYP PFNDOLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); typedef void(APIENTRYP PFNDOLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment,
typedef void (APIENTRYP PFNDOLGENERATEMIPMAPPROC) (GLenum target); GLenum textarget, GLuint texture,
typedef void (APIENTRYP PFNDOLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); GLint level);
typedef void (APIENTRYP PFNDOLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); typedef void(APIENTRYP PFNDOLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment,
typedef GLboolean (APIENTRYP PFNDOLISFRAMEBUFFERPROC) (GLuint framebuffer); GLenum textarget, GLuint texture,
typedef GLboolean (APIENTRYP PFNDOLISRENDERBUFFERPROC) (GLuint renderbuffer); GLint level);
typedef void (APIENTRYP PFNDOLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); typedef void(APIENTRYP PFNDOLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment,
typedef void (APIENTRYP PFNDOLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); GLenum textarget, GLuint texture,
GLint level, GLint zoffset);
typedef void(APIENTRYP PFNDOLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment,
GLuint texture, GLint level, GLint layer);
typedef void(APIENTRYP PFNDOLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint* framebuffers);
typedef void(APIENTRYP PFNDOLGENRENDERBUFFERSPROC)(GLsizei n, GLuint* renderbuffers);
typedef void(APIENTRYP PFNDOLGENERATEMIPMAPPROC)(GLenum target);
typedef void(APIENTRYP PFNDOLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target,
GLenum attachment,
GLenum pname, GLint* params);
typedef void(APIENTRYP PFNDOLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname,
GLint* params);
typedef GLboolean(APIENTRYP PFNDOLISFRAMEBUFFERPROC)(GLuint framebuffer);
typedef GLboolean(APIENTRYP PFNDOLISRENDERBUFFERPROC)(GLuint renderbuffer);
typedef void(APIENTRYP PFNDOLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat,
GLsizei width, GLsizei height);
typedef void(APIENTRYP PFNDOLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples,
GLenum internalformat,
GLsizei width, GLsizei height);
extern PFNDOLBINDFRAMEBUFFERPROC dolBindFramebuffer; extern PFNDOLBINDFRAMEBUFFERPROC dolBindFramebuffer;
extern PFNDOLBINDRENDERBUFFERPROC dolBindRenderbuffer; extern PFNDOLBINDRENDERBUFFERPROC dolBindRenderbuffer;

View File

@ -28,9 +28,11 @@
#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE #define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
#define GL_PROGRAM_BINARY_FORMATS 0x87FF #define GL_PROGRAM_BINARY_FORMATS 0x87FF
typedef void (APIENTRYP PFNDOLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); typedef void(APIENTRYP PFNDOLGETPROGRAMBINARYPROC)(GLuint program, GLsizei bufSize, GLsizei* length,
typedef void (APIENTRYP PFNDOLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); GLenum* binaryFormat, void* binary);
typedef void (APIENTRYP PFNDOLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); typedef void(APIENTRYP PFNDOLPROGRAMBINARYPROC)(GLuint program, GLenum binaryFormat,
const void* binary, GLsizei length);
typedef void(APIENTRYP PFNDOLPROGRAMPARAMETERIPROC)(GLuint program, GLenum pname, GLint value);
extern PFNDOLGETPROGRAMBINARYPROC dolGetProgramBinary; extern PFNDOLGETPROGRAMBINARYPROC dolGetProgramBinary;
extern PFNDOLPROGRAMBINARYPROC dolProgramBinary; extern PFNDOLPROGRAMBINARYPROC dolProgramBinary;

View File

@ -23,8 +23,10 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void *(APIENTRYP PFNDOLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); typedef void*(APIENTRYP PFNDOLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length,
typedef void (APIENTRYP PFNDOLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); GLbitfield access);
typedef void(APIENTRYP PFNDOLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset,
GLsizeiptr length);
extern PFNDOLFLUSHMAPPEDBUFFERRANGEPROC dolFlushMappedBufferRange; extern PFNDOLFLUSHMAPPEDBUFFERRANGEPROC dolFlushMappedBufferRange;
extern PFNDOLMAPBUFFERRANGEPROC dolMapBufferRange; extern PFNDOLMAPBUFFERRANGEPROC dolMapBufferRange;

View File

@ -23,7 +23,7 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP PFNDOLMINSAMPLESHADINGARBPROC) (GLfloat value); typedef void(APIENTRYP PFNDOLMINSAMPLESHADINGARBPROC)(GLfloat value);
extern PFNDOLMINSAMPLESHADINGARBPROC dolMinSampleShading; extern PFNDOLMINSAMPLESHADINGARBPROC dolMinSampleShading;

View File

@ -23,20 +23,28 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP PFNDOLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); typedef void(APIENTRYP PFNDOLGENSAMPLERSPROC)(GLsizei count, GLuint* samplers);
typedef void (APIENTRYP PFNDOLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); typedef void(APIENTRYP PFNDOLDELETESAMPLERSPROC)(GLsizei count, const GLuint* samplers);
typedef GLboolean (APIENTRYP PFNDOLISSAMPLERPROC) (GLuint sampler); typedef GLboolean(APIENTRYP PFNDOLISSAMPLERPROC)(GLuint sampler);
typedef void (APIENTRYP PFNDOLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); typedef void(APIENTRYP PFNDOLBINDSAMPLERPROC)(GLuint unit, GLuint sampler);
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param);
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname,
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); const GLint* param);
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param);
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname,
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); const GLfloat* param);
typedef void (APIENTRYP PFNDOLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname,
typedef void (APIENTRYP PFNDOLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); const GLint* param);
typedef void (APIENTRYP PFNDOLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname,
typedef void (APIENTRYP PFNDOLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); const GLuint* param);
typedef void(APIENTRYP PFNDOLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname,
GLint* params);
typedef void(APIENTRYP PFNDOLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname,
GLint* params);
typedef void(APIENTRYP PFNDOLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname,
GLfloat* params);
typedef void(APIENTRYP PFNDOLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname,
GLuint* params);
extern PFNDOLBINDSAMPLERPROC dolBindSampler; extern PFNDOLBINDSAMPLERPROC dolBindSampler;
extern PFNDOLDELETESAMPLERSPROC dolDeleteSamplers; extern PFNDOLDELETESAMPLERSPROC dolDeleteSamplers;

View File

@ -40,7 +40,9 @@
#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE #define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE
#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF #define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF
typedef void (APIENTRY * PFNDOLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); typedef void(APIENTRY* PFNDOLSHADERSTORAGEBLOCKBINDINGPROC)(GLuint program,
GLuint storageBlockIndex,
GLuint storageBlockBinding);
extern PFNDOLSHADERSTORAGEBLOCKBINDINGPROC dolShaderStorageBlockBinding; extern PFNDOLSHADERSTORAGEBLOCKBINDINGPROC dolShaderStorageBlockBinding;

View File

@ -23,14 +23,15 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef GLsync (APIENTRYP PFNDOLFENCESYNCPROC) (GLenum condition, GLbitfield flags); typedef GLsync(APIENTRYP PFNDOLFENCESYNCPROC)(GLenum condition, GLbitfield flags);
typedef GLboolean (APIENTRYP PFNDOLISSYNCPROC) (GLsync sync); typedef GLboolean(APIENTRYP PFNDOLISSYNCPROC)(GLsync sync);
typedef void (APIENTRYP PFNDOLDELETESYNCPROC) (GLsync sync); typedef void(APIENTRYP PFNDOLDELETESYNCPROC)(GLsync sync);
typedef GLenum (APIENTRYP PFNDOLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); typedef GLenum(APIENTRYP PFNDOLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout);
typedef void (APIENTRYP PFNDOLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); typedef void(APIENTRYP PFNDOLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout);
typedef void (APIENTRYP PFNDOLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); typedef void(APIENTRYP PFNDOLGETINTEGER64VPROC)(GLenum pname, GLint64* data);
typedef void (APIENTRYP PFNDOLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); typedef void(APIENTRYP PFNDOLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei bufSize,
typedef void (APIENTRYP PFNDOLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); GLsizei* length, GLint* values);
typedef void(APIENTRYP PFNDOLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64* data);
extern PFNDOLCLIENTWAITSYNCPROC dolClientWaitSync; extern PFNDOLCLIENTWAITSYNCPROC dolClientWaitSync;
extern PFNDOLDELETESYNCPROC dolDeleteSync; extern PFNDOLDELETESYNCPROC dolDeleteSync;

View File

@ -23,10 +23,16 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP PFNDOLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); typedef void(APIENTRYP PFNDOLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples,
typedef void (APIENTRYP PFNDOLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); GLenum internalformat, GLsizei width,
typedef void (APIENTRYP PFNDOLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); GLsizei height,
typedef void (APIENTRYP PFNDOLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask); GLboolean fixedsamplelocations);
typedef void(APIENTRYP PFNDOLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples,
GLenum internalformat, GLsizei width,
GLsizei height, GLsizei depth,
GLboolean fixedsamplelocations);
typedef void(APIENTRYP PFNDOLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat* val);
typedef void(APIENTRYP PFNDOLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask);
extern PFNDOLTEXIMAGE2DMULTISAMPLEPROC dolTexImage2DMultisample; extern PFNDOLTEXIMAGE2DMULTISAMPLEPROC dolTexImage2DMultisample;
extern PFNDOLTEXIMAGE3DMULTISAMPLEPROC dolTexImage3DMultisample; extern PFNDOLTEXIMAGE3DMULTISAMPLEPROC dolTexImage3DMultisample;

View File

@ -23,8 +23,14 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP PFNDOLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); typedef void(APIENTRYP PFNDOLTEXSTORAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples,
typedef void (APIENTRYP PFNDOLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); GLenum internalformat, GLsizei width,
GLsizei height,
GLboolean fixedsamplelocations);
typedef void(APIENTRYP PFNDOLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples,
GLenum internalformat, GLsizei width,
GLsizei height, GLsizei depth,
GLboolean fixedsamplelocations);
extern PFNDOLTEXSTORAGE2DMULTISAMPLEPROC dolTexStorage2DMultisample; extern PFNDOLTEXSTORAGE2DMULTISAMPLEPROC dolTexStorage2DMultisample;
extern PFNDOLTEXSTORAGE3DMULTISAMPLEPROC dolTexStorage3DMultisample; extern PFNDOLTEXSTORAGE3DMULTISAMPLEPROC dolTexStorage3DMultisample;

View File

@ -23,16 +23,29 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP PFNDOLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); typedef void(APIENTRYP PFNDOLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint* data);
typedef void (APIENTRYP PFNDOLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void(APIENTRYP PFNDOLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer,
typedef void (APIENTRYP PFNDOLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); GLintptr offset, GLsizeiptr size);
typedef void (APIENTRYP PFNDOLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); typedef void(APIENTRYP PFNDOLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer);
typedef void (APIENTRYP PFNDOLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); typedef void(APIENTRYP PFNDOLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount,
typedef void (APIENTRYP PFNDOLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); const GLchar* const* uniformNames,
typedef GLuint (APIENTRYP PFNDOLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); GLuint* uniformIndices);
typedef void (APIENTRYP PFNDOLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); typedef void(APIENTRYP PFNDOLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount,
typedef void (APIENTRYP PFNDOLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); const GLuint* uniformIndices, GLenum pname,
typedef void (APIENTRYP PFNDOLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); GLint* params);
typedef void(APIENTRYP PFNDOLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex,
GLsizei bufSize, GLsizei* length,
GLchar* uniformName);
typedef GLuint(APIENTRYP PFNDOLGETUNIFORMBLOCKINDEXPROC)(GLuint program,
const GLchar* uniformBlockName);
typedef void(APIENTRYP PFNDOLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex,
GLenum pname, GLint* params);
typedef void(APIENTRYP PFNDOLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program,
GLuint uniformBlockIndex,
GLsizei bufSize, GLsizei* length,
GLchar* uniformBlockName);
typedef void(APIENTRYP PFNDOLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex,
GLuint uniformBlockBinding);
extern PFNDOLBINDBUFFERBASEPROC dolBindBufferBase; extern PFNDOLBINDBUFFERBASEPROC dolBindBufferBase;
extern PFNDOLBINDBUFFERRANGEPROC dolBindBufferRange; extern PFNDOLBINDBUFFERRANGEPROC dolBindBufferRange;

View File

@ -23,10 +23,10 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP PFNDOLBINDVERTEXARRAYPROC) (GLuint array); typedef void(APIENTRYP PFNDOLBINDVERTEXARRAYPROC)(GLuint array);
typedef void (APIENTRYP PFNDOLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); typedef void(APIENTRYP PFNDOLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint* arrays);
typedef void (APIENTRYP PFNDOLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); typedef void(APIENTRYP PFNDOLGENVERTEXARRAYSPROC)(GLsizei n, GLuint* arrays);
typedef GLboolean (APIENTRYP PFNDOLISVERTEXARRAYPROC) (GLuint array); typedef GLboolean(APIENTRYP PFNDOLISVERTEXARRAYPROC)(GLuint array);
extern PFNDOLBINDVERTEXARRAYPROC dolBindVertexArray; extern PFNDOLBINDVERTEXARRAYPROC dolBindVertexArray;
extern PFNDOLDELETEVERTEXARRAYSPROC dolDeleteVertexArrays; extern PFNDOLDELETEVERTEXARRAYSPROC dolDeleteVertexArrays;

View File

@ -23,16 +23,18 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
typedef void (APIENTRYP PFNDOLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); typedef void(APIENTRYP PFNDOLVIEWPORTARRAYVPROC)(GLuint first, GLsizei count, const GLfloat* v);
typedef void (APIENTRYP PFNDOLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); typedef void(APIENTRYP PFNDOLVIEWPORTINDEXEDFPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat w,
typedef void (APIENTRYP PFNDOLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); GLfloat h);
typedef void (APIENTRYP PFNDOLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); typedef void(APIENTRYP PFNDOLVIEWPORTINDEXEDFVPROC)(GLuint index, const GLfloat* v);
typedef void (APIENTRYP PFNDOLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); typedef void(APIENTRYP PFNDOLSCISSORARRAYVPROC)(GLuint first, GLsizei count, const GLint* v);
typedef void (APIENTRYP PFNDOLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); typedef void(APIENTRYP PFNDOLSCISSORINDEXEDPROC)(GLuint index, GLint left, GLint bottom,
typedef void (APIENTRYP PFNDOLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); GLsizei width, GLsizei height);
typedef void (APIENTRYP PFNDOLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); typedef void(APIENTRYP PFNDOLSCISSORINDEXEDVPROC)(GLuint index, const GLint* v);
typedef void (APIENTRYP PFNDOLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); typedef void(APIENTRYP PFNDOLDEPTHRANGEARRAYVPROC)(GLuint first, GLsizei count, const GLdouble* v);
typedef void (APIENTRYP PFNDOLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); typedef void(APIENTRYP PFNDOLDEPTHRANGEINDEXEDPROC)(GLuint index, GLdouble n, GLdouble f);
typedef void(APIENTRYP PFNDOLGETFLOATI_VPROC)(GLenum target, GLuint index, GLfloat* data);
typedef void(APIENTRYP PFNDOLGETDOUBLEI_VPROC)(GLenum target, GLuint index, GLdouble* data);
extern PFNDOLDEPTHRANGEARRAYVPROC dolDepthRangeArrayv; extern PFNDOLDEPTHRANGEARRAYVPROC dolDepthRangeArrayv;
extern PFNDOLDEPTHRANGEINDEXEDPROC dolDepthRangeIndexed; extern PFNDOLDEPTHRANGEINDEXEDPROC dolDepthRangeIndexed;

File diff suppressed because it is too large Load Diff

View File

@ -7,14 +7,14 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/GL/GLExtensions/AMD_pinned_memory.h" #include "Common/GL/GLExtensions/AMD_pinned_memory.h"
#include "Common/GL/GLExtensions/ARB_ES2_compatibility.h"
#include "Common/GL/GLExtensions/ARB_ES3_compatibility.h"
#include "Common/GL/GLExtensions/ARB_blend_func_extended.h" #include "Common/GL/GLExtensions/ARB_blend_func_extended.h"
#include "Common/GL/GLExtensions/ARB_buffer_storage.h" #include "Common/GL/GLExtensions/ARB_buffer_storage.h"
#include "Common/GL/GLExtensions/ARB_clip_control.h" #include "Common/GL/GLExtensions/ARB_clip_control.h"
#include "Common/GL/GLExtensions/ARB_copy_image.h" #include "Common/GL/GLExtensions/ARB_copy_image.h"
#include "Common/GL/GLExtensions/ARB_debug_output.h" #include "Common/GL/GLExtensions/ARB_debug_output.h"
#include "Common/GL/GLExtensions/ARB_draw_elements_base_vertex.h" #include "Common/GL/GLExtensions/ARB_draw_elements_base_vertex.h"
#include "Common/GL/GLExtensions/ARB_ES2_compatibility.h"
#include "Common/GL/GLExtensions/ARB_ES3_compatibility.h"
#include "Common/GL/GLExtensions/ARB_framebuffer_object.h" #include "Common/GL/GLExtensions/ARB_framebuffer_object.h"
#include "Common/GL/GLExtensions/ARB_get_program_binary.h" #include "Common/GL/GLExtensions/ARB_get_program_binary.h"
#include "Common/GL/GLExtensions/ARB_map_buffer_range.h" #include "Common/GL/GLExtensions/ARB_map_buffer_range.h"
@ -29,6 +29,10 @@
#include "Common/GL/GLExtensions/ARB_vertex_array_object.h" #include "Common/GL/GLExtensions/ARB_vertex_array_object.h"
#include "Common/GL/GLExtensions/ARB_viewport_array.h" #include "Common/GL/GLExtensions/ARB_viewport_array.h"
#include "Common/GL/GLExtensions/EXT_texture_filter_anisotropic.h" #include "Common/GL/GLExtensions/EXT_texture_filter_anisotropic.h"
#include "Common/GL/GLExtensions/HP_occlusion_test.h"
#include "Common/GL/GLExtensions/KHR_debug.h"
#include "Common/GL/GLExtensions/NV_occlusion_query_samples.h"
#include "Common/GL/GLExtensions/NV_primitive_restart.h"
#include "Common/GL/GLExtensions/gl_1_1.h" #include "Common/GL/GLExtensions/gl_1_1.h"
#include "Common/GL/GLExtensions/gl_1_2.h" #include "Common/GL/GLExtensions/gl_1_2.h"
#include "Common/GL/GLExtensions/gl_1_3.h" #include "Common/GL/GLExtensions/gl_1_3.h"
@ -43,20 +47,16 @@
#include "Common/GL/GLExtensions/gl_4_3.h" #include "Common/GL/GLExtensions/gl_4_3.h"
#include "Common/GL/GLExtensions/gl_4_4.h" #include "Common/GL/GLExtensions/gl_4_4.h"
#include "Common/GL/GLExtensions/gl_4_5.h" #include "Common/GL/GLExtensions/gl_4_5.h"
#include "Common/GL/GLExtensions/HP_occlusion_test.h"
#include "Common/GL/GLExtensions/KHR_debug.h"
#include "Common/GL/GLExtensions/NV_occlusion_query_samples.h"
#include "Common/GL/GLExtensions/NV_primitive_restart.h"
namespace GLExtensions namespace GLExtensions
{ {
// Initializes the interface // Initializes the interface
bool Init(); bool Init();
// Function for checking if the hardware supports an extension // Function for checking if the hardware supports an extension
// example: if (GLExtensions::Supports("GL_ARB_multi_map")) // example: if (GLExtensions::Supports("GL_ARB_multi_map"))
bool Supports(const std::string& name); bool Supports(const std::string& name);
// Returns OpenGL version in format 430 // Returns OpenGL version in format 430
u32 Version(); u32 Version();
} }

View File

@ -23,5 +23,5 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
#define GL_OCCLUSION_TEST_HP 0x8165 #define GL_OCCLUSION_TEST_HP 0x8165
#define GL_OCCLUSION_TEST_RESULT_HP 0x8166 #define GL_OCCLUSION_TEST_RESULT_HP 0x8166

View File

@ -64,18 +64,31 @@
#define GL_DEBUG_SEVERITY_LOW 0x9148 #define GL_DEBUG_SEVERITY_LOW 0x9148
#define GL_DEBUG_OUTPUT 0x92E0 #define GL_DEBUG_OUTPUT 0x92E0
typedef void (APIENTRYP GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); typedef void(APIENTRYP GLDEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity,
GLsizei length, const GLchar* message, const void* userParam);
typedef void (APIENTRYP PFNDOLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const GLvoid *userParam); typedef void(APIENTRYP PFNDOLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback,
typedef void (APIENTRYP PFNDOLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint* ids, GLboolean enabled); const GLvoid* userParam);
typedef void (APIENTRYP PFNDOLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* buf); typedef void(APIENTRYP PFNDOLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity,
typedef GLuint (APIENTRYP PFNDOLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufsize, GLenum* sources, GLenum* types, GLuint* ids, GLenum* severities, GLsizei* lengths, GLchar* messageLog); GLsizei count, const GLuint* ids,
typedef void (APIENTRYP PFNDOLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei* length, GLchar *label); GLboolean enabled);
typedef void (APIENTRYP PFNDOLGETOBJECTPTRLABELPROC) (void* ptr, GLsizei bufSize, GLsizei* length, GLchar *label); typedef void(APIENTRYP PFNDOLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id,
typedef void (APIENTRYP PFNDOLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar* label); GLenum severity, GLsizei length,
typedef void (APIENTRYP PFNDOLOBJECTPTRLABELPROC) (void* ptr, GLsizei length, const GLchar* label); const GLchar* buf);
typedef void (APIENTRYP PFNDOLPOPDEBUGGROUPPROC) (void); typedef GLuint(APIENTRYP PFNDOLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufsize,
typedef void (APIENTRYP PFNDOLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar * message); GLenum* sources, GLenum* types, GLuint* ids,
GLenum* severities, GLsizei* lengths,
GLchar* messageLog);
typedef void(APIENTRYP PFNDOLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize,
GLsizei* length, GLchar* label);
typedef void(APIENTRYP PFNDOLGETOBJECTPTRLABELPROC)(void* ptr, GLsizei bufSize, GLsizei* length,
GLchar* label);
typedef void(APIENTRYP PFNDOLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length,
const GLchar* label);
typedef void(APIENTRYP PFNDOLOBJECTPTRLABELPROC)(void* ptr, GLsizei length, const GLchar* label);
typedef void(APIENTRYP PFNDOLPOPDEBUGGROUPPROC)(void);
typedef void(APIENTRYP PFNDOLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length,
const GLchar* message);
extern PFNDOLDEBUGMESSAGECALLBACKPROC dolDebugMessageCallback; extern PFNDOLDEBUGMESSAGECALLBACKPROC dolDebugMessageCallback;
extern PFNDOLDEBUGMESSAGECONTROLPROC dolDebugMessageControl; extern PFNDOLDEBUGMESSAGECONTROLPROC dolDebugMessageControl;

View File

@ -23,18 +23,18 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
#define GL_PIXEL_COUNTER_BITS_NV 0x8864 #define GL_PIXEL_COUNTER_BITS_NV 0x8864
#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 #define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865
#define GL_PIXEL_COUNT_NV 0x8866 #define GL_PIXEL_COUNT_NV 0x8866
#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 #define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867
typedef void (APIENTRYP PFNDOLGENOCCLUSIONQUERIESNVPROC) (GLsizei n, GLuint *ids); typedef void(APIENTRYP PFNDOLGENOCCLUSIONQUERIESNVPROC)(GLsizei n, GLuint* ids);
typedef void (APIENTRYP PFNDOLDELETEOCCLUSIONQUERIESNVPROC) (GLsizei n, const GLuint *ids); typedef void(APIENTRYP PFNDOLDELETEOCCLUSIONQUERIESNVPROC)(GLsizei n, const GLuint* ids);
typedef GLboolean (APIENTRYP PFNDOLISOCCLUSIONQUERYNVPROC) (GLuint id); typedef GLboolean(APIENTRYP PFNDOLISOCCLUSIONQUERYNVPROC)(GLuint id);
typedef void (APIENTRYP PFNDOLBEGINOCCLUSIONQUERYNVPROC) (GLuint id); typedef void(APIENTRYP PFNDOLBEGINOCCLUSIONQUERYNVPROC)(GLuint id);
typedef void (APIENTRYP PFNDOLENDOCCLUSIONQUERYNVPROC) (void); typedef void(APIENTRYP PFNDOLENDOCCLUSIONQUERYNVPROC)(void);
typedef void (APIENTRYP PFNDOLGETOCCLUSIONQUERYIVNVPROC) (GLuint id, GLenum pname, GLint *params); typedef void(APIENTRYP PFNDOLGETOCCLUSIONQUERYIVNVPROC)(GLuint id, GLenum pname, GLint* params);
typedef void (APIENTRYP PFNDOLGETOCCLUSIONQUERYUIVNVPROC) (GLuint id, GLenum pname, GLuint *params); typedef void(APIENTRYP PFNDOLGETOCCLUSIONQUERYUIVNVPROC)(GLuint id, GLenum pname, GLuint* params);
extern PFNDOLGENOCCLUSIONQUERIESNVPROC dolGenOcclusionQueriesNV; extern PFNDOLGENOCCLUSIONQUERIESNVPROC dolGenOcclusionQueriesNV;
extern PFNDOLDELETEOCCLUSIONQUERIESNVPROC dolDeleteOcclusionQueriesNV; extern PFNDOLDELETEOCCLUSIONQUERIESNVPROC dolDeleteOcclusionQueriesNV;

View File

@ -26,8 +26,8 @@
#define GL_PRIMITIVE_RESTART_NV 0x8558 #define GL_PRIMITIVE_RESTART_NV 0x8558
#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559 #define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559
typedef void (APIENTRYP PFNDOLPRIMITIVERESTARTINDEXNVPROC) (GLuint index); typedef void(APIENTRYP PFNDOLPRIMITIVERESTARTINDEXNVPROC)(GLuint index);
typedef void (APIENTRYP PFNDOLPRIMITIVERESTARTNVPROC) (void); typedef void(APIENTRYP PFNDOLPRIMITIVERESTARTNVPROC)(void);
extern PFNDOLPRIMITIVERESTARTINDEXNVPROC dolPrimitiveRestartIndexNV; extern PFNDOLPRIMITIVERESTARTINDEXNVPROC dolPrimitiveRestartIndexNV;
extern PFNDOLPRIMITIVERESTARTNVPROC dolPrimitiveRestartNV; extern PFNDOLPRIMITIVERESTARTNVPROC dolPrimitiveRestartNV;

File diff suppressed because it is too large Load Diff

View File

@ -23,52 +23,62 @@
#include "Common/GL/GLExtensions/gl_common.h" #include "Common/GL/GLExtensions/gl_common.h"
#define GL_RESCALE_NORMAL 0x803A #define GL_RESCALE_NORMAL 0x803A
#define GL_CLAMP_TO_EDGE 0x812F #define GL_CLAMP_TO_EDGE 0x812F
#define GL_MAX_ELEMENTS_VERTICES 0x80E8 #define GL_MAX_ELEMENTS_VERTICES 0x80E8
#define GL_MAX_ELEMENTS_INDICES 0x80E9 #define GL_MAX_ELEMENTS_INDICES 0x80E9
#define GL_BGR 0x80E0 #define GL_BGR 0x80E0
#define GL_BGRA 0x80E1 #define GL_BGRA 0x80E1
#define GL_UNSIGNED_BYTE_3_3_2 0x8032 #define GL_UNSIGNED_BYTE_3_3_2 0x8032
#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 #define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
#define GL_UNSIGNED_SHORT_5_6_5 0x8363 #define GL_UNSIGNED_SHORT_5_6_5 0x8363
#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 #define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 #define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 #define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
#define GL_UNSIGNED_INT_8_8_8_8 0x8035 #define GL_UNSIGNED_INT_8_8_8_8 0x8035
#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
#define GL_UNSIGNED_INT_10_10_10_2 0x8036 #define GL_UNSIGNED_INT_10_10_10_2 0x8036
#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 #define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8
#define GL_SINGLE_COLOR 0x81F9 #define GL_SINGLE_COLOR 0x81F9
#define GL_SEPARATE_SPECULAR_COLOR 0x81FA #define GL_SEPARATE_SPECULAR_COLOR 0x81FA
#define GL_TEXTURE_MIN_LOD 0x813A #define GL_TEXTURE_MIN_LOD 0x813A
#define GL_TEXTURE_MAX_LOD 0x813B #define GL_TEXTURE_MAX_LOD 0x813B
#define GL_TEXTURE_BASE_LEVEL 0x813C #define GL_TEXTURE_BASE_LEVEL 0x813C
#define GL_TEXTURE_MAX_LEVEL 0x813D #define GL_TEXTURE_MAX_LEVEL 0x813D
#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 #define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12
#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 #define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13
#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 #define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22
#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23
#define GL_ALIASED_POINT_SIZE_RANGE 0x846D #define GL_ALIASED_POINT_SIZE_RANGE 0x846D
#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E #define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
#define GL_PACK_SKIP_IMAGES 0x806B #define GL_PACK_SKIP_IMAGES 0x806B
#define GL_PACK_IMAGE_HEIGHT 0x806C #define GL_PACK_IMAGE_HEIGHT 0x806C
#define GL_UNPACK_SKIP_IMAGES 0x806D #define GL_UNPACK_SKIP_IMAGES 0x806D
#define GL_UNPACK_IMAGE_HEIGHT 0x806E #define GL_UNPACK_IMAGE_HEIGHT 0x806E
#define GL_TEXTURE_3D 0x806F #define GL_TEXTURE_3D 0x806F
#define GL_PROXY_TEXTURE_3D 0x8070 #define GL_PROXY_TEXTURE_3D 0x8070
#define GL_TEXTURE_DEPTH 0x8071 #define GL_TEXTURE_DEPTH 0x8071
#define GL_TEXTURE_WRAP_R 0x8072 #define GL_TEXTURE_WRAP_R 0x8072
#define GL_MAX_3D_TEXTURE_SIZE 0x8073 #define GL_MAX_3D_TEXTURE_SIZE 0x8073
#define GL_TEXTURE_BINDING_3D 0x806A #define GL_TEXTURE_BINDING_3D 0x806A
typedef void (APIENTRYP PFNDOLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); typedef void(APIENTRYP PFNDOLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end,
typedef void (APIENTRYP PFNDOLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); GLsizei count, GLenum type,
typedef void (APIENTRYP PFNDOLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); const GLvoid* indices);
typedef void (APIENTRYP PFNDOLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void(APIENTRYP PFNDOLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat,
GLsizei width, GLsizei height, GLsizei depth,
GLint border, GLenum format, GLenum type,
const GLvoid* pixels);
typedef void(APIENTRYP PFNDOLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset,
GLint yoffset, GLint zoffset, GLsizei width,
GLsizei height, GLsizei depth, GLenum format,
GLenum type, const GLvoid* pixels);
typedef void(APIENTRYP PFNDOLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset,
GLint yoffset, GLint zoffset, GLint x, GLint y,
GLsizei width, GLsizei height);
extern PFNDOLCOPYTEXSUBIMAGE3DPROC dolCopyTexSubImage3D; extern PFNDOLCOPYTEXSUBIMAGE3DPROC dolCopyTexSubImage3D;
extern PFNDOLDRAWRANGEELEMENTSPROC dolDrawRangeElements; extern PFNDOLDRAWRANGEELEMENTSPROC dolDrawRangeElements;

Some files were not shown because too many files have changed in this diff Show More