Reformat all the things. Have fun with merge conflicts.
This commit is contained in:
parent
2115e8a4a6
commit
3570c7f03a
File diff suppressed because it is too large
Load Diff
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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_)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
@ -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
Loading…
Reference in New Issue