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
|
||||
{
|
||||
enum ButtonType
|
||||
{
|
||||
// GC
|
||||
BUTTON_A = 0,
|
||||
BUTTON_B = 1,
|
||||
BUTTON_START = 2,
|
||||
BUTTON_X = 3,
|
||||
BUTTON_Y = 4,
|
||||
BUTTON_Z = 5,
|
||||
BUTTON_UP = 6,
|
||||
BUTTON_DOWN = 7,
|
||||
BUTTON_LEFT = 8,
|
||||
BUTTON_RIGHT = 9,
|
||||
STICK_MAIN = 10, // Used on Java Side
|
||||
STICK_MAIN_UP = 11,
|
||||
STICK_MAIN_DOWN = 12,
|
||||
STICK_MAIN_LEFT = 13,
|
||||
STICK_MAIN_RIGHT = 14,
|
||||
STICK_C = 15, // Used on Java Side
|
||||
STICK_C_UP = 16,
|
||||
STICK_C_DOWN = 17,
|
||||
STICK_C_LEFT = 18,
|
||||
STICK_C_RIGHT = 19,
|
||||
TRIGGER_L = 20,
|
||||
TRIGGER_R = 21,
|
||||
// Wiimote
|
||||
WIIMOTE_BUTTON_A = 100,
|
||||
WIIMOTE_BUTTON_B = 101,
|
||||
WIIMOTE_BUTTON_MINUS = 102,
|
||||
WIIMOTE_BUTTON_PLUS = 103,
|
||||
WIIMOTE_BUTTON_HOME = 104,
|
||||
WIIMOTE_BUTTON_1 = 105,
|
||||
WIIMOTE_BUTTON_2 = 106,
|
||||
WIIMOTE_UP = 107,
|
||||
WIIMOTE_DOWN = 108,
|
||||
WIIMOTE_LEFT = 109,
|
||||
WIIMOTE_RIGHT = 110,
|
||||
WIIMOTE_IR = 111, // To Be Used on Java Side
|
||||
WIIMOTE_IR_UP = 112,
|
||||
WIIMOTE_IR_DOWN = 113,
|
||||
WIIMOTE_IR_LEFT = 114,
|
||||
WIIMOTE_IR_RIGHT = 115,
|
||||
WIIMOTE_IR_FORWARD = 116,
|
||||
WIIMOTE_IR_BACKWARD = 117,
|
||||
WIIMOTE_IR_HIDE = 118,
|
||||
WIIMOTE_SWING = 119, // To Be Used on Java Side
|
||||
WIIMOTE_SWING_UP = 120,
|
||||
WIIMOTE_SWING_DOWN = 121,
|
||||
WIIMOTE_SWING_LEFT = 122,
|
||||
WIIMOTE_SWING_RIGHT = 123,
|
||||
WIIMOTE_SWING_FORWARD = 124,
|
||||
WIIMOTE_SWING_BACKWARD = 125,
|
||||
WIIMOTE_TILT = 126, // To Be Used on Java Side
|
||||
WIIMOTE_TILT_FORWARD = 127,
|
||||
WIIMOTE_TILT_BACKWARD = 128,
|
||||
WIIMOTE_TILT_LEFT = 129,
|
||||
WIIMOTE_TILT_RIGHT = 130,
|
||||
WIIMOTE_TILT_MODIFIER = 131,
|
||||
WIIMOTE_SHAKE_X = 132,
|
||||
WIIMOTE_SHAKE_Y = 133,
|
||||
WIIMOTE_SHAKE_Z = 134,
|
||||
//Nunchuk
|
||||
NUNCHUK_BUTTON_C = 200,
|
||||
NUNCHUK_BUTTON_Z = 201,
|
||||
NUNCHUK_STICK = 202, // To Be Used on Java Side
|
||||
NUNCHUK_STICK_UP = 203,
|
||||
NUNCHUK_STICK_DOWN = 204,
|
||||
NUNCHUK_STICK_LEFT = 205,
|
||||
NUNCHUK_STICK_RIGHT = 206,
|
||||
NUNCHUK_SWING = 207, // To Be Used on Java Side
|
||||
NUNCHUK_SWING_UP = 208,
|
||||
NUNCHUK_SWING_DOWN = 209,
|
||||
NUNCHUK_SWING_LEFT = 210,
|
||||
NUNCHUK_SWING_RIGHT = 211,
|
||||
NUNCHUK_SWING_FORWARD = 212,
|
||||
NUNCHUK_SWING_BACKWARD = 213,
|
||||
NUNCHUK_TILT = 214, // To Be Used on Java Side
|
||||
NUNCHUK_TILT_FORWARD = 215,
|
||||
NUNCHUK_TILT_BACKWARD = 216,
|
||||
NUNCHUK_TILT_LEFT = 217,
|
||||
NUNCHUK_TILT_RIGHT = 218,
|
||||
NUNCHUK_TILT_MODIFIER = 219,
|
||||
NUNCHUK_SHAKE_X = 220,
|
||||
NUNCHUK_SHAKE_Y = 221,
|
||||
NUNCHUK_SHAKE_Z = 222,
|
||||
//Classic
|
||||
CLASSIC_BUTTON_A = 300,
|
||||
CLASSIC_BUTTON_B = 301,
|
||||
CLASSIC_BUTTON_X = 302,
|
||||
CLASSIC_BUTTON_Y = 303,
|
||||
CLASSIC_BUTTON_MINUS = 304,
|
||||
CLASSIC_BUTTON_PLUS = 305,
|
||||
CLASSIC_BUTTON_HOME = 306,
|
||||
CLASSIC_BUTTON_ZL = 307,
|
||||
CLASSIC_BUTTON_ZR = 308,
|
||||
CLASSIC_DPAD_UP = 309,
|
||||
CLASSIC_DPAD_DOWN = 310,
|
||||
CLASSIC_DPAD_LEFT = 311,
|
||||
CLASSIC_DPAD_RIGHT = 312,
|
||||
CLASSIC_STICK_LEFT = 313, // To Be Used on Java Side
|
||||
CLASSIC_STICK_LEFT_UP = 314,
|
||||
CLASSIC_STICK_LEFT_DOWN = 315,
|
||||
CLASSIC_STICK_LEFT_LEFT = 316,
|
||||
CLASSIC_STICK_LEFT_RIGHT = 317,
|
||||
CLASSIC_STICK_RIGHT = 318, // To Be Used on Java Side
|
||||
CLASSIC_STICK_RIGHT_UP = 319,
|
||||
CLASSIC_STICK_RIGHT_DOWN = 320,
|
||||
CLASSIC_STICK_RIGHT_LEFT = 321,
|
||||
CLASSIC_STICK_RIGHT_RIGHT = 322,
|
||||
CLASSIC_TRIGGER_L = 323,
|
||||
CLASSIC_TRIGGER_R = 324,
|
||||
//Guitar
|
||||
GUITAR_BUTTON_MINUS = 400,
|
||||
GUITAR_BUTTON_PLUS = 401,
|
||||
GUITAR_FRET_GREEN = 402,
|
||||
GUITAR_FRET_RED = 403,
|
||||
GUITAR_FRET_YELLOW = 404,
|
||||
GUITAR_FRET_BLUE = 405,
|
||||
GUITAR_FRET_ORANGE = 406,
|
||||
GUITAR_STRUM_UP = 407,
|
||||
GUITAR_STRUM_DOWN = 408,
|
||||
GUITAR_STICK = 409, // To Be Used on Java Side
|
||||
GUITAR_STICK_UP = 410,
|
||||
GUITAR_STICK_DOWN = 411,
|
||||
GUITAR_STICK_LEFT = 412,
|
||||
GUITAR_STICK_RIGHT = 413,
|
||||
GUITAR_WHAMMY_BAR = 414,
|
||||
//Drums
|
||||
DRUMS_BUTTON_MINUS = 500,
|
||||
DRUMS_BUTTON_PLUS = 501,
|
||||
DRUMS_PAD_RED = 502,
|
||||
DRUMS_PAD_YELLOW = 503,
|
||||
DRUMS_PAD_BLUE = 504,
|
||||
DRUMS_PAD_GREEN = 505,
|
||||
DRUMS_PAD_ORANGE = 506,
|
||||
DRUMS_PAD_BASS = 507,
|
||||
DRUMS_STICK = 508, // To Be Used on Java Side
|
||||
DRUMS_STICK_UP = 509,
|
||||
DRUMS_STICK_DOWN = 510,
|
||||
DRUMS_STICK_LEFT = 511,
|
||||
DRUMS_STICK_RIGHT = 512,
|
||||
//Turntable
|
||||
TURNTABLE_BUTTON_GREEN_LEFT = 600,
|
||||
TURNTABLE_BUTTON_RED_LEFT = 601,
|
||||
TURNTABLE_BUTTON_BLUE_LEFT = 602,
|
||||
TURNTABLE_BUTTON_GREEN_RIGHT = 603,
|
||||
TURNTABLE_BUTTON_RED_RIGHT = 604,
|
||||
TURNTABLE_BUTTON_BLUE_RIGHT = 605,
|
||||
TURNTABLE_BUTTON_MINUS = 606,
|
||||
TURNTABLE_BUTTON_PLUS = 607,
|
||||
TURNTABLE_BUTTON_HOME = 608,
|
||||
TURNTABLE_BUTTON_EUPHORIA = 609,
|
||||
TURNTABLE_TABLE_LEFT = 610, // To Be Used on Java Side
|
||||
TURNTABLE_TABLE_LEFT_LEFT = 611,
|
||||
TURNTABLE_TABLE_LEFT_RIGHT = 612,
|
||||
TURNTABLE_TABLE_RIGHT = 613, // To Be Used on Java Side
|
||||
TURNTABLE_TABLE_RIGHT_LEFT = 614,
|
||||
TURNTABLE_TABLE_RIGHT_RIGHT = 615,
|
||||
TURNTABLE_STICK = 616, // To Be Used on Java Side
|
||||
TURNTABLE_STICK_UP = 617,
|
||||
TURNTABLE_STICK_DOWN = 618,
|
||||
TURNTABLE_STICK_LEFT = 619,
|
||||
TURNTABLE_STICK_RIGHT = 620,
|
||||
TURNTABLE_EFFECT_DIAL = 621,
|
||||
TURNTABLE_CROSSFADE = 622, // To Be Used on Java Side
|
||||
TURNTABLE_CROSSFADE_LEFT = 623,
|
||||
TURNTABLE_CROSSFADE_RIGHT = 624,
|
||||
};
|
||||
enum ButtonState
|
||||
{
|
||||
BUTTON_RELEASED = 0,
|
||||
BUTTON_PRESSED = 1
|
||||
};
|
||||
enum BindType
|
||||
{
|
||||
BIND_BUTTON = 0,
|
||||
BIND_AXIS
|
||||
};
|
||||
class Button
|
||||
{
|
||||
private:
|
||||
ButtonState m_state;
|
||||
public:
|
||||
Button() : m_state(BUTTON_RELEASED) {}
|
||||
void SetState(ButtonState state) { m_state = state; }
|
||||
bool Pressed() { return m_state == BUTTON_PRESSED; }
|
||||
enum ButtonType
|
||||
{
|
||||
// GC
|
||||
BUTTON_A = 0,
|
||||
BUTTON_B = 1,
|
||||
BUTTON_START = 2,
|
||||
BUTTON_X = 3,
|
||||
BUTTON_Y = 4,
|
||||
BUTTON_Z = 5,
|
||||
BUTTON_UP = 6,
|
||||
BUTTON_DOWN = 7,
|
||||
BUTTON_LEFT = 8,
|
||||
BUTTON_RIGHT = 9,
|
||||
STICK_MAIN = 10, // Used on Java Side
|
||||
STICK_MAIN_UP = 11,
|
||||
STICK_MAIN_DOWN = 12,
|
||||
STICK_MAIN_LEFT = 13,
|
||||
STICK_MAIN_RIGHT = 14,
|
||||
STICK_C = 15, // Used on Java Side
|
||||
STICK_C_UP = 16,
|
||||
STICK_C_DOWN = 17,
|
||||
STICK_C_LEFT = 18,
|
||||
STICK_C_RIGHT = 19,
|
||||
TRIGGER_L = 20,
|
||||
TRIGGER_R = 21,
|
||||
// Wiimote
|
||||
WIIMOTE_BUTTON_A = 100,
|
||||
WIIMOTE_BUTTON_B = 101,
|
||||
WIIMOTE_BUTTON_MINUS = 102,
|
||||
WIIMOTE_BUTTON_PLUS = 103,
|
||||
WIIMOTE_BUTTON_HOME = 104,
|
||||
WIIMOTE_BUTTON_1 = 105,
|
||||
WIIMOTE_BUTTON_2 = 106,
|
||||
WIIMOTE_UP = 107,
|
||||
WIIMOTE_DOWN = 108,
|
||||
WIIMOTE_LEFT = 109,
|
||||
WIIMOTE_RIGHT = 110,
|
||||
WIIMOTE_IR = 111, // To Be Used on Java Side
|
||||
WIIMOTE_IR_UP = 112,
|
||||
WIIMOTE_IR_DOWN = 113,
|
||||
WIIMOTE_IR_LEFT = 114,
|
||||
WIIMOTE_IR_RIGHT = 115,
|
||||
WIIMOTE_IR_FORWARD = 116,
|
||||
WIIMOTE_IR_BACKWARD = 117,
|
||||
WIIMOTE_IR_HIDE = 118,
|
||||
WIIMOTE_SWING = 119, // To Be Used on Java Side
|
||||
WIIMOTE_SWING_UP = 120,
|
||||
WIIMOTE_SWING_DOWN = 121,
|
||||
WIIMOTE_SWING_LEFT = 122,
|
||||
WIIMOTE_SWING_RIGHT = 123,
|
||||
WIIMOTE_SWING_FORWARD = 124,
|
||||
WIIMOTE_SWING_BACKWARD = 125,
|
||||
WIIMOTE_TILT = 126, // To Be Used on Java Side
|
||||
WIIMOTE_TILT_FORWARD = 127,
|
||||
WIIMOTE_TILT_BACKWARD = 128,
|
||||
WIIMOTE_TILT_LEFT = 129,
|
||||
WIIMOTE_TILT_RIGHT = 130,
|
||||
WIIMOTE_TILT_MODIFIER = 131,
|
||||
WIIMOTE_SHAKE_X = 132,
|
||||
WIIMOTE_SHAKE_Y = 133,
|
||||
WIIMOTE_SHAKE_Z = 134,
|
||||
// Nunchuk
|
||||
NUNCHUK_BUTTON_C = 200,
|
||||
NUNCHUK_BUTTON_Z = 201,
|
||||
NUNCHUK_STICK = 202, // To Be Used on Java Side
|
||||
NUNCHUK_STICK_UP = 203,
|
||||
NUNCHUK_STICK_DOWN = 204,
|
||||
NUNCHUK_STICK_LEFT = 205,
|
||||
NUNCHUK_STICK_RIGHT = 206,
|
||||
NUNCHUK_SWING = 207, // To Be Used on Java Side
|
||||
NUNCHUK_SWING_UP = 208,
|
||||
NUNCHUK_SWING_DOWN = 209,
|
||||
NUNCHUK_SWING_LEFT = 210,
|
||||
NUNCHUK_SWING_RIGHT = 211,
|
||||
NUNCHUK_SWING_FORWARD = 212,
|
||||
NUNCHUK_SWING_BACKWARD = 213,
|
||||
NUNCHUK_TILT = 214, // To Be Used on Java Side
|
||||
NUNCHUK_TILT_FORWARD = 215,
|
||||
NUNCHUK_TILT_BACKWARD = 216,
|
||||
NUNCHUK_TILT_LEFT = 217,
|
||||
NUNCHUK_TILT_RIGHT = 218,
|
||||
NUNCHUK_TILT_MODIFIER = 219,
|
||||
NUNCHUK_SHAKE_X = 220,
|
||||
NUNCHUK_SHAKE_Y = 221,
|
||||
NUNCHUK_SHAKE_Z = 222,
|
||||
// Classic
|
||||
CLASSIC_BUTTON_A = 300,
|
||||
CLASSIC_BUTTON_B = 301,
|
||||
CLASSIC_BUTTON_X = 302,
|
||||
CLASSIC_BUTTON_Y = 303,
|
||||
CLASSIC_BUTTON_MINUS = 304,
|
||||
CLASSIC_BUTTON_PLUS = 305,
|
||||
CLASSIC_BUTTON_HOME = 306,
|
||||
CLASSIC_BUTTON_ZL = 307,
|
||||
CLASSIC_BUTTON_ZR = 308,
|
||||
CLASSIC_DPAD_UP = 309,
|
||||
CLASSIC_DPAD_DOWN = 310,
|
||||
CLASSIC_DPAD_LEFT = 311,
|
||||
CLASSIC_DPAD_RIGHT = 312,
|
||||
CLASSIC_STICK_LEFT = 313, // To Be Used on Java Side
|
||||
CLASSIC_STICK_LEFT_UP = 314,
|
||||
CLASSIC_STICK_LEFT_DOWN = 315,
|
||||
CLASSIC_STICK_LEFT_LEFT = 316,
|
||||
CLASSIC_STICK_LEFT_RIGHT = 317,
|
||||
CLASSIC_STICK_RIGHT = 318, // To Be Used on Java Side
|
||||
CLASSIC_STICK_RIGHT_UP = 319,
|
||||
CLASSIC_STICK_RIGHT_DOWN = 320,
|
||||
CLASSIC_STICK_RIGHT_LEFT = 321,
|
||||
CLASSIC_STICK_RIGHT_RIGHT = 322,
|
||||
CLASSIC_TRIGGER_L = 323,
|
||||
CLASSIC_TRIGGER_R = 324,
|
||||
// Guitar
|
||||
GUITAR_BUTTON_MINUS = 400,
|
||||
GUITAR_BUTTON_PLUS = 401,
|
||||
GUITAR_FRET_GREEN = 402,
|
||||
GUITAR_FRET_RED = 403,
|
||||
GUITAR_FRET_YELLOW = 404,
|
||||
GUITAR_FRET_BLUE = 405,
|
||||
GUITAR_FRET_ORANGE = 406,
|
||||
GUITAR_STRUM_UP = 407,
|
||||
GUITAR_STRUM_DOWN = 408,
|
||||
GUITAR_STICK = 409, // To Be Used on Java Side
|
||||
GUITAR_STICK_UP = 410,
|
||||
GUITAR_STICK_DOWN = 411,
|
||||
GUITAR_STICK_LEFT = 412,
|
||||
GUITAR_STICK_RIGHT = 413,
|
||||
GUITAR_WHAMMY_BAR = 414,
|
||||
// Drums
|
||||
DRUMS_BUTTON_MINUS = 500,
|
||||
DRUMS_BUTTON_PLUS = 501,
|
||||
DRUMS_PAD_RED = 502,
|
||||
DRUMS_PAD_YELLOW = 503,
|
||||
DRUMS_PAD_BLUE = 504,
|
||||
DRUMS_PAD_GREEN = 505,
|
||||
DRUMS_PAD_ORANGE = 506,
|
||||
DRUMS_PAD_BASS = 507,
|
||||
DRUMS_STICK = 508, // To Be Used on Java Side
|
||||
DRUMS_STICK_UP = 509,
|
||||
DRUMS_STICK_DOWN = 510,
|
||||
DRUMS_STICK_LEFT = 511,
|
||||
DRUMS_STICK_RIGHT = 512,
|
||||
// Turntable
|
||||
TURNTABLE_BUTTON_GREEN_LEFT = 600,
|
||||
TURNTABLE_BUTTON_RED_LEFT = 601,
|
||||
TURNTABLE_BUTTON_BLUE_LEFT = 602,
|
||||
TURNTABLE_BUTTON_GREEN_RIGHT = 603,
|
||||
TURNTABLE_BUTTON_RED_RIGHT = 604,
|
||||
TURNTABLE_BUTTON_BLUE_RIGHT = 605,
|
||||
TURNTABLE_BUTTON_MINUS = 606,
|
||||
TURNTABLE_BUTTON_PLUS = 607,
|
||||
TURNTABLE_BUTTON_HOME = 608,
|
||||
TURNTABLE_BUTTON_EUPHORIA = 609,
|
||||
TURNTABLE_TABLE_LEFT = 610, // To Be Used on Java Side
|
||||
TURNTABLE_TABLE_LEFT_LEFT = 611,
|
||||
TURNTABLE_TABLE_LEFT_RIGHT = 612,
|
||||
TURNTABLE_TABLE_RIGHT = 613, // To Be Used on Java Side
|
||||
TURNTABLE_TABLE_RIGHT_LEFT = 614,
|
||||
TURNTABLE_TABLE_RIGHT_RIGHT = 615,
|
||||
TURNTABLE_STICK = 616, // To Be Used on Java Side
|
||||
TURNTABLE_STICK_UP = 617,
|
||||
TURNTABLE_STICK_DOWN = 618,
|
||||
TURNTABLE_STICK_LEFT = 619,
|
||||
TURNTABLE_STICK_RIGHT = 620,
|
||||
TURNTABLE_EFFECT_DIAL = 621,
|
||||
TURNTABLE_CROSSFADE = 622, // To Be Used on Java Side
|
||||
TURNTABLE_CROSSFADE_LEFT = 623,
|
||||
TURNTABLE_CROSSFADE_RIGHT = 624,
|
||||
};
|
||||
enum ButtonState
|
||||
{
|
||||
BUTTON_RELEASED = 0,
|
||||
BUTTON_PRESSED = 1
|
||||
};
|
||||
enum BindType
|
||||
{
|
||||
BIND_BUTTON = 0,
|
||||
BIND_AXIS
|
||||
};
|
||||
class Button
|
||||
{
|
||||
private:
|
||||
ButtonState m_state;
|
||||
|
||||
~Button() {}
|
||||
};
|
||||
class Axis
|
||||
{
|
||||
private:
|
||||
float m_value;
|
||||
public:
|
||||
Axis() : m_value(0.0f) {}
|
||||
void SetValue(float value) { m_value = value; }
|
||||
float AxisValue() { return m_value; }
|
||||
public:
|
||||
Button() : m_state(BUTTON_RELEASED) {}
|
||||
void SetState(ButtonState state) { m_state = state; }
|
||||
bool Pressed() { return m_state == BUTTON_PRESSED; }
|
||||
~Button() {}
|
||||
};
|
||||
class Axis
|
||||
{
|
||||
private:
|
||||
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
|
||||
{
|
||||
const int _padID;
|
||||
const ButtonType _buttontype;
|
||||
const BindType _bindtype;
|
||||
const int _bind;
|
||||
const float _neg;
|
||||
sBind(int padID, ButtonType buttontype, BindType bindtype, int bind, float neg)
|
||||
: _padID(padID), _buttontype(buttontype), _bindtype(bindtype), _bind(bind), _neg(neg)
|
||||
{}
|
||||
};
|
||||
struct sBind
|
||||
{
|
||||
const int _padID;
|
||||
const ButtonType _buttontype;
|
||||
const BindType _bindtype;
|
||||
const int _bind;
|
||||
const float _neg;
|
||||
sBind(int padID, ButtonType buttontype, BindType bindtype, int bind, float 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
|
||||
{
|
||||
private:
|
||||
const std::string _dev;
|
||||
std::map<ButtonType, bool> _buttons;
|
||||
std::map<ButtonType, float> _axises;
|
||||
// Key is padID and ButtonType
|
||||
std::map<std::pair<int, ButtonType>, sBind*> _inputbinds;
|
||||
|
||||
// Key is padID and ButtonType
|
||||
std::map<std::pair<int, ButtonType>, sBind*> _inputbinds;
|
||||
public:
|
||||
InputDevice(std::string dev)
|
||||
: _dev(dev) {}
|
||||
~InputDevice()
|
||||
{
|
||||
for (const auto& bind : _inputbinds)
|
||||
delete bind.second;
|
||||
_inputbinds.clear();
|
||||
}
|
||||
void AddBind(sBind* bind) { _inputbinds[std::make_pair(bind->_padID, bind->_buttontype)] = bind; }
|
||||
bool PressEvent(int button, int action);
|
||||
void AxisEvent(int axis, float value);
|
||||
bool ButtonValue(int padID, ButtonType button);
|
||||
float AxisValue(int padID, ButtonType axis);
|
||||
};
|
||||
public:
|
||||
InputDevice(std::string dev) : _dev(dev) {}
|
||||
~InputDevice()
|
||||
{
|
||||
for (const auto& bind : _inputbinds)
|
||||
delete bind.second;
|
||||
_inputbinds.clear();
|
||||
}
|
||||
void AddBind(sBind* bind) { _inputbinds[std::make_pair(bind->_padID, bind->_buttontype)] = bind; }
|
||||
bool PressEvent(int button, int action);
|
||||
void AxisEvent(int axis, float value);
|
||||
bool ButtonValue(int padID, ButtonType button);
|
||||
float AxisValue(int padID, ButtonType axis);
|
||||
};
|
||||
|
||||
void Init();
|
||||
bool GetButtonPressed(int padID, ButtonType button);
|
||||
float GetAxisValue(int padID, ButtonType axis);
|
||||
bool GamepadEvent(const std::string& dev, int button, int action);
|
||||
void GamepadAxisEvent(const std::string& dev, int axis, float value);
|
||||
void Shutdown();
|
||||
void Init();
|
||||
bool GetButtonPressed(int padID, ButtonType button);
|
||||
float GetAxisValue(int padID, ButtonType axis);
|
||||
bool GamepadEvent(const std::string& dev, int button, int action);
|
||||
void GamepadAxisEvent(const std::string& dev, int axis, float value);
|
||||
void Shutdown();
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,77 +6,77 @@
|
|||
|
||||
#include "AudioCommon/AOSoundStream.h"
|
||||
#include "AudioCommon/Mixer.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#if defined(HAVE_AO) && HAVE_AO
|
||||
|
||||
void AOSound::SoundLoop()
|
||||
{
|
||||
Common::SetCurrentThreadName("Audio thread - ao");
|
||||
Common::SetCurrentThreadName("Audio thread - ao");
|
||||
|
||||
uint_32 numBytesToRender = 256;
|
||||
ao_initialize();
|
||||
default_driver = ao_default_driver_id();
|
||||
format.bits = 16;
|
||||
format.channels = 2;
|
||||
format.rate = m_mixer->GetSampleRate();
|
||||
format.byte_format = AO_FMT_LITTLE;
|
||||
uint_32 numBytesToRender = 256;
|
||||
ao_initialize();
|
||||
default_driver = ao_default_driver_id();
|
||||
format.bits = 16;
|
||||
format.channels = 2;
|
||||
format.rate = m_mixer->GetSampleRate();
|
||||
format.byte_format = AO_FMT_LITTLE;
|
||||
|
||||
device = ao_open_live(default_driver, &format, nullptr /* no options */);
|
||||
if (!device)
|
||||
{
|
||||
PanicAlertT("AudioCommon: Error opening AO device.\n");
|
||||
ao_shutdown();
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
device = ao_open_live(default_driver, &format, nullptr /* no options */);
|
||||
if (!device)
|
||||
{
|
||||
PanicAlertT("AudioCommon: Error opening AO device.\n");
|
||||
ao_shutdown();
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
buf_size = format.bits/8 * format.channels * format.rate;
|
||||
buf_size = format.bits / 8 * format.channels * format.rate;
|
||||
|
||||
while (m_run_thread.load())
|
||||
{
|
||||
m_mixer->Mix(realtimeBuffer, numBytesToRender >> 2);
|
||||
while (m_run_thread.load())
|
||||
{
|
||||
m_mixer->Mix(realtimeBuffer, numBytesToRender >> 2);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(soundCriticalSection);
|
||||
ao_play(device, (char*)realtimeBuffer, numBytesToRender);
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(soundCriticalSection);
|
||||
ao_play(device, (char*)realtimeBuffer, numBytesToRender);
|
||||
}
|
||||
|
||||
soundSyncEvent.Wait();
|
||||
}
|
||||
soundSyncEvent.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
bool AOSound::Start()
|
||||
{
|
||||
m_run_thread.store(true);
|
||||
memset(realtimeBuffer, 0, sizeof(realtimeBuffer));
|
||||
m_run_thread.store(true);
|
||||
memset(realtimeBuffer, 0, sizeof(realtimeBuffer));
|
||||
|
||||
thread = std::thread(&AOSound::SoundLoop, this);
|
||||
return true;
|
||||
thread = std::thread(&AOSound::SoundLoop, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AOSound::Update()
|
||||
{
|
||||
soundSyncEvent.Set();
|
||||
soundSyncEvent.Set();
|
||||
}
|
||||
|
||||
void AOSound::Stop()
|
||||
{
|
||||
m_run_thread.store(false);
|
||||
soundSyncEvent.Set();
|
||||
m_run_thread.store(false);
|
||||
soundSyncEvent.Set();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(soundCriticalSection);
|
||||
thread.join();
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(soundCriticalSection);
|
||||
thread.join();
|
||||
|
||||
if (device)
|
||||
ao_close(device);
|
||||
if (device)
|
||||
ao_close(device);
|
||||
|
||||
ao_shutdown();
|
||||
ao_shutdown();
|
||||
|
||||
device = nullptr;
|
||||
}
|
||||
device = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,29 +19,25 @@
|
|||
class AOSound final : public SoundStream
|
||||
{
|
||||
#if defined(HAVE_AO) && HAVE_AO
|
||||
std::thread thread;
|
||||
std::atomic<bool> m_run_thread;
|
||||
std::mutex soundCriticalSection;
|
||||
Common::Event soundSyncEvent;
|
||||
std::thread thread;
|
||||
std::atomic<bool> m_run_thread;
|
||||
std::mutex soundCriticalSection;
|
||||
Common::Event soundSyncEvent;
|
||||
|
||||
int buf_size;
|
||||
int buf_size;
|
||||
|
||||
ao_device *device;
|
||||
ao_sample_format format;
|
||||
int default_driver;
|
||||
ao_device* device;
|
||||
ao_sample_format format;
|
||||
int default_driver;
|
||||
|
||||
short realtimeBuffer[1024 * 1024];
|
||||
short realtimeBuffer[1024 * 1024];
|
||||
|
||||
public:
|
||||
bool Start() override;
|
||||
void SoundLoop() override;
|
||||
void Stop() override;
|
||||
void Update() override;
|
||||
|
||||
static bool isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool Start() override;
|
||||
void SoundLoop() override;
|
||||
void Stop() override;
|
||||
void Update() override;
|
||||
|
||||
static bool isValid() { return true; }
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -6,230 +6,229 @@
|
|||
|
||||
#include "AudioCommon/AlsaSoundStream.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Thread.h"
|
||||
|
||||
AlsaSound::AlsaSound()
|
||||
: m_thread_status(ALSAThreadStatus::STOPPED)
|
||||
, handle(nullptr)
|
||||
, frames_to_deliver(FRAME_COUNT_MIN)
|
||||
: m_thread_status(ALSAThreadStatus::STOPPED), handle(nullptr),
|
||||
frames_to_deliver(FRAME_COUNT_MIN)
|
||||
{
|
||||
}
|
||||
|
||||
bool AlsaSound::Start()
|
||||
{
|
||||
m_thread_status.store(ALSAThreadStatus::RUNNING);
|
||||
if (!AlsaInit())
|
||||
{
|
||||
m_thread_status.store(ALSAThreadStatus::STOPPED);
|
||||
return false;
|
||||
}
|
||||
m_thread_status.store(ALSAThreadStatus::RUNNING);
|
||||
if (!AlsaInit())
|
||||
{
|
||||
m_thread_status.store(ALSAThreadStatus::STOPPED);
|
||||
return false;
|
||||
}
|
||||
|
||||
thread = std::thread(&AlsaSound::SoundLoop, this);
|
||||
return true;
|
||||
thread = std::thread(&AlsaSound::SoundLoop, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AlsaSound::Stop()
|
||||
{
|
||||
m_thread_status.store(ALSAThreadStatus::STOPPING);
|
||||
m_thread_status.store(ALSAThreadStatus::STOPPING);
|
||||
|
||||
//Give the opportunity to the audio thread
|
||||
//to realize we are stopping the emulation
|
||||
cv.notify_one();
|
||||
thread.join();
|
||||
// Give the opportunity to the audio thread
|
||||
// to realize we are stopping the emulation
|
||||
cv.notify_one();
|
||||
thread.join();
|
||||
}
|
||||
|
||||
void AlsaSound::Update()
|
||||
{
|
||||
// don't need to do anything here.
|
||||
// don't need to do anything here.
|
||||
}
|
||||
|
||||
// Called on audio thread.
|
||||
void AlsaSound::SoundLoop()
|
||||
{
|
||||
Common::SetCurrentThreadName("Audio thread - alsa");
|
||||
while (m_thread_status.load() != ALSAThreadStatus::STOPPING)
|
||||
{
|
||||
while (m_thread_status.load() == ALSAThreadStatus::RUNNING)
|
||||
{
|
||||
m_mixer->Mix(mix_buffer, frames_to_deliver);
|
||||
int rc = snd_pcm_writei(handle, mix_buffer, frames_to_deliver);
|
||||
if (rc == -EPIPE)
|
||||
{
|
||||
// Underrun
|
||||
snd_pcm_prepare(handle);
|
||||
}
|
||||
else if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "writei fail: %s", snd_strerror(rc));
|
||||
}
|
||||
}
|
||||
if (m_thread_status.load() == ALSAThreadStatus::PAUSED)
|
||||
{
|
||||
snd_pcm_drop(handle); // Stop sound output
|
||||
Common::SetCurrentThreadName("Audio thread - alsa");
|
||||
while (m_thread_status.load() != ALSAThreadStatus::STOPPING)
|
||||
{
|
||||
while (m_thread_status.load() == ALSAThreadStatus::RUNNING)
|
||||
{
|
||||
m_mixer->Mix(mix_buffer, frames_to_deliver);
|
||||
int rc = snd_pcm_writei(handle, mix_buffer, frames_to_deliver);
|
||||
if (rc == -EPIPE)
|
||||
{
|
||||
// Underrun
|
||||
snd_pcm_prepare(handle);
|
||||
}
|
||||
else if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "writei fail: %s", snd_strerror(rc));
|
||||
}
|
||||
}
|
||||
if (m_thread_status.load() == ALSAThreadStatus::PAUSED)
|
||||
{
|
||||
snd_pcm_drop(handle); // Stop sound output
|
||||
|
||||
// Block until thread status changes.
|
||||
std::unique_lock<std::mutex> lock(cv_m);
|
||||
cv.wait(lock, [this]{ return m_thread_status.load() != ALSAThreadStatus::PAUSED; });
|
||||
// Block until thread status changes.
|
||||
std::unique_lock<std::mutex> lock(cv_m);
|
||||
cv.wait(lock, [this] { return m_thread_status.load() != ALSAThreadStatus::PAUSED; });
|
||||
|
||||
snd_pcm_prepare(handle); // resume sound output
|
||||
}
|
||||
}
|
||||
AlsaShutdown();
|
||||
m_thread_status.store(ALSAThreadStatus::STOPPED);
|
||||
snd_pcm_prepare(handle); // resume sound output
|
||||
}
|
||||
}
|
||||
AlsaShutdown();
|
||||
m_thread_status.store(ALSAThreadStatus::STOPPED);
|
||||
}
|
||||
|
||||
|
||||
void AlsaSound::Clear(bool muted)
|
||||
{
|
||||
m_muted = muted;
|
||||
m_thread_status.store(muted ? ALSAThreadStatus::PAUSED : ALSAThreadStatus::RUNNING);
|
||||
cv.notify_one(); // Notify thread that status has changed
|
||||
m_muted = muted;
|
||||
m_thread_status.store(muted ? ALSAThreadStatus::PAUSED : ALSAThreadStatus::RUNNING);
|
||||
cv.notify_one(); // Notify thread that status has changed
|
||||
}
|
||||
|
||||
bool AlsaSound::AlsaInit()
|
||||
{
|
||||
unsigned int sample_rate = m_mixer->GetSampleRate();
|
||||
int err;
|
||||
int dir;
|
||||
snd_pcm_sw_params_t *swparams;
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
snd_pcm_uframes_t buffer_size,buffer_size_max;
|
||||
unsigned int periods;
|
||||
unsigned int sample_rate = m_mixer->GetSampleRate();
|
||||
int err;
|
||||
int dir;
|
||||
snd_pcm_sw_params_t* swparams;
|
||||
snd_pcm_hw_params_t* hwparams;
|
||||
snd_pcm_uframes_t buffer_size, buffer_size_max;
|
||||
unsigned int periods;
|
||||
|
||||
err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Audio open error: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Audio open error: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
|
||||
err = snd_pcm_hw_params_any(handle, hwparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Broken configuration for this PCM: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
err = snd_pcm_hw_params_any(handle, hwparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Broken configuration for this PCM: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Access type not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Access type not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Sample format not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Sample format not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
dir = 0;
|
||||
err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &sample_rate, &dir);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Rate not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
dir = 0;
|
||||
err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &sample_rate, &dir);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Rate not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_channels(handle, hwparams, CHANNEL_COUNT);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Channels count not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
err = snd_pcm_hw_params_set_channels(handle, hwparams, CHANNEL_COUNT);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Channels count not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
periods = BUFFER_SIZE_MAX / FRAME_COUNT_MIN;
|
||||
err = snd_pcm_hw_params_set_periods_max(handle, hwparams, &periods, &dir);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Cannot set maximum periods per buffer: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
periods = BUFFER_SIZE_MAX / FRAME_COUNT_MIN;
|
||||
err = snd_pcm_hw_params_set_periods_max(handle, hwparams, &periods, &dir);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Cannot set maximum periods per buffer: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer_size_max = BUFFER_SIZE_MAX;
|
||||
err = snd_pcm_hw_params_set_buffer_size_max(handle, hwparams, &buffer_size_max);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Cannot set maximum buffer size: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
buffer_size_max = BUFFER_SIZE_MAX;
|
||||
err = snd_pcm_hw_params_set_buffer_size_max(handle, hwparams, &buffer_size_max);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Cannot set maximum buffer size: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params(handle, hwparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Unable to install hw params: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
err = snd_pcm_hw_params(handle, hwparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Unable to install hw params: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Cannot get buffer size: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Cannot get buffer size: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_get_periods_max(hwparams, &periods, &dir);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Cannot get periods: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
err = snd_pcm_hw_params_get_periods_max(hwparams, &periods, &dir);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Cannot get periods: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
//periods is the number of fragments alsa can wait for during one
|
||||
//buffer_size
|
||||
frames_to_deliver = buffer_size / periods;
|
||||
//limit the minimum size. pulseaudio advertises a minimum of 32 samples.
|
||||
if (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
|
||||
if ((unsigned int)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);
|
||||
// periods is the number of fragments alsa can wait for during one
|
||||
// buffer_size
|
||||
frames_to_deliver = buffer_size / periods;
|
||||
// limit the minimum size. pulseaudio advertises a minimum of 32 samples.
|
||||
if (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
|
||||
if ((unsigned int)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);
|
||||
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
|
||||
err = snd_pcm_sw_params_current(handle, swparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "cannot init sw params: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
err = snd_pcm_sw_params_current(handle, swparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "cannot init sw params: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "cannot set start thresh: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "cannot set start thresh: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_sw_params(handle, swparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "cannot set sw params: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
err = snd_pcm_sw_params(handle, swparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "cannot set sw params: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_prepare(handle);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Unable to prepare: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
NOTICE_LOG(AUDIO, "ALSA successfully initialized.\n");
|
||||
return true;
|
||||
err = snd_pcm_prepare(handle);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Unable to prepare: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
NOTICE_LOG(AUDIO, "ALSA successfully initialized.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void AlsaSound::AlsaShutdown()
|
||||
{
|
||||
if (handle != nullptr)
|
||||
{
|
||||
snd_pcm_drop(handle);
|
||||
snd_pcm_close(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
if (handle != nullptr)
|
||||
{
|
||||
snd_pcm_drop(handle);
|
||||
snd_pcm_close(handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,47 +20,43 @@ class AlsaSound final : public SoundStream
|
|||
{
|
||||
#if defined(HAVE_ALSA) && HAVE_ALSA
|
||||
public:
|
||||
AlsaSound();
|
||||
AlsaSound();
|
||||
|
||||
bool Start() override;
|
||||
void SoundLoop() override;
|
||||
void Stop() override;
|
||||
void Update() override;
|
||||
void Clear(bool) override;
|
||||
|
||||
static bool isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool Start() override;
|
||||
void SoundLoop() override;
|
||||
void Stop() override;
|
||||
void Update() override;
|
||||
void Clear(bool) override;
|
||||
|
||||
static bool isValid() { return true; }
|
||||
private:
|
||||
// maximum number of frames the buffer can hold
|
||||
static constexpr size_t BUFFER_SIZE_MAX = 8192;
|
||||
// maximum number of frames the buffer can hold
|
||||
static constexpr size_t BUFFER_SIZE_MAX = 8192;
|
||||
|
||||
// minimum number of frames to deliver in one transfer
|
||||
static constexpr u32 FRAME_COUNT_MIN = 256;
|
||||
// minimum number of frames to deliver in one transfer
|
||||
static constexpr u32 FRAME_COUNT_MIN = 256;
|
||||
|
||||
// number of channels per frame
|
||||
static constexpr u32 CHANNEL_COUNT = 2;
|
||||
// number of channels per frame
|
||||
static constexpr u32 CHANNEL_COUNT = 2;
|
||||
|
||||
enum class ALSAThreadStatus
|
||||
{
|
||||
RUNNING,
|
||||
PAUSED,
|
||||
STOPPING,
|
||||
STOPPED,
|
||||
};
|
||||
enum class ALSAThreadStatus
|
||||
{
|
||||
RUNNING,
|
||||
PAUSED,
|
||||
STOPPING,
|
||||
STOPPED,
|
||||
};
|
||||
|
||||
bool AlsaInit();
|
||||
void AlsaShutdown();
|
||||
bool AlsaInit();
|
||||
void AlsaShutdown();
|
||||
|
||||
s16 mix_buffer[BUFFER_SIZE_MAX * CHANNEL_COUNT];
|
||||
std::thread thread;
|
||||
std::atomic<ALSAThreadStatus> m_thread_status;
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
s16 mix_buffer[BUFFER_SIZE_MAX * CHANNEL_COUNT];
|
||||
std::thread thread;
|
||||
std::atomic<ALSAThreadStatus> m_thread_status;
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
|
||||
snd_pcm_t *handle;
|
||||
unsigned int frames_to_deliver;
|
||||
snd_pcm_t* handle;
|
||||
unsigned int frames_to_deliver;
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -2,22 +2,21 @@
|
|||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
||||
#include "AudioCommon/AlsaSoundStream.h"
|
||||
#include "AudioCommon/AOSoundStream.h"
|
||||
#include "AudioCommon/AudioCommon.h"
|
||||
#include "AudioCommon/AOSoundStream.h"
|
||||
#include "AudioCommon/AlsaSoundStream.h"
|
||||
#include "AudioCommon/CoreAudioSoundStream.h"
|
||||
#include "AudioCommon/Mixer.h"
|
||||
#include "AudioCommon/NullSoundStream.h"
|
||||
#include "AudioCommon/OpenALStream.h"
|
||||
#include "AudioCommon/OpenSLESStream.h"
|
||||
#include "AudioCommon/PulseAudioStream.h"
|
||||
#include "AudioCommon/XAudio2_7Stream.h"
|
||||
#include "AudioCommon/XAudio2Stream.h"
|
||||
#include "AudioCommon/XAudio2_7Stream.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Movie.h"
|
||||
|
||||
|
@ -28,182 +27,182 @@ static bool s_audio_dump_start = false;
|
|||
|
||||
namespace AudioCommon
|
||||
{
|
||||
static const int AUDIO_VOLUME_MIN = 0;
|
||||
static const int AUDIO_VOLUME_MAX = 100;
|
||||
static const int AUDIO_VOLUME_MIN = 0;
|
||||
static const int AUDIO_VOLUME_MAX = 100;
|
||||
|
||||
SoundStream* InitSoundStream()
|
||||
{
|
||||
std::string backend = SConfig::GetInstance().sBackend;
|
||||
if (backend == BACKEND_OPENAL && OpenALStream::isValid())
|
||||
g_sound_stream = new OpenALStream();
|
||||
else if (backend == BACKEND_NULLSOUND && NullSound::isValid())
|
||||
g_sound_stream = new NullSound();
|
||||
else if (backend == BACKEND_XAUDIO2)
|
||||
{
|
||||
if (XAudio2::isValid())
|
||||
g_sound_stream = new XAudio2();
|
||||
else if (XAudio2_7::isValid())
|
||||
g_sound_stream = new XAudio2_7();
|
||||
}
|
||||
else if (backend == BACKEND_AOSOUND && AOSound::isValid())
|
||||
g_sound_stream = new AOSound();
|
||||
else if (backend == BACKEND_ALSA && AlsaSound::isValid())
|
||||
g_sound_stream = new AlsaSound();
|
||||
else if (backend == BACKEND_COREAUDIO && CoreAudioSound::isValid())
|
||||
g_sound_stream = new CoreAudioSound();
|
||||
else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid())
|
||||
g_sound_stream = new PulseAudio();
|
||||
else if (backend == BACKEND_OPENSLES && OpenSLESStream::isValid())
|
||||
g_sound_stream = new OpenSLESStream();
|
||||
SoundStream* InitSoundStream()
|
||||
{
|
||||
std::string backend = SConfig::GetInstance().sBackend;
|
||||
if (backend == BACKEND_OPENAL && OpenALStream::isValid())
|
||||
g_sound_stream = new OpenALStream();
|
||||
else if (backend == BACKEND_NULLSOUND && NullSound::isValid())
|
||||
g_sound_stream = new NullSound();
|
||||
else if (backend == BACKEND_XAUDIO2)
|
||||
{
|
||||
if (XAudio2::isValid())
|
||||
g_sound_stream = new XAudio2();
|
||||
else if (XAudio2_7::isValid())
|
||||
g_sound_stream = new XAudio2_7();
|
||||
}
|
||||
else if (backend == BACKEND_AOSOUND && AOSound::isValid())
|
||||
g_sound_stream = new AOSound();
|
||||
else if (backend == BACKEND_ALSA && AlsaSound::isValid())
|
||||
g_sound_stream = new AlsaSound();
|
||||
else if (backend == BACKEND_COREAUDIO && CoreAudioSound::isValid())
|
||||
g_sound_stream = new CoreAudioSound();
|
||||
else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid())
|
||||
g_sound_stream = new PulseAudio();
|
||||
else if (backend == BACKEND_OPENSLES && OpenSLESStream::isValid())
|
||||
g_sound_stream = new OpenSLESStream();
|
||||
|
||||
if (!g_sound_stream && NullSound::isValid())
|
||||
{
|
||||
WARN_LOG(AUDIO, "Could not initialize backend %s, using %s instead.",
|
||||
backend.c_str(), BACKEND_NULLSOUND);
|
||||
g_sound_stream = new NullSound();
|
||||
}
|
||||
if (!g_sound_stream && NullSound::isValid())
|
||||
{
|
||||
WARN_LOG(AUDIO, "Could not initialize backend %s, using %s instead.", backend.c_str(),
|
||||
BACKEND_NULLSOUND);
|
||||
g_sound_stream = new NullSound();
|
||||
}
|
||||
|
||||
if (g_sound_stream)
|
||||
{
|
||||
UpdateSoundStream();
|
||||
if (!g_sound_stream->Start())
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Could not start backend %s, using %s instead",
|
||||
backend.c_str(), BACKEND_NULLSOUND);
|
||||
delete g_sound_stream;
|
||||
g_sound_stream = new NullSound();
|
||||
g_sound_stream->Start();
|
||||
}
|
||||
if (g_sound_stream)
|
||||
{
|
||||
UpdateSoundStream();
|
||||
if (!g_sound_stream->Start())
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Could not start backend %s, using %s instead", backend.c_str(),
|
||||
BACKEND_NULLSOUND);
|
||||
delete g_sound_stream;
|
||||
g_sound_stream = new NullSound();
|
||||
g_sound_stream->Start();
|
||||
}
|
||||
|
||||
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
|
||||
StartAudioDump();
|
||||
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
|
||||
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;
|
||||
g_sound_stream = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ShutdownSoundStream()
|
||||
{
|
||||
INFO_LOG(AUDIO, "Shutting down sound stream");
|
||||
|
||||
if (g_sound_stream)
|
||||
{
|
||||
g_sound_stream->Stop();
|
||||
if (SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
|
||||
StopAudioDump();
|
||||
delete g_sound_stream;
|
||||
g_sound_stream = nullptr;
|
||||
}
|
||||
|
||||
INFO_LOG(AUDIO, "Done shutting down sound stream");
|
||||
}
|
||||
|
||||
std::vector<std::string> GetSoundBackends()
|
||||
{
|
||||
std::vector<std::string> backends;
|
||||
|
||||
if (NullSound::isValid())
|
||||
backends.push_back(BACKEND_NULLSOUND);
|
||||
if (XAudio2_7::isValid() || XAudio2::isValid())
|
||||
backends.push_back(BACKEND_XAUDIO2);
|
||||
if (AOSound::isValid())
|
||||
backends.push_back(BACKEND_AOSOUND);
|
||||
if (AlsaSound::isValid())
|
||||
backends.push_back(BACKEND_ALSA);
|
||||
if (CoreAudioSound::isValid())
|
||||
backends.push_back(BACKEND_COREAUDIO);
|
||||
if (PulseAudio::isValid())
|
||||
backends.push_back(BACKEND_PULSEAUDIO);
|
||||
if (OpenALStream::isValid())
|
||||
backends.push_back(BACKEND_OPENAL);
|
||||
if (OpenSLESStream::isValid())
|
||||
backends.push_back(BACKEND_OPENSLES);
|
||||
return backends;
|
||||
}
|
||||
|
||||
void UpdateSoundStream()
|
||||
{
|
||||
if (g_sound_stream)
|
||||
{
|
||||
int volume = SConfig::GetInstance().m_IsMuted ? 0 : SConfig::GetInstance().m_Volume;
|
||||
g_sound_stream->SetVolume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearAudioBuffer(bool mute)
|
||||
{
|
||||
if (g_sound_stream)
|
||||
g_sound_stream->Clear(mute);
|
||||
}
|
||||
|
||||
void SendAIBuffer(short *samples, unsigned int num_samples)
|
||||
{
|
||||
if (!g_sound_stream)
|
||||
return;
|
||||
|
||||
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
|
||||
StartAudioDump();
|
||||
else if (!SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
|
||||
StopAudioDump();
|
||||
|
||||
CMixer* pMixer = g_sound_stream->GetMixer();
|
||||
|
||||
if (pMixer && samples)
|
||||
{
|
||||
pMixer->PushSamples(samples, num_samples);
|
||||
}
|
||||
|
||||
g_sound_stream->Update();
|
||||
}
|
||||
|
||||
void StartAudioDump()
|
||||
{
|
||||
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";
|
||||
File::CreateFullPath(audio_file_name_dtk);
|
||||
File::CreateFullPath(audio_file_name_dsp);
|
||||
g_sound_stream->GetMixer()->StartLogDTKAudio(audio_file_name_dtk);
|
||||
g_sound_stream->GetMixer()->StartLogDSPAudio(audio_file_name_dsp);
|
||||
s_audio_dump_start = true;
|
||||
}
|
||||
|
||||
void StopAudioDump()
|
||||
{
|
||||
g_sound_stream->GetMixer()->StopLogDTKAudio();
|
||||
g_sound_stream->GetMixer()->StopLogDSPAudio();
|
||||
s_audio_dump_start = false;
|
||||
}
|
||||
|
||||
void IncreaseVolume(unsigned short offset)
|
||||
{
|
||||
SConfig::GetInstance().m_IsMuted = false;
|
||||
int& currentVolume = SConfig::GetInstance().m_Volume;
|
||||
currentVolume += offset;
|
||||
if (currentVolume > AUDIO_VOLUME_MAX)
|
||||
currentVolume = AUDIO_VOLUME_MAX;
|
||||
UpdateSoundStream();
|
||||
}
|
||||
|
||||
void DecreaseVolume(unsigned short offset)
|
||||
{
|
||||
SConfig::GetInstance().m_IsMuted = false;
|
||||
int& currentVolume = SConfig::GetInstance().m_Volume;
|
||||
currentVolume -= offset;
|
||||
if (currentVolume < AUDIO_VOLUME_MIN)
|
||||
currentVolume = AUDIO_VOLUME_MIN;
|
||||
UpdateSoundStream();
|
||||
}
|
||||
|
||||
void ToggleMuteVolume()
|
||||
{
|
||||
bool& isMuted = SConfig::GetInstance().m_IsMuted;
|
||||
isMuted = !isMuted;
|
||||
UpdateSoundStream();
|
||||
}
|
||||
delete g_sound_stream;
|
||||
g_sound_stream = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ShutdownSoundStream()
|
||||
{
|
||||
INFO_LOG(AUDIO, "Shutting down sound stream");
|
||||
|
||||
if (g_sound_stream)
|
||||
{
|
||||
g_sound_stream->Stop();
|
||||
if (SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
|
||||
StopAudioDump();
|
||||
delete g_sound_stream;
|
||||
g_sound_stream = nullptr;
|
||||
}
|
||||
|
||||
INFO_LOG(AUDIO, "Done shutting down sound stream");
|
||||
}
|
||||
|
||||
std::vector<std::string> GetSoundBackends()
|
||||
{
|
||||
std::vector<std::string> backends;
|
||||
|
||||
if (NullSound::isValid())
|
||||
backends.push_back(BACKEND_NULLSOUND);
|
||||
if (XAudio2_7::isValid() || XAudio2::isValid())
|
||||
backends.push_back(BACKEND_XAUDIO2);
|
||||
if (AOSound::isValid())
|
||||
backends.push_back(BACKEND_AOSOUND);
|
||||
if (AlsaSound::isValid())
|
||||
backends.push_back(BACKEND_ALSA);
|
||||
if (CoreAudioSound::isValid())
|
||||
backends.push_back(BACKEND_COREAUDIO);
|
||||
if (PulseAudio::isValid())
|
||||
backends.push_back(BACKEND_PULSEAUDIO);
|
||||
if (OpenALStream::isValid())
|
||||
backends.push_back(BACKEND_OPENAL);
|
||||
if (OpenSLESStream::isValid())
|
||||
backends.push_back(BACKEND_OPENSLES);
|
||||
return backends;
|
||||
}
|
||||
|
||||
void UpdateSoundStream()
|
||||
{
|
||||
if (g_sound_stream)
|
||||
{
|
||||
int volume = SConfig::GetInstance().m_IsMuted ? 0 : SConfig::GetInstance().m_Volume;
|
||||
g_sound_stream->SetVolume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearAudioBuffer(bool mute)
|
||||
{
|
||||
if (g_sound_stream)
|
||||
g_sound_stream->Clear(mute);
|
||||
}
|
||||
|
||||
void SendAIBuffer(short* samples, unsigned int num_samples)
|
||||
{
|
||||
if (!g_sound_stream)
|
||||
return;
|
||||
|
||||
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
|
||||
StartAudioDump();
|
||||
else if (!SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
|
||||
StopAudioDump();
|
||||
|
||||
CMixer* pMixer = g_sound_stream->GetMixer();
|
||||
|
||||
if (pMixer && samples)
|
||||
{
|
||||
pMixer->PushSamples(samples, num_samples);
|
||||
}
|
||||
|
||||
g_sound_stream->Update();
|
||||
}
|
||||
|
||||
void StartAudioDump()
|
||||
{
|
||||
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";
|
||||
File::CreateFullPath(audio_file_name_dtk);
|
||||
File::CreateFullPath(audio_file_name_dsp);
|
||||
g_sound_stream->GetMixer()->StartLogDTKAudio(audio_file_name_dtk);
|
||||
g_sound_stream->GetMixer()->StartLogDSPAudio(audio_file_name_dsp);
|
||||
s_audio_dump_start = true;
|
||||
}
|
||||
|
||||
void StopAudioDump()
|
||||
{
|
||||
g_sound_stream->GetMixer()->StopLogDTKAudio();
|
||||
g_sound_stream->GetMixer()->StopLogDSPAudio();
|
||||
s_audio_dump_start = false;
|
||||
}
|
||||
|
||||
void IncreaseVolume(unsigned short offset)
|
||||
{
|
||||
SConfig::GetInstance().m_IsMuted = false;
|
||||
int& currentVolume = SConfig::GetInstance().m_Volume;
|
||||
currentVolume += offset;
|
||||
if (currentVolume > AUDIO_VOLUME_MAX)
|
||||
currentVolume = AUDIO_VOLUME_MAX;
|
||||
UpdateSoundStream();
|
||||
}
|
||||
|
||||
void DecreaseVolume(unsigned short offset)
|
||||
{
|
||||
SConfig::GetInstance().m_IsMuted = false;
|
||||
int& currentVolume = SConfig::GetInstance().m_Volume;
|
||||
currentVolume -= offset;
|
||||
if (currentVolume < AUDIO_VOLUME_MIN)
|
||||
currentVolume = AUDIO_VOLUME_MIN;
|
||||
UpdateSoundStream();
|
||||
}
|
||||
|
||||
void ToggleMuteVolume()
|
||||
{
|
||||
bool& isMuted = SConfig::GetInstance().m_IsMuted;
|
||||
isMuted = !isMuted;
|
||||
UpdateSoundStream();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,22 +7,21 @@
|
|||
#include "AudioCommon/SoundStream.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
|
||||
class CMixer;
|
||||
|
||||
extern SoundStream *g_sound_stream;
|
||||
extern SoundStream* g_sound_stream;
|
||||
|
||||
namespace AudioCommon
|
||||
{
|
||||
SoundStream* InitSoundStream();
|
||||
void ShutdownSoundStream();
|
||||
std::vector<std::string> GetSoundBackends();
|
||||
void UpdateSoundStream();
|
||||
void ClearAudioBuffer(bool mute);
|
||||
void SendAIBuffer(short* samples, unsigned int num_samples);
|
||||
void StartAudioDump();
|
||||
void StopAudioDump();
|
||||
void IncreaseVolume(unsigned short offset);
|
||||
void DecreaseVolume(unsigned short offset);
|
||||
void ToggleMuteVolume();
|
||||
SoundStream* InitSoundStream();
|
||||
void ShutdownSoundStream();
|
||||
std::vector<std::string> GetSoundBackends();
|
||||
void UpdateSoundStream();
|
||||
void ClearAudioBuffer(bool mute);
|
||||
void SendAIBuffer(short* samples, unsigned int num_samples);
|
||||
void StartAudioDump();
|
||||
void StopAudioDump();
|
||||
void IncreaseVolume(unsigned short offset);
|
||||
void DecreaseVolume(unsigned short offset);
|
||||
void ToggleMuteVolume();
|
||||
}
|
||||
|
|
|
@ -7,105 +7,94 @@
|
|||
#include "AudioCommon/CoreAudioSoundStream.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
OSStatus CoreAudioSound::callback(void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames, AudioBufferList *ioData)
|
||||
OSStatus CoreAudioSound::callback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames, AudioBufferList* ioData)
|
||||
{
|
||||
for (UInt32 i = 0; i < ioData->mNumberBuffers; i++)
|
||||
((CoreAudioSound *)inRefCon)->m_mixer->
|
||||
Mix((short *)ioData->mBuffers[i].mData,
|
||||
ioData->mBuffers[i].mDataByteSize / 4);
|
||||
for (UInt32 i = 0; i < ioData->mNumberBuffers; i++)
|
||||
((CoreAudioSound*)inRefCon)
|
||||
->m_mixer->Mix((short*)ioData->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize / 4);
|
||||
|
||||
return noErr;
|
||||
return noErr;
|
||||
}
|
||||
|
||||
bool CoreAudioSound::Start()
|
||||
{
|
||||
OSStatus err;
|
||||
AURenderCallbackStruct callback_struct;
|
||||
AudioStreamBasicDescription format;
|
||||
AudioComponentDescription desc;
|
||||
AudioComponent component;
|
||||
OSStatus err;
|
||||
AURenderCallbackStruct callback_struct;
|
||||
AudioStreamBasicDescription format;
|
||||
AudioComponentDescription desc;
|
||||
AudioComponent component;
|
||||
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
component = AudioComponentFindNext(nullptr, &desc);
|
||||
if (component == nullptr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error finding audio component");
|
||||
return false;
|
||||
}
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
component = AudioComponentFindNext(nullptr, &desc);
|
||||
if (component == nullptr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error finding audio component");
|
||||
return false;
|
||||
}
|
||||
|
||||
err = AudioComponentInstanceNew(component, &audioUnit);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error opening audio component");
|
||||
return false;
|
||||
}
|
||||
err = AudioComponentInstanceNew(component, &audioUnit);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error opening audio component");
|
||||
return false;
|
||||
}
|
||||
|
||||
FillOutASBDForLPCM(format, m_mixer->GetSampleRate(),
|
||||
2, 16, 16, false, false, false);
|
||||
err = AudioUnitSetProperty(audioUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input, 0, &format,
|
||||
sizeof(AudioStreamBasicDescription));
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error setting audio format");
|
||||
return false;
|
||||
}
|
||||
FillOutASBDForLPCM(format, m_mixer->GetSampleRate(), 2, 16, 16, false, false, false);
|
||||
err = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0,
|
||||
&format, sizeof(AudioStreamBasicDescription));
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error setting audio format");
|
||||
return false;
|
||||
}
|
||||
|
||||
callback_struct.inputProc = callback;
|
||||
callback_struct.inputProcRefCon = this;
|
||||
err = AudioUnitSetProperty(audioUnit,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, 0, &callback_struct,
|
||||
sizeof callback_struct);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error setting audio callback");
|
||||
return false;
|
||||
}
|
||||
callback_struct.inputProc = callback;
|
||||
callback_struct.inputProcRefCon = this;
|
||||
err = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
|
||||
0, &callback_struct, sizeof callback_struct);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error setting audio callback");
|
||||
return false;
|
||||
}
|
||||
|
||||
err = AudioUnitSetParameter(audioUnit,
|
||||
kHALOutputParam_Volume,
|
||||
kAudioUnitScope_Output, 0,
|
||||
m_volume / 100., 0);
|
||||
if (err != noErr)
|
||||
ERROR_LOG(AUDIO, "error setting volume");
|
||||
err = AudioUnitSetParameter(audioUnit, kHALOutputParam_Volume, kAudioUnitScope_Output, 0,
|
||||
m_volume / 100., 0);
|
||||
if (err != noErr)
|
||||
ERROR_LOG(AUDIO, "error setting volume");
|
||||
|
||||
err = AudioUnitInitialize(audioUnit);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error initializing audiounit");
|
||||
return false;
|
||||
}
|
||||
err = AudioUnitInitialize(audioUnit);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error initializing audiounit");
|
||||
return false;
|
||||
}
|
||||
|
||||
err = AudioOutputUnitStart(audioUnit);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error starting audiounit");
|
||||
return false;
|
||||
}
|
||||
err = AudioOutputUnitStart(audioUnit);
|
||||
if (err != noErr)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "error starting audiounit");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CoreAudioSound::SetVolume(int volume)
|
||||
{
|
||||
OSStatus err;
|
||||
m_volume = volume;
|
||||
OSStatus err;
|
||||
m_volume = volume;
|
||||
|
||||
err = AudioUnitSetParameter(audioUnit,
|
||||
kHALOutputParam_Volume,
|
||||
kAudioUnitScope_Output, 0,
|
||||
volume / 100., 0);
|
||||
if (err != noErr)
|
||||
ERROR_LOG(AUDIO, "error setting volume");
|
||||
err = AudioUnitSetParameter(audioUnit, kHALOutputParam_Volume, kAudioUnitScope_Output, 0,
|
||||
volume / 100., 0);
|
||||
if (err != noErr)
|
||||
ERROR_LOG(AUDIO, "error setting volume");
|
||||
}
|
||||
|
||||
void CoreAudioSound::SoundLoop()
|
||||
|
@ -114,19 +103,19 @@ void CoreAudioSound::SoundLoop()
|
|||
|
||||
void CoreAudioSound::Stop()
|
||||
{
|
||||
OSStatus err;
|
||||
OSStatus err;
|
||||
|
||||
err = AudioOutputUnitStop(audioUnit);
|
||||
if (err != noErr)
|
||||
ERROR_LOG(AUDIO, "error stopping audiounit");
|
||||
err = AudioOutputUnitStop(audioUnit);
|
||||
if (err != noErr)
|
||||
ERROR_LOG(AUDIO, "error stopping audiounit");
|
||||
|
||||
err = AudioUnitUninitialize(audioUnit);
|
||||
if (err != noErr)
|
||||
ERROR_LOG(AUDIO, "error uninitializing audiounit");
|
||||
err = AudioUnitUninitialize(audioUnit);
|
||||
if (err != noErr)
|
||||
ERROR_LOG(AUDIO, "error uninitializing audiounit");
|
||||
|
||||
err = AudioComponentInstanceDispose(audioUnit);
|
||||
if (err != noErr)
|
||||
ERROR_LOG(AUDIO, "error closing audio component");
|
||||
err = AudioComponentInstanceDispose(audioUnit);
|
||||
if (err != noErr)
|
||||
ERROR_LOG(AUDIO, "error closing audio component");
|
||||
}
|
||||
|
||||
void CoreAudioSound::Update()
|
||||
|
|
|
@ -14,25 +14,19 @@ class CoreAudioSound final : public SoundStream
|
|||
{
|
||||
#ifdef __APPLE__
|
||||
public:
|
||||
bool Start() override;
|
||||
void SetVolume(int volume) override;
|
||||
void SoundLoop() override;
|
||||
void Stop() override;
|
||||
void Update() override;
|
||||
|
||||
static bool isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool Start() override;
|
||||
void SetVolume(int volume) override;
|
||||
void SoundLoop() override;
|
||||
void Stop() override;
|
||||
void Update() override;
|
||||
|
||||
static bool isValid() { return true; }
|
||||
private:
|
||||
AudioUnit audioUnit;
|
||||
int m_volume;
|
||||
AudioUnit audioUnit;
|
||||
int m_volume;
|
||||
|
||||
static OSStatus callback(void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData);
|
||||
static OSStatus callback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames, AudioBufferList* ioData);
|
||||
#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 float LFE_buf[256];
|
||||
static unsigned int lfe_pos;
|
||||
static float *filter_coefs_lfe;
|
||||
static float* filter_coefs_lfe;
|
||||
static unsigned int len125;
|
||||
|
||||
template<class T, class _ftype_t>
|
||||
static _ftype_t DotProduct(int count, const T *buf, const _ftype_t *coefficients)
|
||||
template <class T, class _ftype_t>
|
||||
static _ftype_t DotProduct(int count, const T* buf, const _ftype_t* coefficients)
|
||||
{
|
||||
int i;
|
||||
float sum0 = 0.0f, sum1 = 0.0f, sum2 = 0.0f, sum3 = 0.0f;
|
||||
int i;
|
||||
float sum0 = 0.0f, sum1 = 0.0f, sum2 = 0.0f, sum3 = 0.0f;
|
||||
|
||||
// Unrolled loop
|
||||
for (i = 0; (i + 3) < count; i += 4)
|
||||
{
|
||||
sum0 += buf[i + 0] * coefficients[i + 0];
|
||||
sum1 += buf[i + 1] * coefficients[i + 1];
|
||||
sum2 += buf[i + 2] * coefficients[i + 2];
|
||||
sum3 += buf[i + 3] * coefficients[i + 3];
|
||||
}
|
||||
// Unrolled loop
|
||||
for (i = 0; (i + 3) < count; i += 4)
|
||||
{
|
||||
sum0 += buf[i + 0] * coefficients[i + 0];
|
||||
sum1 += buf[i + 1] * coefficients[i + 1];
|
||||
sum2 += buf[i + 2] * coefficients[i + 2];
|
||||
sum3 += buf[i + 3] * coefficients[i + 3];
|
||||
}
|
||||
|
||||
// Epilogue of unrolled loop
|
||||
for (; i < count; i++)
|
||||
sum0 += buf[i] * coefficients[i];
|
||||
// Epilogue of unrolled loop
|
||||
for (; i < count; i++)
|
||||
sum0 += buf[i] * coefficients[i];
|
||||
|
||||
return sum0 + sum1 + sum2 + sum3;
|
||||
return sum0 + sum1 + sum2 + sum3;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static T FIRFilter(const T *buf, int pos, int len, int count, const float *coefficients)
|
||||
template <class T>
|
||||
static T FIRFilter(const T* buf, int pos, int len, int count, const float* coefficients)
|
||||
{
|
||||
int count1, count2;
|
||||
int count1, count2;
|
||||
|
||||
if (pos >= count)
|
||||
{
|
||||
pos -= count;
|
||||
count1 = count; count2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
count2 = pos;
|
||||
count1 = count - pos;
|
||||
pos = len - count1;
|
||||
}
|
||||
if (pos >= count)
|
||||
{
|
||||
pos -= count;
|
||||
count1 = count;
|
||||
count2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
count2 = pos;
|
||||
count1 = count - pos;
|
||||
pos = len - count1;
|
||||
}
|
||||
|
||||
// high part of window
|
||||
const T *ptr = &buf[pos];
|
||||
// high part of window
|
||||
const T* ptr = &buf[pos];
|
||||
|
||||
float r1 = DotProduct(count1, ptr, coefficients); coefficients += count1;
|
||||
float r2 = DotProduct(count2, buf, coefficients);
|
||||
return T(r1 + r2);
|
||||
float r1 = DotProduct(count1, ptr, coefficients);
|
||||
coefficients += count1;
|
||||
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)
|
||||
{
|
||||
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
|
||||
for (int i = 0; i < n; i++)
|
||||
*w++ = float(0.54 - 0.46*cos(k*(float)i));
|
||||
// Calculate window coefficients
|
||||
for (int i = 0; i < n; 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
|
||||
*/
|
||||
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 end = ((*n + 1) >> 1) - o; // Loop end
|
||||
unsigned int o = *n & 1; // Indicator for odd filter length
|
||||
unsigned int end = ((*n + 1) >> 1) - o; // Loop end
|
||||
|
||||
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 g = 0.0f; // Gain
|
||||
float t1; // Temporary variables
|
||||
float fc1; // Cutoff frequencies
|
||||
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 g = 0.0f; // Gain
|
||||
float t1; // Temporary variables
|
||||
float fc1; // Cutoff frequencies
|
||||
|
||||
// Sanity check
|
||||
if (*n == 0)
|
||||
return nullptr;
|
||||
// Sanity check
|
||||
if (*n == 0)
|
||||
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
|
||||
Hamming(*n, w);
|
||||
// Get window coefficients
|
||||
Hamming(*n, w);
|
||||
|
||||
fc1 = *fc;
|
||||
// Cutoff frequency must be < 0.5 where 0.5 <=> Fs/2
|
||||
fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1 / 2 : 0.25f;
|
||||
k1 *= fc1;
|
||||
fc1 = *fc;
|
||||
// Cutoff frequency must be < 0.5 where 0.5 <=> Fs/2
|
||||
fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1 / 2 : 0.25f;
|
||||
k1 *= fc1;
|
||||
|
||||
// Low pass filter
|
||||
// Low pass filter
|
||||
|
||||
// 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,
|
||||
// where x is zero. To make sure nothing strange happens, we set this
|
||||
// value separately.
|
||||
if (o)
|
||||
{
|
||||
w[end] = fc1 * w[end] * 2.0f;
|
||||
g = w[end];
|
||||
}
|
||||
// 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,
|
||||
// where x is zero. To make sure nothing strange happens, we set this
|
||||
// value separately.
|
||||
if (o)
|
||||
{
|
||||
w[end] = fc1 * w[end] * 2.0f;
|
||||
g = w[end];
|
||||
}
|
||||
|
||||
// Create filter
|
||||
for (u32 i = 0; i < end; i++)
|
||||
{
|
||||
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
|
||||
g += 2*w[end - i - 1]; // Total gain in filter
|
||||
}
|
||||
// Create filter
|
||||
for (u32 i = 0; i < end; i++)
|
||||
{
|
||||
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
|
||||
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
|
||||
g = 1/g;
|
||||
for (u32 i = 0; i < *n; i++)
|
||||
w[i] *= g;
|
||||
|
||||
return w;
|
||||
return w;
|
||||
}
|
||||
|
||||
static void OnSeek()
|
||||
{
|
||||
l_fwr = r_fwr = lpr_fwr = lmr_fwr = 0;
|
||||
std::fill(fwrbuf_l.begin(), fwrbuf_l.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;
|
||||
std::fill(lf.begin(), lf.end(), 0.0f);
|
||||
std::fill(rf.begin(), rf.end(), 0.0f);
|
||||
std::fill(lr.begin(), lr.end(), 0.0f);
|
||||
std::fill(rr.begin(), rr.end(), 0.0f);
|
||||
std::fill(cf.begin(), cf.end(), 0.0f);
|
||||
std::fill(cr.begin(), cr.end(), 0.0f);
|
||||
lfe_pos = 0;
|
||||
memset(LFE_buf, 0, sizeof(LFE_buf));
|
||||
l_fwr = r_fwr = lpr_fwr = lmr_fwr = 0;
|
||||
std::fill(fwrbuf_l.begin(), fwrbuf_l.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;
|
||||
std::fill(lf.begin(), lf.end(), 0.0f);
|
||||
std::fill(rf.begin(), rf.end(), 0.0f);
|
||||
std::fill(lr.begin(), lr.end(), 0.0f);
|
||||
std::fill(rr.begin(), rr.end(), 0.0f);
|
||||
std::fill(cf.begin(), cf.end(), 0.0f);
|
||||
std::fill(cr.begin(), cr.end(), 0.0f);
|
||||
lfe_pos = 0;
|
||||
memset(LFE_buf, 0, sizeof(LFE_buf));
|
||||
}
|
||||
|
||||
static void Done()
|
||||
{
|
||||
OnSeek();
|
||||
OnSeek();
|
||||
|
||||
if (filter_coefs_lfe)
|
||||
{
|
||||
free(filter_coefs_lfe);
|
||||
}
|
||||
if (filter_coefs_lfe)
|
||||
{
|
||||
free(filter_coefs_lfe);
|
||||
}
|
||||
|
||||
filter_coefs_lfe = nullptr;
|
||||
filter_coefs_lfe = nullptr;
|
||||
}
|
||||
|
||||
static float* CalculateCoefficients125HzLowpass(int rate)
|
||||
{
|
||||
len125 = 256;
|
||||
float f = 125.0f / (rate / 2);
|
||||
float *coeffs = DesignFIR(&len125, &f, 0);
|
||||
static const float M3_01DB = 0.7071067812f;
|
||||
for (unsigned int i = 0; i < len125; i++)
|
||||
{
|
||||
coeffs[i] *= M3_01DB;
|
||||
}
|
||||
return coeffs;
|
||||
len125 = 256;
|
||||
float f = 125.0f / (rate / 2);
|
||||
float* coeffs = DesignFIR(&len125, &f, 0);
|
||||
static const float M3_01DB = 0.7071067812f;
|
||||
for (unsigned int i = 0; i < len125; i++)
|
||||
{
|
||||
coeffs[i] *= M3_01DB;
|
||||
}
|
||||
return coeffs;
|
||||
}
|
||||
|
||||
static float PassiveLock(float x)
|
||||
{
|
||||
static const float MATAGCLOCK = 0.2f; /* AGC range (around 1) where the matrix behaves passively */
|
||||
const float x1 = x - 1;
|
||||
const float ax1s = fabs(x - 1) * (1.0f / MATAGCLOCK);
|
||||
return x1 - x1 / (1 + ax1s * ax1s) + 1;
|
||||
static const float MATAGCLOCK =
|
||||
0.2f; /* AGC range (around 1) where the matrix behaves passively */
|
||||
const float x1 = x - 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,
|
||||
const int ir, bool decode_rear,
|
||||
const int _dlbuflen,
|
||||
float _l_fwr, float _r_fwr,
|
||||
float _lpr_fwr, float _lmr_fwr,
|
||||
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 void MatrixDecode(const float* in, const int k, const int il, const int ir, bool decode_rear,
|
||||
const int _dlbuflen, float _l_fwr, float _r_fwr, float _lpr_fwr,
|
||||
float _lmr_fwr, 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 MATAGCTRIG = 8.0f; /* (Fuzzy) AGC trigger */
|
||||
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 M9_03DB = 0.3535533906f;
|
||||
static const float MATAGCTRIG = 8.0f; /* (Fuzzy) AGC trigger */
|
||||
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. */
|
||||
|
||||
const int kr = (k + olddelay) % _dlbuflen;
|
||||
float l_gain = (_l_fwr + _r_fwr) / (1 + _l_fwr + _l_fwr);
|
||||
float r_gain = (_l_fwr + _r_fwr) / (1 + _r_fwr + _r_fwr);
|
||||
// The 2nd axis has strong gain fluctuations, and therefore require
|
||||
// limits. The factor corresponds to the 1 / amplification of (Lt
|
||||
// - Rt) when (Lt, Rt) is strongly correlated. (e.g. during
|
||||
// dialogues). It should be bigger than -12 dB to prevent
|
||||
// distortion.
|
||||
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 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 lpr, lmr;
|
||||
float l_agc, r_agc, lpr_agc, lmr_agc;
|
||||
float f, d_gain, c_gain, c_agc_cfk;
|
||||
const int kr = (k + olddelay) % _dlbuflen;
|
||||
float l_gain = (_l_fwr + _r_fwr) / (1 + _l_fwr + _l_fwr);
|
||||
float r_gain = (_l_fwr + _r_fwr) / (1 + _r_fwr + _r_fwr);
|
||||
// The 2nd axis has strong gain fluctuations, and therefore require
|
||||
// limits. The factor corresponds to the 1 / amplification of (Lt
|
||||
// - Rt) when (Lt, Rt) is strongly correlated. (e.g. during
|
||||
// dialogues). It should be bigger than -12 dB to prevent
|
||||
// distortion.
|
||||
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 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 lpr, lmr;
|
||||
float l_agc, r_agc, lpr_agc, lmr_agc;
|
||||
float f, d_gain, c_gain, c_agc_cfk;
|
||||
|
||||
/*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/
|
||||
/* AGC adaption */
|
||||
d_gain = (fabs(l_gain - *_adapt_l_gain) + fabs(r_gain - *_adapt_r_gain)) * 0.5f;
|
||||
f = d_gain * (1.0f / MATAGCTRIG);
|
||||
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
|
||||
*_adapt_l_gain = (1 - f) * *_adapt_l_gain + f * l_gain;
|
||||
*_adapt_r_gain = (1 - f) * *_adapt_r_gain + f * r_gain;
|
||||
/* Matrix */
|
||||
l_agc = in[il] * PassiveLock(*_adapt_l_gain);
|
||||
r_agc = in[ir] * PassiveLock(*_adapt_r_gain);
|
||||
_cf[k] = (l_agc + r_agc) * (float)M_SQRT1_2;
|
||||
if (decode_rear)
|
||||
{
|
||||
_lr[kr] = _rr[kr] = (l_agc - r_agc) * (float)M_SQRT1_2;
|
||||
// Stereo rear channel is steered with the same AGC steering as
|
||||
// the decoding matrix. Note this requires a fast updating AGC
|
||||
// at the order of 20 ms (which is the case here).
|
||||
_lr[kr] *= (_l_fwr + _l_fwr) / (1 + _l_fwr + _r_fwr);
|
||||
_rr[kr] *= (_r_fwr + _r_fwr) / (1 + _l_fwr + _r_fwr);
|
||||
}
|
||||
/*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/
|
||||
/* AGC adaption */
|
||||
d_gain = (fabs(l_gain - *_adapt_l_gain) + fabs(r_gain - *_adapt_r_gain)) * 0.5f;
|
||||
f = d_gain * (1.0f / MATAGCTRIG);
|
||||
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
|
||||
*_adapt_l_gain = (1 - f) * *_adapt_l_gain + f * l_gain;
|
||||
*_adapt_r_gain = (1 - f) * *_adapt_r_gain + f * r_gain;
|
||||
/* Matrix */
|
||||
l_agc = in[il] * PassiveLock(*_adapt_l_gain);
|
||||
r_agc = in[ir] * PassiveLock(*_adapt_r_gain);
|
||||
_cf[k] = (l_agc + r_agc) * (float)M_SQRT1_2;
|
||||
if (decode_rear)
|
||||
{
|
||||
_lr[kr] = _rr[kr] = (l_agc - r_agc) * (float)M_SQRT1_2;
|
||||
// Stereo rear channel is steered with the same AGC steering as
|
||||
// the decoding matrix. Note this requires a fast updating AGC
|
||||
// at the order of 20 ms (which is the case here).
|
||||
_lr[kr] *= (_l_fwr + _l_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) ***/
|
||||
lpr = (in[il] + in[ir]) * (float)M_SQRT1_2;
|
||||
lmr = (in[il] - in[ir]) * (float)M_SQRT1_2;
|
||||
/* AGC adaption */
|
||||
d_gain = fabs(lmr_unlim_gain - *_adapt_lmr_gain);
|
||||
f = d_gain * (1.0f / MATAGCTRIG);
|
||||
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
|
||||
*_adapt_lpr_gain = (1 - f) * *_adapt_lpr_gain + f * lpr_gain;
|
||||
*_adapt_lmr_gain = (1 - f) * *_adapt_lmr_gain + f * lmr_gain;
|
||||
/* Matrix */
|
||||
lpr_agc = lpr * PassiveLock(*_adapt_lpr_gain);
|
||||
lmr_agc = lmr * PassiveLock(*_adapt_lmr_gain);
|
||||
_lf[k] = (lpr_agc + lmr_agc) * (float)M_SQRT1_2;
|
||||
_rf[k] = (lpr_agc - lmr_agc) * (float)M_SQRT1_2;
|
||||
/*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/
|
||||
lpr = (in[il] + in[ir]) * (float)M_SQRT1_2;
|
||||
lmr = (in[il] - in[ir]) * (float)M_SQRT1_2;
|
||||
/* AGC adaption */
|
||||
d_gain = fabs(lmr_unlim_gain - *_adapt_lmr_gain);
|
||||
f = d_gain * (1.0f / MATAGCTRIG);
|
||||
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
|
||||
*_adapt_lpr_gain = (1 - f) * *_adapt_lpr_gain + f * lpr_gain;
|
||||
*_adapt_lmr_gain = (1 - f) * *_adapt_lmr_gain + f * lmr_gain;
|
||||
/* Matrix */
|
||||
lpr_agc = lpr * PassiveLock(*_adapt_lpr_gain);
|
||||
lmr_agc = lmr * PassiveLock(*_adapt_lmr_gain);
|
||||
_lf[k] = (lpr_agc + lmr_agc) * (float)M_SQRT1_2;
|
||||
_rf[k] = (lpr_agc - lmr_agc) * (float)M_SQRT1_2;
|
||||
|
||||
/*** CENTER FRONT CANCELLATION ***/
|
||||
// A heuristic approach exploits that Lt + Rt gain contains the
|
||||
// information about Lt, Rt correlation. This effectively reshapes
|
||||
// the front and rear "cones" to concentrate Lt + Rt to C and
|
||||
// introduce Lt - Rt in L, R.
|
||||
/* 0.67677 is the empirical lower bound for lpr_gain. */
|
||||
c_gain = 8 * (*_adapt_lpr_gain - 0.67677f);
|
||||
c_gain = c_gain > 0 ? c_gain : 0;
|
||||
// c_gain should not be too high, not even reaching full
|
||||
// cancellation (~ 0.50 - 0.55 at current AGC implementation), or
|
||||
// the center will sound too narrow. */
|
||||
c_gain = MATCOMPGAIN / (1 + c_gain * c_gain);
|
||||
c_agc_cfk = c_gain * _cf[k];
|
||||
_lf[k] -= c_agc_cfk;
|
||||
_rf[k] -= c_agc_cfk;
|
||||
_cf[k] += c_agc_cfk + c_agc_cfk;
|
||||
/*** CENTER FRONT CANCELLATION ***/
|
||||
// A heuristic approach exploits that Lt + Rt gain contains the
|
||||
// information about Lt, Rt correlation. This effectively reshapes
|
||||
// the front and rear "cones" to concentrate Lt + Rt to C and
|
||||
// introduce Lt - Rt in L, R.
|
||||
/* 0.67677 is the empirical lower bound for lpr_gain. */
|
||||
c_gain = 8 * (*_adapt_lpr_gain - 0.67677f);
|
||||
c_gain = c_gain > 0 ? c_gain : 0;
|
||||
// c_gain should not be too high, not even reaching full
|
||||
// cancellation (~ 0.50 - 0.55 at current AGC implementation), or
|
||||
// the center will sound too narrow. */
|
||||
c_gain = MATCOMPGAIN / (1 + c_gain * c_gain);
|
||||
c_agc_cfk = c_gain * _cf[k];
|
||||
_lf[k] -= c_agc_cfk;
|
||||
_rf[k] -= 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 int cfg_delay = 0;
|
||||
static const unsigned int fmt_freq = 48000;
|
||||
static const unsigned int fmt_nchannels = 2; // input channels
|
||||
static const unsigned int FWRDURATION = 240; // FWR average duration (samples)
|
||||
static const int cfg_delay = 0;
|
||||
static const unsigned int fmt_freq = 48000;
|
||||
static const unsigned int fmt_nchannels = 2; // input channels
|
||||
|
||||
int cur = 0;
|
||||
int cur = 0;
|
||||
|
||||
if (olddelay != cfg_delay || oldfreq != fmt_freq)
|
||||
{
|
||||
Done();
|
||||
olddelay = cfg_delay;
|
||||
oldfreq = fmt_freq;
|
||||
dlbuflen = std::max(FWRDURATION, (fmt_freq * cfg_delay / 1000)); //+(len7000-1);
|
||||
cyc_pos = dlbuflen - 1;
|
||||
fwrbuf_l.resize(dlbuflen);
|
||||
fwrbuf_r.resize(dlbuflen);
|
||||
lf.resize(dlbuflen);
|
||||
rf.resize(dlbuflen);
|
||||
lr.resize(dlbuflen);
|
||||
rr.resize(dlbuflen);
|
||||
cf.resize(dlbuflen);
|
||||
cr.resize(dlbuflen);
|
||||
filter_coefs_lfe = CalculateCoefficients125HzLowpass(fmt_freq);
|
||||
lfe_pos = 0;
|
||||
memset(LFE_buf, 0, sizeof(LFE_buf));
|
||||
}
|
||||
if (olddelay != cfg_delay || oldfreq != fmt_freq)
|
||||
{
|
||||
Done();
|
||||
olddelay = cfg_delay;
|
||||
oldfreq = fmt_freq;
|
||||
dlbuflen = std::max(FWRDURATION, (fmt_freq * cfg_delay / 1000)); //+(len7000-1);
|
||||
cyc_pos = dlbuflen - 1;
|
||||
fwrbuf_l.resize(dlbuflen);
|
||||
fwrbuf_r.resize(dlbuflen);
|
||||
lf.resize(dlbuflen);
|
||||
rf.resize(dlbuflen);
|
||||
lr.resize(dlbuflen);
|
||||
rr.resize(dlbuflen);
|
||||
cf.resize(dlbuflen);
|
||||
cr.resize(dlbuflen);
|
||||
filter_coefs_lfe = CalculateCoefficients125HzLowpass(fmt_freq);
|
||||
lfe_pos = 0;
|
||||
memset(LFE_buf, 0, sizeof(LFE_buf));
|
||||
}
|
||||
|
||||
float *in = samples; // Input audio data
|
||||
float *end = in + numsamples * fmt_nchannels; // Loop end
|
||||
float* in = samples; // Input audio data
|
||||
float* end = in + numsamples * fmt_nchannels; // Loop end
|
||||
|
||||
while (in < end)
|
||||
{
|
||||
const int k = cyc_pos;
|
||||
while (in < end)
|
||||
{
|
||||
const int k = cyc_pos;
|
||||
|
||||
const int fwr_pos = (k + FWRDURATION) % dlbuflen;
|
||||
/* Update the full wave rectified total amplitude */
|
||||
/* Input matrix decoder */
|
||||
l_fwr += fabs(in[0]) - fabs(fwrbuf_l[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]);
|
||||
lmr_fwr += fabs(in[0] - in[1]) - fabs(fwrbuf_l[fwr_pos] - fwrbuf_r[fwr_pos]);
|
||||
const int fwr_pos = (k + FWRDURATION) % dlbuflen;
|
||||
/* Update the full wave rectified total amplitude */
|
||||
/* Input matrix decoder */
|
||||
l_fwr += fabs(in[0]) - fabs(fwrbuf_l[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]);
|
||||
lmr_fwr += fabs(in[0] - in[1]) - fabs(fwrbuf_l[fwr_pos] - fwrbuf_r[fwr_pos]);
|
||||
|
||||
/* Matrix encoded 2 channel sources */
|
||||
fwrbuf_l[k] = in[0];
|
||||
fwrbuf_r[k] = in[1];
|
||||
MatrixDecode(in, k, 0, 1, true, dlbuflen,
|
||||
l_fwr, r_fwr,
|
||||
lpr_fwr, lmr_fwr,
|
||||
&adapt_l_gain, &adapt_r_gain,
|
||||
&adapt_lpr_gain, &adapt_lmr_gain,
|
||||
&lf[0], &rf[0], &lr[0], &rr[0], &cf[0]);
|
||||
/* Matrix encoded 2 channel sources */
|
||||
fwrbuf_l[k] = in[0];
|
||||
fwrbuf_r[k] = in[1];
|
||||
MatrixDecode(in, k, 0, 1, true, dlbuflen, l_fwr, r_fwr, lpr_fwr, lmr_fwr, &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 + 1] = rf[k];
|
||||
out[cur + 2] = cf[k];
|
||||
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);
|
||||
lfe_pos++;
|
||||
if (lfe_pos == len125)
|
||||
{
|
||||
lfe_pos = 0;
|
||||
}
|
||||
out[cur + 4] = lr[k];
|
||||
out[cur + 5] = rr[k];
|
||||
// Next sample...
|
||||
in += 2;
|
||||
cur += 6;
|
||||
cyc_pos--;
|
||||
if (cyc_pos < 0)
|
||||
{
|
||||
cyc_pos += dlbuflen;
|
||||
}
|
||||
}
|
||||
out[cur + 0] = lf[k];
|
||||
out[cur + 1] = rf[k];
|
||||
out[cur + 2] = cf[k];
|
||||
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);
|
||||
lfe_pos++;
|
||||
if (lfe_pos == len125)
|
||||
{
|
||||
lfe_pos = 0;
|
||||
}
|
||||
out[cur + 4] = lr[k];
|
||||
out[cur + 5] = rr[k];
|
||||
// Next sample...
|
||||
in += 2;
|
||||
cur += 6;
|
||||
cyc_pos--;
|
||||
if (cyc_pos < 0)
|
||||
{
|
||||
cyc_pos += dlbuflen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DPL2Reset()
|
||||
{
|
||||
olddelay = -1;
|
||||
oldfreq = 0;
|
||||
filter_coefs_lfe = nullptr;
|
||||
olddelay = -1;
|
||||
oldfreq = 0;
|
||||
filter_coefs_lfe = nullptr;
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
void DPL2Decode(float *samples, int numsamples, float *out);
|
||||
void DPL2Decode(float* samples, int numsamples, float* out);
|
||||
void DPL2Reset();
|
||||
|
|
|
@ -8,18 +8,17 @@
|
|||
#include "AudioCommon/Mixer.h"
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
#if _M_SSE >= 0x301 && !(defined __GNUC__ && !defined __SSSE3__)
|
||||
#include <tmmintrin.h>
|
||||
#endif
|
||||
|
||||
CMixer::CMixer(unsigned int BackendSampleRate)
|
||||
: m_sampleRate(BackendSampleRate)
|
||||
CMixer::CMixer(unsigned int BackendSampleRate) : m_sampleRate(BackendSampleRate)
|
||||
{
|
||||
INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized");
|
||||
INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized");
|
||||
}
|
||||
|
||||
CMixer::~CMixer()
|
||||
|
@ -27,249 +26,253 @@ CMixer::~CMixer()
|
|||
}
|
||||
|
||||
// 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
|
||||
// This is the only function changing the read value, so it's safe to
|
||||
// cache it locally although it's written here.
|
||||
// The writing pointer will be modified outside, but it will only increase,
|
||||
// so we will just ignore new written data while interpolating.
|
||||
// Without this cache, the compiler wouldn't be allowed to optimize the
|
||||
// interpolation loop.
|
||||
u32 indexR = m_indexR.load();
|
||||
u32 indexW = m_indexW.load();
|
||||
// Cache access in non-volatile variable
|
||||
// This is the only function changing the read value, so it's safe to
|
||||
// cache it locally although it's written here.
|
||||
// The writing pointer will be modified outside, but it will only increase,
|
||||
// so we will just ignore new written data while interpolating.
|
||||
// Without this cache, the compiler wouldn't be allowed to optimize the
|
||||
// interpolation loop.
|
||||
u32 indexR = m_indexR.load();
|
||||
u32 indexW = m_indexW.load();
|
||||
|
||||
u32 low_waterwark = m_input_sample_rate * SConfig::GetInstance().iTimingVariance / 1000;
|
||||
low_waterwark = std::min(low_waterwark, MAX_SAMPLES / 2);
|
||||
u32 low_waterwark = m_input_sample_rate * SConfig::GetInstance().iTimingVariance / 1000;
|
||||
low_waterwark = std::min(low_waterwark, MAX_SAMPLES / 2);
|
||||
|
||||
float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2);
|
||||
m_numLeftI = (numLeft + m_numLeftI*(CONTROL_AVG-1)) / CONTROL_AVG;
|
||||
float offset = (m_numLeftI - low_waterwark) * CONTROL_FACTOR;
|
||||
if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT;
|
||||
if (offset < -MAX_FREQ_SHIFT) offset = -MAX_FREQ_SHIFT;
|
||||
float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2);
|
||||
m_numLeftI = (numLeft + m_numLeftI * (CONTROL_AVG - 1)) / CONTROL_AVG;
|
||||
float offset = (m_numLeftI - low_waterwark) * CONTROL_FACTOR;
|
||||
if (offset > MAX_FREQ_SHIFT)
|
||||
offset = MAX_FREQ_SHIFT;
|
||||
if (offset < -MAX_FREQ_SHIFT)
|
||||
offset = -MAX_FREQ_SHIFT;
|
||||
|
||||
//render numleft sample pairs to samples[]
|
||||
//advance indexR with sample position
|
||||
//remember fractional offset
|
||||
// render numleft sample pairs to samples[]
|
||||
// advance indexR with sample position
|
||||
// remember fractional offset
|
||||
|
||||
float emulationspeed = SConfig::GetInstance().m_EmulationSpeed;
|
||||
float aid_sample_rate = m_input_sample_rate + offset;
|
||||
if (consider_framelimit && emulationspeed > 0.0f)
|
||||
{
|
||||
aid_sample_rate = aid_sample_rate * emulationspeed;
|
||||
}
|
||||
float emulationspeed = SConfig::GetInstance().m_EmulationSpeed;
|
||||
float aid_sample_rate = m_input_sample_rate + offset;
|
||||
if (consider_framelimit && emulationspeed > 0.0f)
|
||||
{
|
||||
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 rvolume = m_RVolume.load();
|
||||
s32 lvolume = m_LVolume.load();
|
||||
s32 rvolume = m_RVolume.load();
|
||||
|
||||
// TODO: consider a higher-quality resampling algorithm.
|
||||
for (; currentSample < numSamples * 2 && ((indexW-indexR) & INDEX_MASK) > 2; currentSample += 2)
|
||||
{
|
||||
u32 indexR2 = indexR + 2; //next sample
|
||||
// TODO: consider a higher-quality resampling algorithm.
|
||||
for (; currentSample < numSamples * 2 && ((indexW - indexR) & INDEX_MASK) > 2; currentSample += 2)
|
||||
{
|
||||
u32 indexR2 = indexR + 2; // next sample
|
||||
|
||||
s16 l1 = Common::swap16(m_buffer[indexR & INDEX_MASK]); //current
|
||||
s16 l2 = Common::swap16(m_buffer[indexR2 & INDEX_MASK]); //next
|
||||
int sampleL = ((l1 << 16) + (l2 - l1) * (u16)m_frac) >> 16;
|
||||
sampleL = (sampleL * lvolume) >> 8;
|
||||
sampleL += samples[currentSample + 1];
|
||||
samples[currentSample + 1] = MathUtil::Clamp(sampleL, -32767, 32767);
|
||||
s16 l1 = Common::swap16(m_buffer[indexR & INDEX_MASK]); // current
|
||||
s16 l2 = Common::swap16(m_buffer[indexR2 & INDEX_MASK]); // next
|
||||
int sampleL = ((l1 << 16) + (l2 - l1) * (u16)m_frac) >> 16;
|
||||
sampleL = (sampleL * lvolume) >> 8;
|
||||
sampleL += samples[currentSample + 1];
|
||||
samples[currentSample + 1] = MathUtil::Clamp(sampleL, -32767, 32767);
|
||||
|
||||
s16 r1 = Common::swap16(m_buffer[(indexR + 1) & INDEX_MASK]); //current
|
||||
s16 r2 = Common::swap16(m_buffer[(indexR2 + 1) & INDEX_MASK]); //next
|
||||
int sampleR = ((r1 << 16) + (r2 - r1) * (u16)m_frac) >> 16;
|
||||
sampleR = (sampleR * rvolume) >> 8;
|
||||
sampleR += samples[currentSample];
|
||||
samples[currentSample] = MathUtil::Clamp(sampleR, -32767, 32767);
|
||||
s16 r1 = Common::swap16(m_buffer[(indexR + 1) & INDEX_MASK]); // current
|
||||
s16 r2 = Common::swap16(m_buffer[(indexR2 + 1) & INDEX_MASK]); // next
|
||||
int sampleR = ((r1 << 16) + (r2 - r1) * (u16)m_frac) >> 16;
|
||||
sampleR = (sampleR * rvolume) >> 8;
|
||||
sampleR += samples[currentSample];
|
||||
samples[currentSample] = MathUtil::Clamp(sampleR, -32767, 32767);
|
||||
|
||||
m_frac += ratio;
|
||||
indexR += 2 * (u16)(m_frac >> 16);
|
||||
m_frac &= 0xffff;
|
||||
}
|
||||
m_frac += ratio;
|
||||
indexR += 2 * (u16)(m_frac >> 16);
|
||||
m_frac &= 0xffff;
|
||||
}
|
||||
|
||||
// Padding
|
||||
short s[2];
|
||||
s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]);
|
||||
s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]);
|
||||
s[0] = (s[0] * rvolume) >> 8;
|
||||
s[1] = (s[1] * lvolume) >> 8;
|
||||
for (; currentSample < numSamples * 2; currentSample += 2)
|
||||
{
|
||||
int sampleR = MathUtil::Clamp(s[0] + samples[currentSample + 0], -32767, 32767);
|
||||
int sampleL = MathUtil::Clamp(s[1] + samples[currentSample + 1], -32767, 32767);
|
||||
// Padding
|
||||
short s[2];
|
||||
s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]);
|
||||
s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]);
|
||||
s[0] = (s[0] * rvolume) >> 8;
|
||||
s[1] = (s[1] * lvolume) >> 8;
|
||||
for (; currentSample < numSamples * 2; currentSample += 2)
|
||||
{
|
||||
int sampleR = MathUtil::Clamp(s[0] + samples[currentSample + 0], -32767, 32767);
|
||||
int sampleL = MathUtil::Clamp(s[1] + samples[currentSample + 1], -32767, 32767);
|
||||
|
||||
samples[currentSample + 0] = sampleR;
|
||||
samples[currentSample + 1] = sampleL;
|
||||
}
|
||||
samples[currentSample + 0] = sampleR;
|
||||
samples[currentSample + 1] = sampleL;
|
||||
}
|
||||
|
||||
// Flush cached variable
|
||||
m_indexR.store(indexR);
|
||||
// Flush cached variable
|
||||
m_indexR.store(indexR);
|
||||
|
||||
return numSamples;
|
||||
return numSamples;
|
||||
}
|
||||
|
||||
unsigned int CMixer::Mix(short* samples, unsigned int num_samples, bool consider_framelimit)
|
||||
{
|
||||
if (!samples)
|
||||
return 0;
|
||||
if (!samples)
|
||||
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_streaming_mixer.Mix(samples, num_samples, consider_framelimit);
|
||||
m_wiimote_speaker_mixer.Mix(samples, num_samples, consider_framelimit);
|
||||
return num_samples;
|
||||
m_dma_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);
|
||||
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
|
||||
// indexR isn't allowed to cache in the audio throttling loop as it
|
||||
// needs to get updates to not deadlock.
|
||||
u32 indexW = m_indexW.load();
|
||||
// Cache access in non-volatile variable
|
||||
// indexR isn't allowed to cache in the audio throttling loop as it
|
||||
// needs to get updates to not deadlock.
|
||||
u32 indexW = m_indexW.load();
|
||||
|
||||
// Check if we have enough free space
|
||||
// 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)
|
||||
return;
|
||||
// Check if we have enough free space
|
||||
// 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)
|
||||
return;
|
||||
|
||||
// AyuanX: Actual re-sampling work has been moved to sound thread
|
||||
// to alleviate the workload on main thread
|
||||
// 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);
|
||||
if (over_bytes > 0)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4);
|
||||
}
|
||||
// AyuanX: Actual re-sampling work has been moved to sound thread
|
||||
// to alleviate the workload on main thread
|
||||
// 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);
|
||||
if (over_bytes > 0)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
if (m_log_dsp_audio)
|
||||
m_wave_writer_dsp.AddStereoSamplesBE(samples, num_samples);
|
||||
m_dma_mixer.PushSamples(samples, num_samples);
|
||||
if (m_log_dsp_audio)
|
||||
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);
|
||||
if (m_log_dtk_audio)
|
||||
m_wave_writer_dtk.AddStereoSamplesBE(samples, num_samples);
|
||||
m_streaming_mixer.PushSamples(samples, num_samples);
|
||||
if (m_log_dtk_audio)
|
||||
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)
|
||||
{
|
||||
m_wiimote_speaker_mixer.SetInputSampleRate(sample_rate);
|
||||
if (num_samples < MAX_SAMPLES)
|
||||
{
|
||||
m_wiimote_speaker_mixer.SetInputSampleRate(sample_rate);
|
||||
|
||||
for (unsigned int i = 0; i < num_samples; ++i)
|
||||
{
|
||||
samples_stereo[i * 2] = Common::swap16(samples[i]);
|
||||
samples_stereo[i * 2 + 1] = Common::swap16(samples[i]);
|
||||
}
|
||||
for (unsigned int i = 0; i < num_samples; ++i)
|
||||
{
|
||||
samples_stereo[i * 2] = 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)
|
||||
{
|
||||
m_dma_mixer.SetInputSampleRate(rate);
|
||||
m_dma_mixer.SetInputSampleRate(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)
|
||||
{
|
||||
m_streaming_mixer.SetVolume(lvolume, rvolume);
|
||||
m_streaming_mixer.SetVolume(lvolume, 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)
|
||||
{
|
||||
if (!m_log_dtk_audio)
|
||||
{
|
||||
m_log_dtk_audio = true;
|
||||
m_wave_writer_dtk.Start(filename, 48000);
|
||||
m_wave_writer_dtk.SetSkipSilence(false);
|
||||
NOTICE_LOG(AUDIO, "Starting DTK Audio logging");
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(AUDIO, "DTK Audio logging has already been started");
|
||||
}
|
||||
if (!m_log_dtk_audio)
|
||||
{
|
||||
m_log_dtk_audio = true;
|
||||
m_wave_writer_dtk.Start(filename, 48000);
|
||||
m_wave_writer_dtk.SetSkipSilence(false);
|
||||
NOTICE_LOG(AUDIO, "Starting DTK Audio logging");
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(AUDIO, "DTK Audio logging has already been started");
|
||||
}
|
||||
}
|
||||
|
||||
void CMixer::StopLogDTKAudio()
|
||||
{
|
||||
if (m_log_dtk_audio)
|
||||
{
|
||||
m_log_dtk_audio = false;
|
||||
m_wave_writer_dtk.Stop();
|
||||
NOTICE_LOG(AUDIO, "Stopping DTK Audio logging");
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(AUDIO, "DTK Audio logging has already been stopped");
|
||||
}
|
||||
if (m_log_dtk_audio)
|
||||
{
|
||||
m_log_dtk_audio = false;
|
||||
m_wave_writer_dtk.Stop();
|
||||
NOTICE_LOG(AUDIO, "Stopping DTK Audio logging");
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(AUDIO, "DTK Audio logging has already been stopped");
|
||||
}
|
||||
}
|
||||
|
||||
void CMixer::StartLogDSPAudio(const std::string& filename)
|
||||
{
|
||||
if (!m_log_dsp_audio)
|
||||
{
|
||||
m_log_dsp_audio = true;
|
||||
m_wave_writer_dsp.Start(filename, 32000);
|
||||
m_wave_writer_dsp.SetSkipSilence(false);
|
||||
NOTICE_LOG(AUDIO, "Starting DSP Audio logging");
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(AUDIO, "DSP Audio logging has already been started");
|
||||
}
|
||||
if (!m_log_dsp_audio)
|
||||
{
|
||||
m_log_dsp_audio = true;
|
||||
m_wave_writer_dsp.Start(filename, 32000);
|
||||
m_wave_writer_dsp.SetSkipSilence(false);
|
||||
NOTICE_LOG(AUDIO, "Starting DSP Audio logging");
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(AUDIO, "DSP Audio logging has already been started");
|
||||
}
|
||||
}
|
||||
|
||||
void CMixer::StopLogDSPAudio()
|
||||
{
|
||||
if (m_log_dsp_audio)
|
||||
{
|
||||
m_log_dsp_audio = false;
|
||||
m_wave_writer_dsp.Stop();
|
||||
NOTICE_LOG(AUDIO, "Stopping DSP Audio logging");
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(AUDIO, "DSP Audio logging has already been stopped");
|
||||
}
|
||||
if (m_log_dsp_audio)
|
||||
{
|
||||
m_log_dsp_audio = false;
|
||||
m_wave_writer_dsp.Stop();
|
||||
NOTICE_LOG(AUDIO, "Stopping DSP Audio logging");
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(AUDIO, "DSP Audio logging has already been stopped");
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
m_LVolume.store(lvolume + (lvolume >> 7));
|
||||
m_RVolume.store(rvolume + (rvolume >> 7));
|
||||
m_LVolume.store(lvolume + (lvolume >> 7));
|
||||
m_RVolume.store(rvolume + (rvolume >> 7));
|
||||
}
|
||||
|
|
|
@ -13,74 +13,73 @@
|
|||
class CMixer final
|
||||
{
|
||||
public:
|
||||
explicit CMixer(unsigned int BackendSampleRate);
|
||||
~CMixer();
|
||||
explicit CMixer(unsigned int BackendSampleRate);
|
||||
~CMixer();
|
||||
|
||||
// Called from audio threads
|
||||
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
|
||||
// Called from audio threads
|
||||
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
|
||||
|
||||
// Called from main thread
|
||||
void PushSamples(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);
|
||||
unsigned int GetSampleRate() const { return m_sampleRate; }
|
||||
// Called from main thread
|
||||
void PushSamples(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);
|
||||
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 SetStreamInputSampleRate(unsigned int rate);
|
||||
void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume);
|
||||
void SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume);
|
||||
void StartLogDTKAudio(const std::string& filename);
|
||||
void StopLogDTKAudio();
|
||||
|
||||
void StartLogDTKAudio(const std::string& filename);
|
||||
void StopLogDTKAudio();
|
||||
|
||||
void StartLogDSPAudio(const std::string& filename);
|
||||
void StopLogDSPAudio();
|
||||
|
||||
float GetCurrentSpeed() const { return m_speed.load(); }
|
||||
void UpdateSpeed(float val) { m_speed.store(val); }
|
||||
void StartLogDSPAudio(const std::string& filename);
|
||||
void StopLogDSPAudio();
|
||||
|
||||
float GetCurrentSpeed() const { return m_speed.load(); }
|
||||
void UpdateSpeed(float val) { m_speed.store(val); }
|
||||
private:
|
||||
static constexpr u32 MAX_SAMPLES = 1024 * 4; // 128 ms
|
||||
static constexpr u32 INDEX_MASK = MAX_SAMPLES * 2 - 1;
|
||||
static constexpr int MAX_FREQ_SHIFT = 200; // Per 32000 Hz
|
||||
static constexpr float CONTROL_FACTOR = 0.2f;
|
||||
static constexpr u32 CONTROL_AVG = 32; // In freq_shift per FIFO size offset
|
||||
static constexpr u32 MAX_SAMPLES = 1024 * 4; // 128 ms
|
||||
static constexpr u32 INDEX_MASK = MAX_SAMPLES * 2 - 1;
|
||||
static constexpr int MAX_FREQ_SHIFT = 200; // Per 32000 Hz
|
||||
static constexpr float CONTROL_FACTOR = 0.2f;
|
||||
static constexpr u32 CONTROL_AVG = 32; // In freq_shift per FIFO size offset
|
||||
|
||||
class MixerFifo final
|
||||
{
|
||||
public:
|
||||
MixerFifo(CMixer* mixer, unsigned sample_rate)
|
||||
: m_mixer(mixer)
|
||||
, m_input_sample_rate(sample_rate)
|
||||
{
|
||||
}
|
||||
void PushSamples(const short* samples, unsigned int num_samples);
|
||||
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
|
||||
void SetInputSampleRate(unsigned int rate);
|
||||
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;
|
||||
class MixerFifo final
|
||||
{
|
||||
public:
|
||||
MixerFifo(CMixer* mixer, unsigned sample_rate)
|
||||
: m_mixer(mixer), m_input_sample_rate(sample_rate)
|
||||
{
|
||||
}
|
||||
void PushSamples(const short* samples, unsigned int num_samples);
|
||||
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
|
||||
void SetInputSampleRate(unsigned int rate);
|
||||
void SetVolume(unsigned int lvolume, unsigned int rvolume);
|
||||
|
||||
WaveFileWriter m_wave_writer_dtk;
|
||||
WaveFileWriter m_wave_writer_dsp;
|
||||
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;
|
||||
|
||||
bool m_log_dtk_audio = false;
|
||||
bool m_log_dsp_audio = false;
|
||||
WaveFileWriter m_wave_writer_dtk;
|
||||
WaveFileWriter m_wave_writer_dsp;
|
||||
|
||||
// Current rate of emulation (1.0 = 100% speed)
|
||||
std::atomic<float> m_speed{0.0f};
|
||||
bool m_log_dtk_audio = false;
|
||||
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()
|
||||
{
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void NullSound::SetVolume(int volume)
|
||||
|
@ -22,19 +22,22 @@ void NullSound::SetVolume(int volume)
|
|||
|
||||
void NullSound::Update()
|
||||
{
|
||||
// num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD.
|
||||
constexpr u32 stereo_16_bit_size = 4;
|
||||
constexpr u32 dma_length = 32;
|
||||
const u64 audio_dma_period = 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();
|
||||
// num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD.
|
||||
constexpr u32 stereo_16_bit_size = 4;
|
||||
constexpr u32 dma_length = 32;
|
||||
const u64 audio_dma_period =
|
||||
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)
|
||||
{
|
||||
m_muted = mute;
|
||||
m_muted = mute;
|
||||
}
|
||||
|
||||
void NullSound::Stop()
|
||||
|
|
|
@ -10,18 +10,17 @@
|
|||
class NullSound final : public SoundStream
|
||||
{
|
||||
public:
|
||||
bool Start() override;
|
||||
void SoundLoop() override;
|
||||
void SetVolume(int volume) override;
|
||||
void Stop() override;
|
||||
void Clear(bool mute) override;
|
||||
void Update() override;
|
||||
|
||||
static bool isValid() { return true; }
|
||||
bool Start() override;
|
||||
void SoundLoop() override;
|
||||
void SetVolume(int volume) override;
|
||||
void Stop() override;
|
||||
void Clear(bool mute) override;
|
||||
void Update() override;
|
||||
|
||||
static bool isValid() { return true; }
|
||||
private:
|
||||
static constexpr size_t BUFFER_SIZE = 48000 * 4 / 32;
|
||||
static constexpr size_t BUFFER_SIZE = 48000 * 4 / 32;
|
||||
|
||||
// Playback position
|
||||
std::array<short, BUFFER_SIZE / sizeof(short)> m_realtime_buffer;
|
||||
// Playback position
|
||||
std::array<short, BUFFER_SIZE / sizeof(short)> m_realtime_buffer;
|
||||
};
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
#include "AudioCommon/aldlist.h"
|
||||
#include "AudioCommon/DPL2Decoder.h"
|
||||
#include "AudioCommon/OpenALStream.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "AudioCommon/aldlist.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
#if defined HAVE_OPENAL && HAVE_OPENAL
|
||||
|
@ -25,351 +25,363 @@ static soundtouch::SoundTouch soundTouch;
|
|||
//
|
||||
bool OpenALStream::Start()
|
||||
{
|
||||
m_run_thread.store(true);
|
||||
bool bReturn = false;
|
||||
m_run_thread.store(true);
|
||||
bool bReturn = false;
|
||||
|
||||
ALDeviceList pDeviceList;
|
||||
if (pDeviceList.GetNumDevices())
|
||||
{
|
||||
char *defDevName = pDeviceList.GetDeviceName(pDeviceList.GetDefaultDevice());
|
||||
ALDeviceList pDeviceList;
|
||||
if (pDeviceList.GetNumDevices())
|
||||
{
|
||||
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);
|
||||
if (pDevice)
|
||||
{
|
||||
ALCcontext *pContext = alcCreateContext(pDevice, nullptr);
|
||||
if (pContext)
|
||||
{
|
||||
// Used to determine an appropriate period size (2x period = total buffer size)
|
||||
//ALCint refresh;
|
||||
//alcGetIntegerv(pDevice, ALC_REFRESH, 1, &refresh);
|
||||
//period_size_in_millisec = 1000 / refresh;
|
||||
ALCdevice* pDevice = alcOpenDevice(defDevName);
|
||||
if (pDevice)
|
||||
{
|
||||
ALCcontext* pContext = alcCreateContext(pDevice, nullptr);
|
||||
if (pContext)
|
||||
{
|
||||
// Used to determine an appropriate period size (2x period = total buffer size)
|
||||
// ALCint refresh;
|
||||
// alcGetIntegerv(pDevice, ALC_REFRESH, 1, &refresh);
|
||||
// period_size_in_millisec = 1000 / refresh;
|
||||
|
||||
alcMakeContextCurrent(pContext);
|
||||
thread = std::thread(&OpenALStream::SoundLoop, this);
|
||||
bReturn = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
alcCloseDevice(pDevice);
|
||||
PanicAlertT("OpenAL: can't create context for device %s", defDevName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlertT("OpenAL: can't open device %s", defDevName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlertT("OpenAL: can't find sound devices");
|
||||
}
|
||||
alcMakeContextCurrent(pContext);
|
||||
thread = std::thread(&OpenALStream::SoundLoop, this);
|
||||
bReturn = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
alcCloseDevice(pDevice);
|
||||
PanicAlertT("OpenAL: can't create context for device %s", defDevName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlertT("OpenAL: can't open device %s", defDevName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlertT("OpenAL: can't find sound devices");
|
||||
}
|
||||
|
||||
// Initialize DPL2 parameters
|
||||
DPL2Reset();
|
||||
// Initialize DPL2 parameters
|
||||
DPL2Reset();
|
||||
|
||||
soundTouch.clear();
|
||||
return bReturn;
|
||||
soundTouch.clear();
|
||||
return bReturn;
|
||||
}
|
||||
|
||||
void OpenALStream::Stop()
|
||||
{
|
||||
m_run_thread.store(false);
|
||||
// kick the thread if it's waiting
|
||||
soundSyncEvent.Set();
|
||||
m_run_thread.store(false);
|
||||
// kick the thread if it's waiting
|
||||
soundSyncEvent.Set();
|
||||
|
||||
soundTouch.clear();
|
||||
soundTouch.clear();
|
||||
|
||||
thread.join();
|
||||
thread.join();
|
||||
|
||||
alSourceStop(uiSource);
|
||||
alSourcei(uiSource, AL_BUFFER, 0);
|
||||
alSourceStop(uiSource);
|
||||
alSourcei(uiSource, AL_BUFFER, 0);
|
||||
|
||||
// Clean up buffers and sources
|
||||
alDeleteSources(1, &uiSource);
|
||||
uiSource = 0;
|
||||
alDeleteBuffers(numBuffers, uiBuffers);
|
||||
// Clean up buffers and sources
|
||||
alDeleteSources(1, &uiSource);
|
||||
uiSource = 0;
|
||||
alDeleteBuffers(numBuffers, uiBuffers);
|
||||
|
||||
ALCcontext *pContext = alcGetCurrentContext();
|
||||
ALCdevice *pDevice = alcGetContextsDevice(pContext);
|
||||
ALCcontext* pContext = alcGetCurrentContext();
|
||||
ALCdevice* pDevice = alcGetContextsDevice(pContext);
|
||||
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(pContext);
|
||||
alcCloseDevice(pDevice);
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(pContext);
|
||||
alcCloseDevice(pDevice);
|
||||
}
|
||||
|
||||
void OpenALStream::SetVolume(int volume)
|
||||
{
|
||||
fVolume = (float)volume / 100.0f;
|
||||
fVolume = (float)volume / 100.0f;
|
||||
|
||||
if (uiSource)
|
||||
alSourcef(uiSource, AL_GAIN, fVolume);
|
||||
if (uiSource)
|
||||
alSourcef(uiSource, AL_GAIN, fVolume);
|
||||
}
|
||||
|
||||
void OpenALStream::Update()
|
||||
{
|
||||
soundSyncEvent.Set();
|
||||
soundSyncEvent.Set();
|
||||
}
|
||||
|
||||
void OpenALStream::Clear(bool mute)
|
||||
{
|
||||
m_muted = mute;
|
||||
m_muted = mute;
|
||||
|
||||
if (m_muted)
|
||||
{
|
||||
soundTouch.clear();
|
||||
alSourceStop(uiSource);
|
||||
}
|
||||
else
|
||||
{
|
||||
alSourcePlay(uiSource);
|
||||
}
|
||||
if (m_muted)
|
||||
{
|
||||
soundTouch.clear();
|
||||
alSourceStop(uiSource);
|
||||
}
|
||||
else
|
||||
{
|
||||
alSourcePlay(uiSource);
|
||||
}
|
||||
}
|
||||
|
||||
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__)
|
||||
bool float32_capable = false;
|
||||
const ALenum AL_FORMAT_STEREO_FLOAT32 = 0;
|
||||
// OS X does not have the alext AL_FORMAT_51CHN32 yet.
|
||||
surround_capable = false;
|
||||
const ALenum AL_FORMAT_51CHN32 = 0;
|
||||
const ALenum AL_FORMAT_51CHN16 = 0;
|
||||
bool float32_capable = false;
|
||||
const ALenum AL_FORMAT_STEREO_FLOAT32 = 0;
|
||||
// OS X does not have the alext AL_FORMAT_51CHN32 yet.
|
||||
surround_capable = false;
|
||||
const ALenum AL_FORMAT_51CHN32 = 0;
|
||||
const ALenum AL_FORMAT_51CHN16 = 0;
|
||||
#else
|
||||
bool float32_capable = true;
|
||||
bool float32_capable = true;
|
||||
#endif
|
||||
|
||||
u32 ulFrequency = m_mixer->GetSampleRate();
|
||||
numBuffers = SConfig::GetInstance().iLatency + 2; // OpenAL requires a minimum of two buffers
|
||||
u32 ulFrequency = m_mixer->GetSampleRate();
|
||||
numBuffers = SConfig::GetInstance().iLatency + 2; // OpenAL requires a minimum of two buffers
|
||||
|
||||
memset(uiBuffers, 0, numBuffers * sizeof(ALuint));
|
||||
uiSource = 0;
|
||||
memset(uiBuffers, 0, numBuffers * sizeof(ALuint));
|
||||
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.
|
||||
if (strstr(alGetString(AL_RENDERER), "X-Fi"))
|
||||
float32_capable = false;
|
||||
// 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.
|
||||
if (strstr(alGetString(AL_RENDERER), "X-Fi"))
|
||||
float32_capable = false;
|
||||
|
||||
// Generate some AL Buffers for streaming
|
||||
alGenBuffers(numBuffers, (ALuint *)uiBuffers);
|
||||
// Generate a Source to playback the Buffers
|
||||
alGenSources(1, &uiSource);
|
||||
// Generate some AL Buffers for streaming
|
||||
alGenBuffers(numBuffers, (ALuint*)uiBuffers);
|
||||
// Generate a Source to playback the Buffers
|
||||
alGenSources(1, &uiSource);
|
||||
|
||||
// Short Silence
|
||||
if (float32_capable)
|
||||
memset(sampleBuffer, 0, OAL_MAX_SAMPLES * numBuffers * FRAME_SURROUND_FLOAT);
|
||||
else
|
||||
memset(sampleBuffer, 0, OAL_MAX_SAMPLES * numBuffers * FRAME_SURROUND_SHORT);
|
||||
// Short Silence
|
||||
if (float32_capable)
|
||||
memset(sampleBuffer, 0, OAL_MAX_SAMPLES * numBuffers * FRAME_SURROUND_FLOAT);
|
||||
else
|
||||
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++)
|
||||
{
|
||||
if (surround_capable)
|
||||
{
|
||||
if (float32_capable)
|
||||
alBufferData(uiBuffers[i], AL_FORMAT_51CHN32, sampleBuffer, 4 * FRAME_SURROUND_FLOAT, ulFrequency);
|
||||
else
|
||||
alBufferData(uiBuffers[i], AL_FORMAT_51CHN16, sampleBuffer, 4 * FRAME_SURROUND_SHORT, ulFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
alBufferData(uiBuffers[i], AL_FORMAT_STEREO16, realtimeBuffer, 4 * FRAME_STEREO_SHORT, ulFrequency);
|
||||
}
|
||||
}
|
||||
alSourceQueueBuffers(uiSource, numBuffers, uiBuffers);
|
||||
alSourcePlay(uiSource);
|
||||
for (int i = 0; i < numBuffers; i++)
|
||||
{
|
||||
if (surround_capable)
|
||||
{
|
||||
if (float32_capable)
|
||||
alBufferData(uiBuffers[i], AL_FORMAT_51CHN32, sampleBuffer, 4 * FRAME_SURROUND_FLOAT,
|
||||
ulFrequency);
|
||||
else
|
||||
alBufferData(uiBuffers[i], AL_FORMAT_51CHN16, sampleBuffer, 4 * FRAME_SURROUND_SHORT,
|
||||
ulFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
alBufferData(uiBuffers[i], AL_FORMAT_STEREO16, realtimeBuffer, 4 * FRAME_STEREO_SHORT,
|
||||
ulFrequency);
|
||||
}
|
||||
}
|
||||
alSourceQueueBuffers(uiSource, numBuffers, uiBuffers);
|
||||
alSourcePlay(uiSource);
|
||||
|
||||
// Set the default sound volume as saved in the config file.
|
||||
alSourcef(uiSource, AL_GAIN, fVolume);
|
||||
// Set the default sound volume as saved in the config file.
|
||||
alSourcef(uiSource, AL_GAIN, fVolume);
|
||||
|
||||
// TODO: Error handling
|
||||
//ALenum err = alGetError();
|
||||
// TODO: Error handling
|
||||
// ALenum err = alGetError();
|
||||
|
||||
ALint iBuffersFilled = 0;
|
||||
ALint iBuffersProcessed = 0;
|
||||
ALint iState = 0;
|
||||
ALuint uiBufferTemp[OAL_MAX_BUFFERS] = { 0 };
|
||||
ALint iBuffersFilled = 0;
|
||||
ALint iBuffersProcessed = 0;
|
||||
ALint iState = 0;
|
||||
ALuint uiBufferTemp[OAL_MAX_BUFFERS] = {0};
|
||||
|
||||
soundTouch.setChannels(2);
|
||||
soundTouch.setSampleRate(ulFrequency);
|
||||
soundTouch.setTempo(1.0);
|
||||
soundTouch.setSetting(SETTING_USE_QUICKSEEK, 0);
|
||||
soundTouch.setSetting(SETTING_USE_AA_FILTER, 0);
|
||||
soundTouch.setSetting(SETTING_SEQUENCE_MS, 1);
|
||||
soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 28);
|
||||
soundTouch.setSetting(SETTING_OVERLAP_MS, 12);
|
||||
soundTouch.setChannels(2);
|
||||
soundTouch.setSampleRate(ulFrequency);
|
||||
soundTouch.setTempo(1.0);
|
||||
soundTouch.setSetting(SETTING_USE_QUICKSEEK, 0);
|
||||
soundTouch.setSetting(SETTING_USE_AA_FILTER, 0);
|
||||
soundTouch.setSetting(SETTING_SEQUENCE_MS, 1);
|
||||
soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 28);
|
||||
soundTouch.setSetting(SETTING_OVERLAP_MS, 12);
|
||||
|
||||
while (m_run_thread.load())
|
||||
{
|
||||
// num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD.
|
||||
const u32 stereo_16_bit_size = 4;
|
||||
const u32 dma_length = 32;
|
||||
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 num_samples_to_render = (audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond();
|
||||
while (m_run_thread.load())
|
||||
{
|
||||
// num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD.
|
||||
const u32 stereo_16_bit_size = 4;
|
||||
const u32 dma_length = 32;
|
||||
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 num_samples_to_render =
|
||||
(audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond();
|
||||
|
||||
unsigned int numSamples = (unsigned int)num_samples_to_render;
|
||||
unsigned int minSamples = surround_capable ? 240 : 0; // DPL2 accepts 240 samples minimum (FWRDURATION)
|
||||
unsigned int numSamples = (unsigned int)num_samples_to_render;
|
||||
unsigned int minSamples =
|
||||
surround_capable ? 240 : 0; // DPL2 accepts 240 samples minimum (FWRDURATION)
|
||||
|
||||
numSamples = (numSamples > OAL_MAX_SAMPLES) ? OAL_MAX_SAMPLES : numSamples;
|
||||
numSamples = m_mixer->Mix(realtimeBuffer, numSamples, false);
|
||||
numSamples = (numSamples > OAL_MAX_SAMPLES) ? OAL_MAX_SAMPLES : numSamples;
|
||||
numSamples = m_mixer->Mix(realtimeBuffer, numSamples, false);
|
||||
|
||||
// Convert the samples from short to float
|
||||
float dest[OAL_MAX_SAMPLES * STEREO_CHANNELS];
|
||||
for (u32 i = 0; i < numSamples * STEREO_CHANNELS; ++i)
|
||||
dest[i] = (float)realtimeBuffer[i] / (1 << 15);
|
||||
// Convert the samples from short to float
|
||||
float dest[OAL_MAX_SAMPLES * STEREO_CHANNELS];
|
||||
for (u32 i = 0; i < numSamples * STEREO_CHANNELS; ++i)
|
||||
dest[i] = (float)realtimeBuffer[i] / (1 << 15);
|
||||
|
||||
soundTouch.putSamples(dest, numSamples);
|
||||
soundTouch.putSamples(dest, numSamples);
|
||||
|
||||
if (iBuffersProcessed == iBuffersFilled)
|
||||
{
|
||||
alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &iBuffersProcessed);
|
||||
iBuffersFilled = 0;
|
||||
}
|
||||
if (iBuffersProcessed == iBuffersFilled)
|
||||
{
|
||||
alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &iBuffersProcessed);
|
||||
iBuffersFilled = 0;
|
||||
}
|
||||
|
||||
if (iBuffersProcessed)
|
||||
{
|
||||
double rate = (double)m_mixer->GetCurrentSpeed();
|
||||
if (rate <= 0)
|
||||
{
|
||||
Core::RequestRefreshInfo();
|
||||
rate = (double)m_mixer->GetCurrentSpeed();
|
||||
}
|
||||
if (iBuffersProcessed)
|
||||
{
|
||||
double rate = (double)m_mixer->GetCurrentSpeed();
|
||||
if (rate <= 0)
|
||||
{
|
||||
Core::RequestRefreshInfo();
|
||||
rate = (double)m_mixer->GetCurrentSpeed();
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (rate > 0.10)
|
||||
{
|
||||
soundTouch.setTempo(rate);
|
||||
if (rate > 10)
|
||||
{
|
||||
soundTouch.clear();
|
||||
}
|
||||
}
|
||||
// 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.
|
||||
if (rate > 0.10)
|
||||
{
|
||||
soundTouch.setTempo(rate);
|
||||
if (rate > 10)
|
||||
{
|
||||
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)
|
||||
continue;
|
||||
if (nSamples <= minSamples)
|
||||
continue;
|
||||
|
||||
// Remove the Buffer from the Queue. (uiBuffer contains the Buffer ID for the unqueued Buffer)
|
||||
if (iBuffersFilled == 0)
|
||||
{
|
||||
alSourceUnqueueBuffers(uiSource, iBuffersProcessed, uiBufferTemp);
|
||||
ALenum err = alGetError();
|
||||
if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error unqueuing buffers: %08x", err);
|
||||
}
|
||||
}
|
||||
// Remove the Buffer from the Queue. (uiBuffer contains the Buffer ID for the unqueued
|
||||
// Buffer)
|
||||
if (iBuffersFilled == 0)
|
||||
{
|
||||
alSourceUnqueueBuffers(uiSource, iBuffersProcessed, uiBufferTemp);
|
||||
ALenum err = alGetError();
|
||||
if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error unqueuing buffers: %08x", err);
|
||||
}
|
||||
}
|
||||
|
||||
if (surround_capable)
|
||||
{
|
||||
float dpl2[OAL_MAX_SAMPLES * OAL_MAX_BUFFERS * SURROUND_CHANNELS];
|
||||
DPL2Decode(sampleBuffer, nSamples, dpl2);
|
||||
if (surround_capable)
|
||||
{
|
||||
float dpl2[OAL_MAX_SAMPLES * OAL_MAX_BUFFERS * SURROUND_CHANNELS];
|
||||
DPL2Decode(sampleBuffer, nSamples, dpl2);
|
||||
|
||||
// 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
|
||||
// AL_FORMAT_50CHN32 to make this super-explicit.
|
||||
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
|
||||
for (u32 i = 0; i < nSamples; ++i)
|
||||
{
|
||||
dpl2[i*SURROUND_CHANNELS + 3 /*sub/lfe*/] = 0.0f;
|
||||
}
|
||||
// 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
|
||||
// AL_FORMAT_50CHN32 to make this super-explicit.
|
||||
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
|
||||
for (u32 i = 0; i < nSamples; ++i)
|
||||
{
|
||||
dpl2[i * SURROUND_CHANNELS + 3 /*sub/lfe*/] = 0.0f;
|
||||
}
|
||||
|
||||
if (float32_capable)
|
||||
{
|
||||
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_51CHN32, dpl2, nSamples * FRAME_SURROUND_FLOAT, ulFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
short surround_short[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS];
|
||||
for (u32 i = 0; i < nSamples * SURROUND_CHANNELS; ++i)
|
||||
surround_short[i] = (short)((float)dpl2[i] * (1 << 15));
|
||||
if (float32_capable)
|
||||
{
|
||||
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_51CHN32, dpl2,
|
||||
nSamples * FRAME_SURROUND_FLOAT, ulFrequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
short surround_short[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS];
|
||||
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();
|
||||
if (err == AL_INVALID_ENUM)
|
||||
{
|
||||
// 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.");
|
||||
surround_capable = false;
|
||||
}
|
||||
else if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error occurred while buffering data: %08x", err);
|
||||
}
|
||||
}
|
||||
ALenum err = alGetError();
|
||||
if (err == AL_INVALID_ENUM)
|
||||
{
|
||||
// 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.");
|
||||
surround_capable = false;
|
||||
}
|
||||
else if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error occurred while buffering data: %08x", err);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (float32_capable)
|
||||
{
|
||||
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO_FLOAT32, sampleBuffer, nSamples * FRAME_STEREO_FLOAT, ulFrequency);
|
||||
ALenum err = alGetError();
|
||||
if (err == AL_INVALID_ENUM)
|
||||
{
|
||||
float32_capable = false;
|
||||
}
|
||||
else if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error occurred while buffering float32 data: %08x", err);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (float32_capable)
|
||||
{
|
||||
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO_FLOAT32, sampleBuffer,
|
||||
nSamples * FRAME_STEREO_FLOAT, ulFrequency);
|
||||
ALenum err = alGetError();
|
||||
if (err == AL_INVALID_ENUM)
|
||||
{
|
||||
float32_capable = false;
|
||||
}
|
||||
else if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error occurred while buffering float32 data: %08x", err);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Convert the samples from float to short
|
||||
short stereo[OAL_MAX_SAMPLES * STEREO_CHANNELS * OAL_MAX_BUFFERS];
|
||||
for (u32 i = 0; i < nSamples * STEREO_CHANNELS; ++i)
|
||||
stereo[i] = (short)((float)sampleBuffer[i] * (1 << 15));
|
||||
else
|
||||
{
|
||||
// Convert the samples from float to short
|
||||
short stereo[OAL_MAX_SAMPLES * STEREO_CHANNELS * OAL_MAX_BUFFERS];
|
||||
for (u32 i = 0; i < nSamples * STEREO_CHANNELS; ++i)
|
||||
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]);
|
||||
ALenum err = alGetError();
|
||||
if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error queuing buffers: %08x", err);
|
||||
}
|
||||
iBuffersFilled++;
|
||||
alSourceQueueBuffers(uiSource, 1, &uiBufferTemp[iBuffersFilled]);
|
||||
ALenum err = alGetError();
|
||||
if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error queuing buffers: %08x", err);
|
||||
}
|
||||
iBuffersFilled++;
|
||||
|
||||
if (iBuffersFilled == numBuffers)
|
||||
{
|
||||
alSourcePlay(uiSource);
|
||||
err = alGetError();
|
||||
if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error occurred during playback: %08x", err);
|
||||
}
|
||||
}
|
||||
if (iBuffersFilled == numBuffers)
|
||||
{
|
||||
alSourcePlay(uiSource);
|
||||
err = alGetError();
|
||||
if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error occurred during playback: %08x", err);
|
||||
}
|
||||
}
|
||||
|
||||
alGetSourcei(uiSource, AL_SOURCE_STATE, &iState);
|
||||
if (iState != AL_PLAYING)
|
||||
{
|
||||
// Buffer underrun occurred, resume playback
|
||||
alSourcePlay(uiSource);
|
||||
err = alGetError();
|
||||
if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error occurred resuming playback: %08x", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
soundSyncEvent.Wait();
|
||||
}
|
||||
}
|
||||
alGetSourcei(uiSource, AL_SOURCE_STATE, &iState);
|
||||
if (iState != AL_PLAYING)
|
||||
{
|
||||
// Buffer underrun occurred, resume playback
|
||||
alSourcePlay(uiSource);
|
||||
err = alGetError();
|
||||
if (err != 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Error occurred resuming playback: %08x", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
soundSyncEvent.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif //HAVE_OPENAL
|
||||
|
||||
#endif // HAVE_OPENAL
|
||||
|
|
|
@ -32,56 +32,52 @@
|
|||
#define BOOL SoundTouch_BOOL
|
||||
#endif
|
||||
|
||||
#include <soundtouch/SoundTouch.h>
|
||||
#include <soundtouch/STTypes.h>
|
||||
#include <soundtouch/SoundTouch.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#undef BOOL
|
||||
#endif
|
||||
|
||||
// 16 bit Stereo
|
||||
#define SFX_MAX_SOURCE 1
|
||||
#define OAL_MAX_BUFFERS 32
|
||||
#define OAL_MAX_SAMPLES 256
|
||||
#define STEREO_CHANNELS 2
|
||||
#define SURROUND_CHANNELS 6 // number of channels in surround mode
|
||||
#define SIZE_SHORT 2
|
||||
#define SIZE_FLOAT 4 // size of a float in bytes
|
||||
#define FRAME_STEREO_SHORT STEREO_CHANNELS * SIZE_SHORT
|
||||
#define FRAME_STEREO_FLOAT STEREO_CHANNELS * SIZE_FLOAT
|
||||
#define FRAME_SURROUND_FLOAT SURROUND_CHANNELS * SIZE_FLOAT
|
||||
#define FRAME_SURROUND_SHORT SURROUND_CHANNELS * SIZE_SHORT
|
||||
#define SFX_MAX_SOURCE 1
|
||||
#define OAL_MAX_BUFFERS 32
|
||||
#define OAL_MAX_SAMPLES 256
|
||||
#define STEREO_CHANNELS 2
|
||||
#define SURROUND_CHANNELS 6 // number of channels in surround mode
|
||||
#define SIZE_SHORT 2
|
||||
#define SIZE_FLOAT 4 // size of a float in bytes
|
||||
#define FRAME_STEREO_SHORT STEREO_CHANNELS* SIZE_SHORT
|
||||
#define FRAME_STEREO_FLOAT STEREO_CHANNELS* SIZE_FLOAT
|
||||
#define FRAME_SURROUND_FLOAT SURROUND_CHANNELS* SIZE_FLOAT
|
||||
#define FRAME_SURROUND_SHORT SURROUND_CHANNELS* SIZE_SHORT
|
||||
#endif
|
||||
|
||||
class OpenALStream final : public SoundStream
|
||||
{
|
||||
#if defined HAVE_OPENAL && HAVE_OPENAL
|
||||
public:
|
||||
OpenALStream() : uiSource(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool Start() override;
|
||||
void SoundLoop() override;
|
||||
void SetVolume(int volume) override;
|
||||
void Stop() override;
|
||||
void Clear(bool mute) override;
|
||||
void Update() override;
|
||||
|
||||
static bool isValid() { return true; }
|
||||
OpenALStream() : uiSource(0) {}
|
||||
bool Start() override;
|
||||
void SoundLoop() override;
|
||||
void SetVolume(int volume) override;
|
||||
void Stop() override;
|
||||
void Clear(bool mute) override;
|
||||
void Update() override;
|
||||
|
||||
static bool isValid() { return true; }
|
||||
private:
|
||||
std::thread thread;
|
||||
std::atomic<bool> m_run_thread;
|
||||
std::thread thread;
|
||||
std::atomic<bool> m_run_thread;
|
||||
|
||||
Common::Event soundSyncEvent;
|
||||
Common::Event soundSyncEvent;
|
||||
|
||||
short realtimeBuffer[OAL_MAX_SAMPLES * STEREO_CHANNELS];
|
||||
soundtouch::SAMPLETYPE sampleBuffer[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS];
|
||||
ALuint uiBuffers[OAL_MAX_BUFFERS];
|
||||
ALuint uiSource;
|
||||
ALfloat fVolume;
|
||||
short realtimeBuffer[OAL_MAX_SAMPLES * STEREO_CHANNELS];
|
||||
soundtouch::SAMPLETYPE sampleBuffer[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS];
|
||||
ALuint uiBuffers[OAL_MAX_BUFFERS];
|
||||
ALuint uiSource;
|
||||
ALfloat fVolume;
|
||||
|
||||
u8 numBuffers;
|
||||
#endif // HAVE_OPENAL
|
||||
u8 numBuffers;
|
||||
#endif // HAVE_OPENAL
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@ static SLPlayItf bqPlayerPlay;
|
|||
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
|
||||
static SLMuteSoloItf bqPlayerMuteSolo;
|
||||
static SLVolumeItf bqPlayerVolume;
|
||||
static CMixer *g_mixer;
|
||||
static CMixer* g_mixer;
|
||||
#define BUFFER_SIZE 512
|
||||
#define BUFFER_SIZE_IN_SAMPLES (BUFFER_SIZE / 2)
|
||||
|
||||
|
@ -32,106 +32,107 @@ static CMixer *g_mixer;
|
|||
static short buffer[2][BUFFER_SIZE];
|
||||
static int curBuffer = 0;
|
||||
|
||||
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
||||
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
|
||||
{
|
||||
assert(bq == bqPlayerBufferQueue);
|
||||
assert(nullptr == context);
|
||||
assert(bq == bqPlayerBufferQueue);
|
||||
assert(nullptr == context);
|
||||
|
||||
// Render to the fresh buffer
|
||||
g_mixer->Mix(reinterpret_cast<short *>(buffer[curBuffer]), BUFFER_SIZE_IN_SAMPLES);
|
||||
SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeof(buffer[0]));
|
||||
curBuffer ^= 1; // Switch buffer
|
||||
// Render to the fresh buffer
|
||||
g_mixer->Mix(reinterpret_cast<short*>(buffer[curBuffer]), BUFFER_SIZE_IN_SAMPLES);
|
||||
SLresult result =
|
||||
(*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeof(buffer[0]));
|
||||
curBuffer ^= 1; // Switch buffer
|
||||
|
||||
// Comment from sample code:
|
||||
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
|
||||
// which for this code example would indicate a programming error
|
||||
_assert_msg_(AUDIO, SL_RESULT_SUCCESS == result, "Couldn't enqueue audio stream.");
|
||||
// Comment from sample code:
|
||||
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
|
||||
// which for this code example would indicate a programming error
|
||||
_assert_msg_(AUDIO, SL_RESULT_SUCCESS == result, "Couldn't enqueue audio stream.");
|
||||
}
|
||||
|
||||
bool OpenSLESStream::Start()
|
||||
{
|
||||
SLresult result;
|
||||
// create engine
|
||||
result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
SLresult result;
|
||||
// create engine
|
||||
result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
|
||||
SLDataFormat_PCM format_pcm = {
|
||||
SL_DATAFORMAT_PCM,
|
||||
2,
|
||||
m_mixer->GetSampleRate() * 1000,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
|
||||
SL_BYTEORDER_LITTLEENDIAN
|
||||
};
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
|
||||
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,
|
||||
2,
|
||||
m_mixer->GetSampleRate() * 1000,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
|
||||
SL_BYTEORDER_LITTLEENDIAN};
|
||||
|
||||
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
|
||||
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
|
||||
|
||||
// configure audio sink
|
||||
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
|
||||
SLDataSink audioSnk = {&loc_outmix, nullptr};
|
||||
// configure audio sink
|
||||
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
|
||||
SLDataSink audioSnk = {&loc_outmix, nullptr};
|
||||
|
||||
// create audio player
|
||||
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
|
||||
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
// create audio player
|
||||
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
|
||||
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
result =
|
||||
(*engineEngine)
|
||||
->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
|
||||
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
|
||||
&bqPlayerBufferQueue);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result =
|
||||
(*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
||||
assert(SL_RESULT_SUCCESS == result);
|
||||
|
||||
// Render and enqueue a first buffer.
|
||||
curBuffer ^= 1;
|
||||
g_mixer = m_mixer.get();
|
||||
// Render and enqueue a first buffer.
|
||||
curBuffer ^= 1;
|
||||
g_mixer = m_mixer.get();
|
||||
|
||||
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[0], sizeof(buffer[0]));
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
return false;
|
||||
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[0], sizeof(buffer[0]));
|
||||
if (SL_RESULT_SUCCESS != result)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenSLESStream::Stop()
|
||||
{
|
||||
if (bqPlayerObject != nullptr)
|
||||
{
|
||||
(*bqPlayerObject)->Destroy(bqPlayerObject);
|
||||
bqPlayerObject = nullptr;
|
||||
bqPlayerPlay = nullptr;
|
||||
bqPlayerBufferQueue = nullptr;
|
||||
bqPlayerMuteSolo = nullptr;
|
||||
bqPlayerVolume = nullptr;
|
||||
}
|
||||
if (bqPlayerObject != nullptr)
|
||||
{
|
||||
(*bqPlayerObject)->Destroy(bqPlayerObject);
|
||||
bqPlayerObject = nullptr;
|
||||
bqPlayerPlay = nullptr;
|
||||
bqPlayerBufferQueue = nullptr;
|
||||
bqPlayerMuteSolo = nullptr;
|
||||
bqPlayerVolume = nullptr;
|
||||
}
|
||||
|
||||
if (outputMixObject != nullptr)
|
||||
{
|
||||
(*outputMixObject)->Destroy(outputMixObject);
|
||||
outputMixObject = nullptr;
|
||||
}
|
||||
if (outputMixObject != nullptr)
|
||||
{
|
||||
(*outputMixObject)->Destroy(outputMixObject);
|
||||
outputMixObject = nullptr;
|
||||
}
|
||||
|
||||
if (engineObject != nullptr)
|
||||
{
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
engineObject = nullptr;
|
||||
engineEngine = nullptr;
|
||||
}
|
||||
if (engineObject != nullptr)
|
||||
{
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
engineObject = nullptr;
|
||||
engineEngine = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -13,12 +13,11 @@ class OpenSLESStream final : public SoundStream
|
|||
{
|
||||
#ifdef ANDROID
|
||||
public:
|
||||
bool Start() override;
|
||||
void Stop() override;
|
||||
static bool isValid() { return true; }
|
||||
|
||||
bool Start() override;
|
||||
void Stop() override;
|
||||
static bool isValid() { return true; }
|
||||
private:
|
||||
std::thread thread;
|
||||
Common::Event soundSyncEvent;
|
||||
#endif // HAVE_OPENSL
|
||||
std::thread thread;
|
||||
Common::Event soundSyncEvent;
|
||||
#endif // HAVE_OPENSL
|
||||
};
|
||||
|
|
|
@ -7,245 +7,247 @@
|
|||
#include "AudioCommon/DPL2Decoder.h"
|
||||
#include "AudioCommon/PulseAudioStream.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
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()
|
||||
: m_thread()
|
||||
, m_run_thread()
|
||||
PulseAudio::PulseAudio() : m_thread(), m_run_thread()
|
||||
{
|
||||
}
|
||||
|
||||
bool PulseAudio::Start()
|
||||
{
|
||||
m_stereo = !SConfig::GetInstance().bDPL2Decoder;
|
||||
m_channels = m_stereo ? 2 : 5; // will tell PA we use a Stereo or 5.0 channel setup
|
||||
m_stereo = !SConfig::GetInstance().bDPL2Decoder;
|
||||
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_thread = std::thread(&PulseAudio::SoundLoop, this);
|
||||
m_run_thread = true;
|
||||
m_thread = std::thread(&PulseAudio::SoundLoop, this);
|
||||
|
||||
// Initialize DPL2 parameters
|
||||
DPL2Reset();
|
||||
// Initialize DPL2 parameters
|
||||
DPL2Reset();
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PulseAudio::Stop()
|
||||
{
|
||||
m_run_thread = false;
|
||||
m_thread.join();
|
||||
m_run_thread = false;
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
void PulseAudio::Update()
|
||||
{
|
||||
// don't need to do anything here.
|
||||
// don't need to do anything here.
|
||||
}
|
||||
|
||||
// Called on audio thread.
|
||||
void PulseAudio::SoundLoop()
|
||||
{
|
||||
Common::SetCurrentThreadName("Audio thread - pulse");
|
||||
Common::SetCurrentThreadName("Audio thread - pulse");
|
||||
|
||||
if (PulseInit())
|
||||
{
|
||||
while (m_run_thread.load() && m_pa_connected == 1 && m_pa_error >= 0)
|
||||
m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr);
|
||||
if (PulseInit())
|
||||
{
|
||||
while (m_run_thread.load() && m_pa_connected == 1 && m_pa_error >= 0)
|
||||
m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr);
|
||||
|
||||
if (m_pa_error < 0)
|
||||
ERROR_LOG(AUDIO, "PulseAudio error: %s", pa_strerror(m_pa_error));
|
||||
if (m_pa_error < 0)
|
||||
ERROR_LOG(AUDIO, "PulseAudio error: %s", pa_strerror(m_pa_error));
|
||||
|
||||
PulseShutdown();
|
||||
}
|
||||
PulseShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool PulseAudio::PulseInit()
|
||||
{
|
||||
m_pa_error = 0;
|
||||
m_pa_connected = 0;
|
||||
m_pa_error = 0;
|
||||
m_pa_connected = 0;
|
||||
|
||||
// create pulseaudio main loop and context
|
||||
// also register the async state callback which is called when the connection to the pa server has changed
|
||||
m_pa_ml = pa_mainloop_new();
|
||||
m_pa_mlapi = pa_mainloop_get_api(m_pa_ml);
|
||||
m_pa_ctx = pa_context_new(m_pa_mlapi, "dolphin-emu");
|
||||
m_pa_error = pa_context_connect(m_pa_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr);
|
||||
pa_context_set_state_callback(m_pa_ctx, StateCallback, this);
|
||||
// create pulseaudio main loop and context
|
||||
// also register the async state callback which is called when the connection to the pa server has
|
||||
// changed
|
||||
m_pa_ml = pa_mainloop_new();
|
||||
m_pa_mlapi = pa_mainloop_get_api(m_pa_ml);
|
||||
m_pa_ctx = pa_context_new(m_pa_mlapi, "dolphin-emu");
|
||||
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
|
||||
while (m_pa_connected == 0 && m_pa_error >= 0)
|
||||
m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr);
|
||||
// wait until we're connected to the pulseaudio server
|
||||
while (m_pa_connected == 0 && m_pa_error >= 0)
|
||||
m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr);
|
||||
|
||||
if (m_pa_connected == 2 || m_pa_error < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error));
|
||||
return false;
|
||||
}
|
||||
if (m_pa_connected == 2 || m_pa_error < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error));
|
||||
return false;
|
||||
}
|
||||
|
||||
// create a new audio stream with our sample format
|
||||
// also connect the callbacks for this stream
|
||||
pa_sample_spec ss;
|
||||
pa_channel_map channel_map;
|
||||
pa_channel_map* channel_map_p = nullptr; // auto channel map
|
||||
if (m_stereo)
|
||||
{
|
||||
ss.format = PA_SAMPLE_S16LE;
|
||||
m_bytespersample = sizeof(s16);
|
||||
}
|
||||
else
|
||||
{
|
||||
// surround is remixed in floats, use a float PA buffer to save another conversion
|
||||
ss.format = PA_SAMPLE_FLOAT32NE;
|
||||
m_bytespersample = sizeof(float);
|
||||
// create a new audio stream with our sample format
|
||||
// also connect the callbacks for this stream
|
||||
pa_sample_spec ss;
|
||||
pa_channel_map channel_map;
|
||||
pa_channel_map* channel_map_p = nullptr; // auto channel map
|
||||
if (m_stereo)
|
||||
{
|
||||
ss.format = PA_SAMPLE_S16LE;
|
||||
m_bytespersample = sizeof(s16);
|
||||
}
|
||||
else
|
||||
{
|
||||
// surround is remixed in floats, use a float PA buffer to save another conversion
|
||||
ss.format = PA_SAMPLE_FLOAT32NE;
|
||||
m_bytespersample = sizeof(float);
|
||||
|
||||
channel_map_p = &channel_map; // explicit channel map:
|
||||
channel_map.channels = 5;
|
||||
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
|
||||
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
|
||||
channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
|
||||
channel_map.map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
|
||||
channel_map.map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
|
||||
}
|
||||
ss.channels = m_channels;
|
||||
ss.rate = m_mixer->GetSampleRate();
|
||||
assert(pa_sample_spec_valid(&ss));
|
||||
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_underflow_callback(m_pa_s, UnderflowCallback, this);
|
||||
channel_map_p = &channel_map; // explicit channel map:
|
||||
channel_map.channels = 5;
|
||||
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
|
||||
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
|
||||
channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
|
||||
channel_map.map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
|
||||
channel_map.map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
|
||||
}
|
||||
ss.channels = m_channels;
|
||||
ss.rate = m_mixer->GetSampleRate();
|
||||
assert(pa_sample_spec_valid(&ss));
|
||||
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_underflow_callback(m_pa_s, UnderflowCallback, this);
|
||||
|
||||
// connect this audio stream to the default audio playback
|
||||
// limit buffersize to reduce latency
|
||||
m_pa_ba.fragsize = -1;
|
||||
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.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
|
||||
pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
|
||||
m_pa_error = pa_stream_connect_playback(m_pa_s, nullptr, &m_pa_ba, flags, nullptr, nullptr);
|
||||
if (m_pa_error < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error));
|
||||
return false;
|
||||
}
|
||||
// connect this audio stream to the default audio playback
|
||||
// limit buffersize to reduce latency
|
||||
m_pa_ba.fragsize = -1;
|
||||
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.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
|
||||
pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY |
|
||||
PA_STREAM_AUTO_TIMING_UPDATE);
|
||||
m_pa_error = pa_stream_connect_playback(m_pa_s, nullptr, &m_pa_ba, flags, nullptr, nullptr);
|
||||
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");
|
||||
return true;
|
||||
INFO_LOG(AUDIO, "Pulse successfully initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
void PulseAudio::PulseShutdown()
|
||||
{
|
||||
pa_context_disconnect(m_pa_ctx);
|
||||
pa_context_unref(m_pa_ctx);
|
||||
pa_mainloop_free(m_pa_ml);
|
||||
pa_context_disconnect(m_pa_ctx);
|
||||
pa_context_unref(m_pa_ctx);
|
||||
pa_mainloop_free(m_pa_ml);
|
||||
}
|
||||
|
||||
void PulseAudio::StateCallback(pa_context* c)
|
||||
{
|
||||
pa_context_state_t state = pa_context_get_state(c);
|
||||
switch (state)
|
||||
{
|
||||
case PA_CONTEXT_FAILED:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
m_pa_connected = 2;
|
||||
break;
|
||||
case PA_CONTEXT_READY:
|
||||
m_pa_connected = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pa_context_state_t state = pa_context_get_state(c);
|
||||
switch (state)
|
||||
{
|
||||
case PA_CONTEXT_FAILED:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
m_pa_connected = 2;
|
||||
break;
|
||||
case PA_CONTEXT_READY:
|
||||
m_pa_connected = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// on underflow, increase pulseaudio latency in ~10ms steps
|
||||
void PulseAudio::UnderflowCallback(pa_stream* s)
|
||||
{
|
||||
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_unref(op);
|
||||
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_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)
|
||||
{
|
||||
int bytes_per_frame = m_channels * m_bytespersample;
|
||||
int frames = (length / bytes_per_frame);
|
||||
size_t trunc_length = frames * bytes_per_frame;
|
||||
int bytes_per_frame = m_channels * m_bytespersample;
|
||||
int frames = (length / bytes_per_frame);
|
||||
size_t trunc_length = frames * bytes_per_frame;
|
||||
|
||||
// fetch dst buffer directly from pulseaudio, so no memcpy is needed
|
||||
void* buffer;
|
||||
m_pa_error = pa_stream_begin_write(s, &buffer, &trunc_length);
|
||||
// fetch dst buffer directly from pulseaudio, so no memcpy is needed
|
||||
void* buffer;
|
||||
m_pa_error = pa_stream_begin_write(s, &buffer, &trunc_length);
|
||||
|
||||
if (!buffer || m_pa_error < 0)
|
||||
return; // error will be printed from main loop
|
||||
if (!buffer || m_pa_error < 0)
|
||||
return; // error will be printed from main loop
|
||||
|
||||
if (m_stereo)
|
||||
{
|
||||
// use the raw s16 stereo mix
|
||||
m_mixer->Mix((s16*) buffer, frames);
|
||||
}
|
||||
else
|
||||
{
|
||||
// get a floating point mix
|
||||
s16 s16buffer_stereo[frames * 2];
|
||||
m_mixer->Mix(s16buffer_stereo, frames); // implicitly mixes to 16-bit stereo
|
||||
if (m_stereo)
|
||||
{
|
||||
// use the raw s16 stereo mix
|
||||
m_mixer->Mix((s16*)buffer, frames);
|
||||
}
|
||||
else
|
||||
{
|
||||
// get a floating point mix
|
||||
s16 s16buffer_stereo[frames * 2];
|
||||
m_mixer->Mix(s16buffer_stereo, frames); // implicitly mixes to 16-bit stereo
|
||||
|
||||
float floatbuffer_stereo[frames * 2];
|
||||
// s16 to float
|
||||
for (int i=0; i < frames * 2; ++i)
|
||||
{
|
||||
floatbuffer_stereo[i] = s16buffer_stereo[i] / float(1 << 15);
|
||||
}
|
||||
float floatbuffer_stereo[frames * 2];
|
||||
// s16 to float
|
||||
for (int i = 0; i < frames * 2; ++i)
|
||||
{
|
||||
floatbuffer_stereo[i] = s16buffer_stereo[i] / float(1 << 15);
|
||||
}
|
||||
|
||||
if (m_channels == 5) // Extract dpl2/5.0 Surround
|
||||
{
|
||||
float floatbuffer_6chan[frames * 6];
|
||||
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
|
||||
DPL2Decode(floatbuffer_stereo, frames, floatbuffer_6chan);
|
||||
if (m_channels == 5) // Extract dpl2/5.0 Surround
|
||||
{
|
||||
float floatbuffer_6chan[frames * 6];
|
||||
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
|
||||
DPL2Decode(floatbuffer_stereo, frames, floatbuffer_6chan);
|
||||
|
||||
// Discard the subwoofer channel - DPL2Decode generates a pretty
|
||||
// good 5.0 but not a good 5.1 output.
|
||||
const int dpl2_to_5chan[] = {0,1,2,4,5};
|
||||
for (int i=0; i < frames; ++i)
|
||||
{
|
||||
for (int j=0; j < m_channels; ++j)
|
||||
{
|
||||
((float*)buffer)[m_channels * i + j] = floatbuffer_6chan[6 * i + dpl2_to_5chan[j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Unsupported number of PA channels requested: %d", (int)m_channels);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Discard the subwoofer channel - DPL2Decode generates a pretty
|
||||
// good 5.0 but not a good 5.1 output.
|
||||
const int dpl2_to_5chan[] = {0, 1, 2, 4, 5};
|
||||
for (int i = 0; i < frames; ++i)
|
||||
{
|
||||
for (int j = 0; j < m_channels; ++j)
|
||||
{
|
||||
((float*)buffer)[m_channels * i + j] = floatbuffer_6chan[6 * i + dpl2_to_5chan[j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Unsupported number of PA channels requested: %d", (int)m_channels);
|
||||
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).
|
||||
|
||||
void PulseAudio::StateCallback(pa_context* c, void* userdata)
|
||||
{
|
||||
PulseAudio* p = (PulseAudio*) userdata;
|
||||
p->StateCallback(c);
|
||||
PulseAudio* p = (PulseAudio*)userdata;
|
||||
p->StateCallback(c);
|
||||
}
|
||||
|
||||
void PulseAudio::UnderflowCallback(pa_stream* s, void* userdata)
|
||||
{
|
||||
PulseAudio* p = (PulseAudio*) userdata;
|
||||
p->UnderflowCallback(s);
|
||||
PulseAudio* p = (PulseAudio*)userdata;
|
||||
p->UnderflowCallback(s);
|
||||
}
|
||||
|
||||
void PulseAudio::WriteCallback(pa_stream* s, size_t length, void* userdata)
|
||||
{
|
||||
PulseAudio* p = (PulseAudio*) userdata;
|
||||
p->WriteCallback(s, length);
|
||||
PulseAudio* p = (PulseAudio*)userdata;
|
||||
p->WriteCallback(s, length);
|
||||
}
|
||||
|
|
|
@ -18,42 +18,41 @@ class PulseAudio final : public SoundStream
|
|||
{
|
||||
#if defined(HAVE_PULSEAUDIO) && HAVE_PULSEAUDIO
|
||||
public:
|
||||
PulseAudio();
|
||||
PulseAudio();
|
||||
|
||||
bool Start() override;
|
||||
void Stop() override;
|
||||
void Update() override;
|
||||
bool Start() override;
|
||||
void Stop() override;
|
||||
void Update() override;
|
||||
|
||||
static bool isValid() { return true; }
|
||||
|
||||
void StateCallback(pa_context *c);
|
||||
void WriteCallback(pa_stream *s, size_t length);
|
||||
void UnderflowCallback(pa_stream *s);
|
||||
static bool isValid() { return true; }
|
||||
void StateCallback(pa_context* c);
|
||||
void WriteCallback(pa_stream* s, size_t length);
|
||||
void UnderflowCallback(pa_stream* s);
|
||||
|
||||
private:
|
||||
void SoundLoop() override;
|
||||
void SoundLoop() override;
|
||||
|
||||
bool PulseInit();
|
||||
void PulseShutdown();
|
||||
bool PulseInit();
|
||||
void PulseShutdown();
|
||||
|
||||
// wrapper callback functions, last parameter _must_ be PulseAudio*
|
||||
static void StateCallback(pa_context *c, void *userdata);
|
||||
static void WriteCallback(pa_stream *s, size_t length, void *userdata);
|
||||
static void UnderflowCallback(pa_stream *s, void *userdata);
|
||||
// wrapper callback functions, last parameter _must_ be PulseAudio*
|
||||
static void StateCallback(pa_context* c, void* userdata);
|
||||
static void WriteCallback(pa_stream* s, size_t length, void* userdata);
|
||||
static void UnderflowCallback(pa_stream* s, void* userdata);
|
||||
|
||||
std::thread m_thread;
|
||||
std::atomic<bool> m_run_thread;
|
||||
std::thread m_thread;
|
||||
std::atomic<bool> m_run_thread;
|
||||
|
||||
bool m_stereo; // stereo, else surround
|
||||
int m_bytespersample;
|
||||
int m_channels;
|
||||
bool m_stereo; // stereo, else surround
|
||||
int m_bytespersample;
|
||||
int m_channels;
|
||||
|
||||
int m_pa_error;
|
||||
int m_pa_connected;
|
||||
pa_mainloop *m_pa_ml;
|
||||
pa_mainloop_api *m_pa_mlapi;
|
||||
pa_context *m_pa_ctx;
|
||||
pa_stream *m_pa_s;
|
||||
pa_buffer_attr m_pa_ba;
|
||||
int m_pa_error;
|
||||
int m_pa_connected;
|
||||
pa_mainloop* m_pa_ml;
|
||||
pa_mainloop_api* m_pa_mlapi;
|
||||
pa_context* m_pa_ctx;
|
||||
pa_stream* m_pa_s;
|
||||
pa_buffer_attr m_pa_ba;
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -14,51 +14,49 @@
|
|||
class SoundStream
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<CMixer> m_mixer;
|
||||
bool m_logAudio;
|
||||
WaveFileWriter g_wave_writer;
|
||||
bool m_muted;
|
||||
std::unique_ptr<CMixer> m_mixer;
|
||||
bool m_logAudio;
|
||||
WaveFileWriter g_wave_writer;
|
||||
bool m_muted;
|
||||
|
||||
public:
|
||||
SoundStream() : m_mixer(new CMixer(48000)), m_logAudio(false), m_muted(false) {}
|
||||
virtual ~SoundStream() { }
|
||||
SoundStream() : m_mixer(new CMixer(48000)), m_logAudio(false), m_muted(false) {}
|
||||
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; }
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
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()
|
||||
{
|
||||
Stop();
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRate)
|
||||
{
|
||||
// Check if the file is already open
|
||||
if (file)
|
||||
{
|
||||
PanicAlertT("The file %s was already open, the file header will not be written.", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
// Check if the file is already open
|
||||
if (file)
|
||||
{
|
||||
PanicAlertT("The file %s was already open, the file header will not be written.",
|
||||
filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
file.Open(filename, "wb");
|
||||
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());
|
||||
return false;
|
||||
}
|
||||
file.Open(filename, "wb");
|
||||
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());
|
||||
return false;
|
||||
}
|
||||
|
||||
audio_size = 0;
|
||||
audio_size = 0;
|
||||
|
||||
// -----------------
|
||||
// Write file header
|
||||
// -----------------
|
||||
Write4("RIFF");
|
||||
Write(100 * 1000 * 1000); // write big value in case the file gets truncated
|
||||
Write4("WAVE");
|
||||
Write4("fmt ");
|
||||
// -----------------
|
||||
// Write file header
|
||||
// -----------------
|
||||
Write4("RIFF");
|
||||
Write(100 * 1000 * 1000); // write big value in case the file gets truncated
|
||||
Write4("WAVE");
|
||||
Write4("fmt ");
|
||||
|
||||
Write(16); // size of fmt block
|
||||
Write(0x00020001); //two channels, uncompressed
|
||||
Write(16); // size of fmt block
|
||||
Write(0x00020001); // two channels, uncompressed
|
||||
|
||||
const u32 sample_rate = HLESampleRate;
|
||||
Write(sample_rate);
|
||||
Write(sample_rate * 2 * 2); //two channels, 16bit
|
||||
const u32 sample_rate = HLESampleRate;
|
||||
Write(sample_rate);
|
||||
Write(sample_rate * 2 * 2); // two channels, 16bit
|
||||
|
||||
Write(0x00100004);
|
||||
Write4("data");
|
||||
Write(100 * 1000 * 1000 - 32);
|
||||
Write(0x00100004);
|
||||
Write4("data");
|
||||
Write(100 * 1000 * 1000 - 32);
|
||||
|
||||
// We are now at offset 44
|
||||
if (file.Tell() != 44)
|
||||
PanicAlert("Wrong offset: %lld", (long long)file.Tell());
|
||||
// We are now at offset 44
|
||||
if (file.Tell() != 44)
|
||||
PanicAlert("Wrong offset: %lld", (long long)file.Tell());
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaveFileWriter::Stop()
|
||||
{
|
||||
// u32 file_size = (u32)ftello(file);
|
||||
file.Seek(4, SEEK_SET);
|
||||
Write(audio_size + 36);
|
||||
// u32 file_size = (u32)ftello(file);
|
||||
file.Seek(4, SEEK_SET);
|
||||
Write(audio_size + 36);
|
||||
|
||||
file.Seek(40, SEEK_SET);
|
||||
Write(audio_size);
|
||||
file.Seek(40, SEEK_SET);
|
||||
Write(audio_size);
|
||||
|
||||
file.Close();
|
||||
file.Close();
|
||||
}
|
||||
|
||||
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)
|
||||
PanicAlertT("WaveFileWriter - file not open.");
|
||||
if (!file)
|
||||
PanicAlertT("WaveFileWriter - file not open.");
|
||||
|
||||
if (skip_silence)
|
||||
{
|
||||
bool all_zero = true;
|
||||
if (skip_silence)
|
||||
{
|
||||
bool all_zero = true;
|
||||
|
||||
for (u32 i = 0; i < count * 2; i++)
|
||||
{
|
||||
if (sample_data[i])
|
||||
all_zero = false;
|
||||
}
|
||||
for (u32 i = 0; i < count * 2; i++)
|
||||
{
|
||||
if (sample_data[i])
|
||||
all_zero = false;
|
||||
}
|
||||
|
||||
if (all_zero)
|
||||
return;
|
||||
}
|
||||
if (all_zero)
|
||||
return;
|
||||
}
|
||||
|
||||
file.WriteBytes(sample_data, count * 4);
|
||||
audio_size += count * 4;
|
||||
file.WriteBytes(sample_data, 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)
|
||||
PanicAlertT("WaveFileWriter - file not open.");
|
||||
if (!file)
|
||||
PanicAlertT("WaveFileWriter - file not open.");
|
||||
|
||||
if (count > BUFFER_SIZE * 2)
|
||||
PanicAlert("WaveFileWriter - buffer too small (count = %u).", count);
|
||||
if (count > BUFFER_SIZE * 2)
|
||||
PanicAlert("WaveFileWriter - buffer too small (count = %u).", count);
|
||||
|
||||
if (skip_silence)
|
||||
{
|
||||
bool all_zero = true;
|
||||
if (skip_silence)
|
||||
{
|
||||
bool all_zero = true;
|
||||
|
||||
for (u32 i = 0; i < count * 2; i++)
|
||||
{
|
||||
if (sample_data[i])
|
||||
all_zero = false;
|
||||
}
|
||||
for (u32 i = 0; i < count * 2; i++)
|
||||
{
|
||||
if (sample_data[i])
|
||||
all_zero = false;
|
||||
}
|
||||
|
||||
if (all_zero)
|
||||
return;
|
||||
}
|
||||
if (all_zero)
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < count; i++)
|
||||
{
|
||||
//Flip the audio channels from RL to LR
|
||||
conv_buffer[2 * i] = Common::swap16((u16)sample_data[2 * i + 1]);
|
||||
conv_buffer[2 * i + 1] = Common::swap16((u16)sample_data[2 * i]);
|
||||
}
|
||||
for (u32 i = 0; i < count; i++)
|
||||
{
|
||||
// Flip the audio channels from RL to LR
|
||||
conv_buffer[2 * i] = Common::swap16((u16)sample_data[2 * i + 1]);
|
||||
conv_buffer[2 * i + 1] = Common::swap16((u16)sample_data[2 * i]);
|
||||
}
|
||||
|
||||
file.WriteBytes(conv_buffer.data(), count * 4);
|
||||
audio_size += count * 4;
|
||||
file.WriteBytes(conv_buffer.data(), count * 4);
|
||||
audio_size += count * 4;
|
||||
}
|
||||
|
|
|
@ -23,25 +23,23 @@
|
|||
class WaveFileWriter : NonCopyable
|
||||
{
|
||||
public:
|
||||
WaveFileWriter();
|
||||
~WaveFileWriter();
|
||||
WaveFileWriter();
|
||||
~WaveFileWriter();
|
||||
|
||||
bool Start(const std::string& filename, unsigned int HLESampleRate);
|
||||
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; }
|
||||
bool Start(const std::string& filename, unsigned int HLESampleRate);
|
||||
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; }
|
||||
private:
|
||||
static constexpr size_t BUFFER_SIZE = 32 * 1024;
|
||||
static constexpr size_t BUFFER_SIZE = 32 * 1024;
|
||||
|
||||
File::IOFile file;
|
||||
bool skip_silence = false;
|
||||
u32 audio_size = 0;
|
||||
std::array<short, BUFFER_SIZE> conv_buffer{};
|
||||
void Write(u32 value);
|
||||
void Write4(const char* ptr);
|
||||
File::IOFile file;
|
||||
bool skip_silence = false;
|
||||
u32 audio_size = 0;
|
||||
std::array<short, BUFFER_SIZE> conv_buffer{};
|
||||
void Write(u32 value);
|
||||
void Write4(const char* ptr);
|
||||
};
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "AudioCommon/XAudio2Stream.h"
|
||||
#include <xaudio2.h>
|
||||
#include "AudioCommon/AudioCommon.h"
|
||||
#include "AudioCommon/XAudio2Stream.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#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.
|
||||
|
@ -16,29 +16,28 @@
|
|||
struct StreamingVoiceContext : public IXAudio2VoiceCallback
|
||||
{
|
||||
private:
|
||||
CMixer* const m_mixer;
|
||||
Common::Event& m_sound_sync_event;
|
||||
IXAudio2SourceVoice* m_source_voice;
|
||||
std::unique_ptr<BYTE[]> xaudio_buffer;
|
||||
CMixer* const m_mixer;
|
||||
Common::Event& m_sound_sync_event;
|
||||
IXAudio2SourceVoice* m_source_voice;
|
||||
std::unique_ptr<BYTE[]> xaudio_buffer;
|
||||
|
||||
void SubmitBuffer(PBYTE buf_data);
|
||||
void SubmitBuffer(PBYTE buf_data);
|
||||
|
||||
public:
|
||||
StreamingVoiceContext(IXAudio2 *pXAudio2, CMixer *pMixer, Common::Event& pSyncEvent);
|
||||
StreamingVoiceContext(IXAudio2* pXAudio2, CMixer* pMixer, Common::Event& pSyncEvent);
|
||||
|
||||
~StreamingVoiceContext();
|
||||
~StreamingVoiceContext();
|
||||
|
||||
void StreamingVoiceContext::Stop();
|
||||
void StreamingVoiceContext::Play();
|
||||
void StreamingVoiceContext::Stop();
|
||||
void StreamingVoiceContext::Play();
|
||||
|
||||
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
||||
STDMETHOD_(void, OnBufferStart) (void*) {}
|
||||
STDMETHOD_(void, OnLoopEnd) (void*) {}
|
||||
STDMETHOD_(void, OnStreamEnd) () {}
|
||||
|
||||
STDMETHOD_(void, OnBufferEnd) (void* context);
|
||||
STDMETHOD_(void, OnVoiceError)(THIS_ void* pBufferContext, HRESULT Error) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd)() {}
|
||||
STDMETHOD_(void, OnBufferStart)(void*) {}
|
||||
STDMETHOD_(void, OnLoopEnd)(void*) {}
|
||||
STDMETHOD_(void, OnStreamEnd)() {}
|
||||
STDMETHOD_(void, OnBufferEnd)(void* context);
|
||||
};
|
||||
|
||||
const int NUM_BUFFERS = 3;
|
||||
|
@ -50,199 +49,199 @@ const int BUFFER_SIZE_BYTES = BUFFER_SIZE * sizeof(s16);
|
|||
|
||||
void StreamingVoiceContext::SubmitBuffer(PBYTE buf_data)
|
||||
{
|
||||
XAUDIO2_BUFFER buf = {};
|
||||
buf.AudioBytes = BUFFER_SIZE_BYTES;
|
||||
buf.pContext = buf_data;
|
||||
buf.pAudioData = buf_data;
|
||||
XAUDIO2_BUFFER buf = {};
|
||||
buf.AudioBytes = BUFFER_SIZE_BYTES;
|
||||
buf.pContext = 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)
|
||||
: m_mixer(pMixer)
|
||||
, m_sound_sync_event(pSyncEvent)
|
||||
, xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]())
|
||||
StreamingVoiceContext::StreamingVoiceContext(IXAudio2* pXAudio2, CMixer* pMixer,
|
||||
Common::Event& pSyncEvent)
|
||||
: m_mixer(pMixer), m_sound_sync_event(pSyncEvent),
|
||||
xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]())
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE wfx = {};
|
||||
WAVEFORMATEXTENSIBLE wfx = {};
|
||||
|
||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
|
||||
wfx.Format.nChannels = 2;
|
||||
wfx.Format.wBitsPerSample = 16;
|
||||
wfx.Format.nBlockAlign = wfx.Format.nChannels*wfx.Format.wBitsPerSample / 8;
|
||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
wfx.Samples.wValidBitsPerSample = 16;
|
||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
|
||||
wfx.Format.nChannels = 2;
|
||||
wfx.Format.wBitsPerSample = 16;
|
||||
wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
|
||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
wfx.Samples.wValidBitsPerSample = 16;
|
||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
||||
// create source voice
|
||||
HRESULT hr;
|
||||
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC, 1.0f, this)))
|
||||
{
|
||||
PanicAlert("XAudio2 CreateSourceVoice failed: %#X", hr);
|
||||
return;
|
||||
}
|
||||
// create source voice
|
||||
HRESULT hr;
|
||||
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC,
|
||||
1.0f, this)))
|
||||
{
|
||||
PanicAlert("XAudio2 CreateSourceVoice failed: %#X", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
m_source_voice->Start();
|
||||
m_source_voice->Start();
|
||||
|
||||
// start buffers with silence
|
||||
for (int i = 0; i != NUM_BUFFERS; ++i)
|
||||
SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES));
|
||||
// start buffers with silence
|
||||
for (int i = 0; i != NUM_BUFFERS; ++i)
|
||||
SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES));
|
||||
}
|
||||
|
||||
StreamingVoiceContext::~StreamingVoiceContext()
|
||||
{
|
||||
if (m_source_voice)
|
||||
{
|
||||
m_source_voice->Stop();
|
||||
m_source_voice->DestroyVoice();
|
||||
}
|
||||
if (m_source_voice)
|
||||
{
|
||||
m_source_voice->Stop();
|
||||
m_source_voice->DestroyVoice();
|
||||
}
|
||||
}
|
||||
|
||||
void StreamingVoiceContext::Stop()
|
||||
{
|
||||
if (m_source_voice)
|
||||
m_source_voice->Stop();
|
||||
if (m_source_voice)
|
||||
m_source_voice->Stop();
|
||||
}
|
||||
|
||||
void StreamingVoiceContext::Play()
|
||||
{
|
||||
if (m_source_voice)
|
||||
m_source_voice->Start();
|
||||
if (m_source_voice)
|
||||
m_source_voice->Start();
|
||||
}
|
||||
|
||||
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)
|
||||
return;
|
||||
if (!m_source_voice || !context)
|
||||
return;
|
||||
|
||||
//m_sound_sync_event->Wait(); // sync
|
||||
//m_sound_sync_event->Spin(); // or tight sync
|
||||
// m_sound_sync_event->Wait(); // sync
|
||||
// m_sound_sync_event->Spin(); // or tight sync
|
||||
|
||||
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
|
||||
SubmitBuffer(static_cast<BYTE*>(context));
|
||||
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
|
||||
SubmitBuffer(static_cast<BYTE*>(context));
|
||||
}
|
||||
|
||||
HMODULE XAudio2::m_xaudio2_dll = nullptr;
|
||||
typedef decltype(&XAudio2Create) XAudio2Create_t;
|
||||
void *XAudio2::PXAudio2Create = nullptr;
|
||||
void* XAudio2::PXAudio2Create = nullptr;
|
||||
|
||||
bool XAudio2::InitLibrary()
|
||||
{
|
||||
if (m_xaudio2_dll)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (m_xaudio2_dll)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
m_xaudio2_dll = ::LoadLibrary(XAUDIO2_DLL);
|
||||
if (!m_xaudio2_dll)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_xaudio2_dll = ::LoadLibrary(XAUDIO2_DLL);
|
||||
if (!m_xaudio2_dll)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PXAudio2Create)
|
||||
{
|
||||
PXAudio2Create = (XAudio2Create_t)::GetProcAddress(m_xaudio2_dll, "XAudio2Create");
|
||||
if (!PXAudio2Create)
|
||||
{
|
||||
::FreeLibrary(m_xaudio2_dll);
|
||||
m_xaudio2_dll = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!PXAudio2Create)
|
||||
{
|
||||
PXAudio2Create = (XAudio2Create_t)::GetProcAddress(m_xaudio2_dll, "XAudio2Create");
|
||||
if (!PXAudio2Create)
|
||||
{
|
||||
::FreeLibrary(m_xaudio2_dll);
|
||||
m_xaudio2_dll = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
XAudio2::XAudio2()
|
||||
: m_mastering_voice(nullptr)
|
||||
, m_volume(1.0f)
|
||||
, m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
|
||||
: m_mastering_voice(nullptr), m_volume(1.0f),
|
||||
m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
|
||||
{
|
||||
}
|
||||
|
||||
XAudio2::~XAudio2()
|
||||
{
|
||||
Stop();
|
||||
if (m_cleanup_com)
|
||||
CoUninitialize();
|
||||
Stop();
|
||||
if (m_cleanup_com)
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
bool XAudio2::Start()
|
||||
{
|
||||
HRESULT hr;
|
||||
HRESULT hr;
|
||||
|
||||
// callback doesn't seem to run on a specific CPU anyways
|
||||
IXAudio2* xaudptr;
|
||||
if (FAILED(hr = ((XAudio2Create_t)PXAudio2Create)(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
||||
{
|
||||
PanicAlert("XAudio2 init failed: %#X", hr);
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
|
||||
// callback doesn't seem to run on a specific CPU anyways
|
||||
IXAudio2* xaudptr;
|
||||
if (FAILED(hr = ((XAudio2Create_t)PXAudio2Create)(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
||||
{
|
||||
PanicAlert("XAudio2 init failed: %#X", hr);
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
|
||||
|
||||
// XAudio2 master voice
|
||||
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
|
||||
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate())))
|
||||
{
|
||||
PanicAlert("XAudio2 master voice creation failed: %#X", hr);
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
// XAudio2 master voice
|
||||
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
|
||||
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate())))
|
||||
{
|
||||
PanicAlert("XAudio2 master voice creation failed: %#X", hr);
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Volume
|
||||
m_mastering_voice->SetVolume(m_volume);
|
||||
// Volume
|
||||
m_mastering_voice->SetVolume(m_volume);
|
||||
|
||||
m_voice_context = std::unique_ptr<StreamingVoiceContext>
|
||||
(new StreamingVoiceContext(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
|
||||
m_voice_context = std::unique_ptr<StreamingVoiceContext>(
|
||||
new StreamingVoiceContext(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void XAudio2::SetVolume(int volume)
|
||||
{
|
||||
//linear 1- .01
|
||||
m_volume = (float)volume / 100.f;
|
||||
// linear 1- .01
|
||||
m_volume = (float)volume / 100.f;
|
||||
|
||||
if (m_mastering_voice)
|
||||
m_mastering_voice->SetVolume(m_volume);
|
||||
if (m_mastering_voice)
|
||||
m_mastering_voice->SetVolume(m_volume);
|
||||
}
|
||||
|
||||
void XAudio2::Clear(bool mute)
|
||||
{
|
||||
m_muted = mute;
|
||||
m_muted = mute;
|
||||
|
||||
if (m_voice_context)
|
||||
{
|
||||
if (m_muted)
|
||||
m_voice_context->Stop();
|
||||
else
|
||||
m_voice_context->Play();
|
||||
}
|
||||
if (m_voice_context)
|
||||
{
|
||||
if (m_muted)
|
||||
m_voice_context->Stop();
|
||||
else
|
||||
m_voice_context->Play();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
m_mastering_voice->DestroyVoice();
|
||||
m_mastering_voice = nullptr;
|
||||
}
|
||||
if (m_mastering_voice)
|
||||
{
|
||||
m_mastering_voice->DestroyVoice();
|
||||
m_mastering_voice = nullptr;
|
||||
}
|
||||
|
||||
m_xaudio2.reset(); // release interface
|
||||
m_xaudio2.reset(); // release interface
|
||||
|
||||
if (m_xaudio2_dll)
|
||||
{
|
||||
::FreeLibrary(m_xaudio2_dll);
|
||||
m_xaudio2_dll = nullptr;
|
||||
PXAudio2Create = nullptr;
|
||||
}
|
||||
if (m_xaudio2_dll)
|
||||
{
|
||||
::FreeLibrary(m_xaudio2_dll);
|
||||
m_xaudio2_dll = nullptr;
|
||||
PXAudio2Create = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,40 +26,40 @@ class XAudio2 final : public SoundStream
|
|||
#ifdef _WIN32
|
||||
|
||||
private:
|
||||
class Releaser
|
||||
{
|
||||
public:
|
||||
template <typename R>
|
||||
void operator()(R* ptr)
|
||||
{
|
||||
ptr->Release();
|
||||
}
|
||||
};
|
||||
class Releaser
|
||||
{
|
||||
public:
|
||||
template <typename R>
|
||||
void operator()(R* ptr)
|
||||
{
|
||||
ptr->Release();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
|
||||
std::unique_ptr<StreamingVoiceContext> m_voice_context;
|
||||
IXAudio2MasteringVoice *m_mastering_voice;
|
||||
std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
|
||||
std::unique_ptr<StreamingVoiceContext> m_voice_context;
|
||||
IXAudio2MasteringVoice* m_mastering_voice;
|
||||
|
||||
Common::Event m_sound_sync_event;
|
||||
float m_volume;
|
||||
Common::Event m_sound_sync_event;
|
||||
float m_volume;
|
||||
|
||||
const bool m_cleanup_com;
|
||||
const bool m_cleanup_com;
|
||||
|
||||
static HMODULE m_xaudio2_dll;
|
||||
static void *PXAudio2Create;
|
||||
static HMODULE m_xaudio2_dll;
|
||||
static void* PXAudio2Create;
|
||||
|
||||
static bool InitLibrary();
|
||||
static bool InitLibrary();
|
||||
|
||||
public:
|
||||
XAudio2();
|
||||
virtual ~XAudio2();
|
||||
XAudio2();
|
||||
virtual ~XAudio2();
|
||||
|
||||
bool Start() override;
|
||||
void Stop() override;
|
||||
bool Start() override;
|
||||
void Stop() override;
|
||||
|
||||
void Clear(bool mute) override;
|
||||
void SetVolume(int volume) override;
|
||||
void Clear(bool mute) override;
|
||||
void SetVolume(int volume) override;
|
||||
|
||||
static bool isValid() { return InitLibrary(); }
|
||||
static bool isValid() { return InitLibrary(); }
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -10,35 +10,34 @@
|
|||
#include "AudioCommon/AudioCommon.h"
|
||||
#include "AudioCommon/XAudio2_7Stream.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
struct StreamingVoiceContext2_7 : public IXAudio2VoiceCallback
|
||||
{
|
||||
private:
|
||||
CMixer* const m_mixer;
|
||||
Common::Event& m_sound_sync_event;
|
||||
IXAudio2SourceVoice* m_source_voice;
|
||||
std::unique_ptr<BYTE[]> xaudio_buffer;
|
||||
CMixer* const m_mixer;
|
||||
Common::Event& m_sound_sync_event;
|
||||
IXAudio2SourceVoice* m_source_voice;
|
||||
std::unique_ptr<BYTE[]> xaudio_buffer;
|
||||
|
||||
void SubmitBuffer(PBYTE buf_data);
|
||||
void SubmitBuffer(PBYTE buf_data);
|
||||
|
||||
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::Play();
|
||||
void StreamingVoiceContext2_7::Stop();
|
||||
void StreamingVoiceContext2_7::Play();
|
||||
|
||||
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
||||
STDMETHOD_(void, OnBufferStart) (void*) {}
|
||||
STDMETHOD_(void, OnLoopEnd) (void*) {}
|
||||
STDMETHOD_(void, OnStreamEnd) () {}
|
||||
|
||||
STDMETHOD_(void, OnBufferEnd) (void* context);
|
||||
STDMETHOD_(void, OnVoiceError)(THIS_ void* pBufferContext, HRESULT Error) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd)() {}
|
||||
STDMETHOD_(void, OnBufferStart)(void*) {}
|
||||
STDMETHOD_(void, OnLoopEnd)(void*) {}
|
||||
STDMETHOD_(void, OnStreamEnd)() {}
|
||||
STDMETHOD_(void, OnBufferEnd)(void* context);
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
XAUDIO2_BUFFER buf = {};
|
||||
buf.AudioBytes = BUFFER_SIZE_BYTES;
|
||||
buf.pContext = buf_data;
|
||||
buf.pAudioData = buf_data;
|
||||
XAUDIO2_BUFFER buf = {};
|
||||
buf.AudioBytes = BUFFER_SIZE_BYTES;
|
||||
buf.pContext = 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)
|
||||
: m_mixer(pMixer)
|
||||
, m_sound_sync_event(pSyncEvent)
|
||||
, xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]())
|
||||
StreamingVoiceContext2_7::StreamingVoiceContext2_7(IXAudio2* pXAudio2, CMixer* pMixer,
|
||||
Common::Event& pSyncEvent)
|
||||
: m_mixer(pMixer), m_sound_sync_event(pSyncEvent),
|
||||
xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]())
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE wfx = {};
|
||||
WAVEFORMATEXTENSIBLE wfx = {};
|
||||
|
||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
|
||||
wfx.Format.nChannels = 2;
|
||||
wfx.Format.wBitsPerSample = 16;
|
||||
wfx.Format.nBlockAlign = wfx.Format.nChannels*wfx.Format.wBitsPerSample / 8;
|
||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
wfx.Samples.wValidBitsPerSample = 16;
|
||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
|
||||
wfx.Format.nChannels = 2;
|
||||
wfx.Format.wBitsPerSample = 16;
|
||||
wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
|
||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
wfx.Samples.wValidBitsPerSample = 16;
|
||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
||||
// create source voice
|
||||
HRESULT hr;
|
||||
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC, 1.0f, this)))
|
||||
{
|
||||
PanicAlert("XAudio2_7 CreateSourceVoice failed: %#X", hr);
|
||||
return;
|
||||
}
|
||||
// create source voice
|
||||
HRESULT hr;
|
||||
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC,
|
||||
1.0f, this)))
|
||||
{
|
||||
PanicAlert("XAudio2_7 CreateSourceVoice failed: %#X", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
m_source_voice->Start();
|
||||
m_source_voice->Start();
|
||||
|
||||
// start buffers with silence
|
||||
for (int i = 0; i != NUM_BUFFERS; ++i)
|
||||
SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES));
|
||||
// start buffers with silence
|
||||
for (int i = 0; i != NUM_BUFFERS; ++i)
|
||||
SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES));
|
||||
}
|
||||
|
||||
StreamingVoiceContext2_7::~StreamingVoiceContext2_7()
|
||||
{
|
||||
if (m_source_voice)
|
||||
{
|
||||
m_source_voice->Stop();
|
||||
m_source_voice->DestroyVoice();
|
||||
}
|
||||
if (m_source_voice)
|
||||
{
|
||||
m_source_voice->Stop();
|
||||
m_source_voice->DestroyVoice();
|
||||
}
|
||||
}
|
||||
|
||||
void StreamingVoiceContext2_7::Stop()
|
||||
{
|
||||
if (m_source_voice)
|
||||
m_source_voice->Stop();
|
||||
if (m_source_voice)
|
||||
m_source_voice->Stop();
|
||||
}
|
||||
|
||||
void StreamingVoiceContext2_7::Play()
|
||||
{
|
||||
if (m_source_voice)
|
||||
m_source_voice->Start();
|
||||
if (m_source_voice)
|
||||
m_source_voice->Start();
|
||||
}
|
||||
|
||||
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)
|
||||
return;
|
||||
if (!m_source_voice || !context)
|
||||
return;
|
||||
|
||||
//m_sound_sync_event->Wait(); // sync
|
||||
//m_sound_sync_event->Spin(); // or tight sync
|
||||
// m_sound_sync_event->Wait(); // sync
|
||||
// m_sound_sync_event->Spin(); // or tight sync
|
||||
|
||||
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
|
||||
SubmitBuffer(static_cast<BYTE*>(context));
|
||||
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
|
||||
SubmitBuffer(static_cast<BYTE*>(context));
|
||||
}
|
||||
|
||||
HMODULE XAudio2_7::m_xaudio2_dll = nullptr;
|
||||
|
||||
void XAudio2_7::ReleaseIXAudio2(IXAudio2* ptr)
|
||||
{
|
||||
ptr->Release();
|
||||
ptr->Release();
|
||||
}
|
||||
|
||||
bool XAudio2_7::InitLibrary()
|
||||
{
|
||||
if (m_xaudio2_dll)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (m_xaudio2_dll)
|
||||
{
|
||||
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()
|
||||
: m_mastering_voice(nullptr)
|
||||
, m_volume(1.0f)
|
||||
, m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
|
||||
: m_mastering_voice(nullptr), m_volume(1.0f),
|
||||
m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
|
||||
{
|
||||
}
|
||||
|
||||
XAudio2_7::~XAudio2_7()
|
||||
{
|
||||
Stop();
|
||||
if (m_cleanup_com)
|
||||
CoUninitialize();
|
||||
Stop();
|
||||
if (m_cleanup_com)
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
bool XAudio2_7::Start()
|
||||
{
|
||||
HRESULT hr;
|
||||
HRESULT hr;
|
||||
|
||||
// callback doesn't seem to run on a specific CPU anyways
|
||||
IXAudio2* xaudptr;
|
||||
if (FAILED(hr = XAudio2Create(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
||||
{
|
||||
PanicAlert("XAudio2_7 init failed: %#X", hr);
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
|
||||
// callback doesn't seem to run on a specific CPU anyways
|
||||
IXAudio2* xaudptr;
|
||||
if (FAILED(hr = XAudio2Create(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
||||
{
|
||||
PanicAlert("XAudio2_7 init failed: %#X", hr);
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
|
||||
|
||||
// XAudio2 master voice
|
||||
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
|
||||
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate())))
|
||||
{
|
||||
PanicAlert("XAudio2_7 master voice creation failed: %#X", hr);
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
// XAudio2 master voice
|
||||
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
|
||||
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate())))
|
||||
{
|
||||
PanicAlert("XAudio2_7 master voice creation failed: %#X", hr);
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Volume
|
||||
m_mastering_voice->SetVolume(m_volume);
|
||||
// Volume
|
||||
m_mastering_voice->SetVolume(m_volume);
|
||||
|
||||
m_voice_context = std::unique_ptr<StreamingVoiceContext2_7>
|
||||
(new StreamingVoiceContext2_7(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
|
||||
m_voice_context = std::unique_ptr<StreamingVoiceContext2_7>(
|
||||
new StreamingVoiceContext2_7(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void XAudio2_7::SetVolume(int volume)
|
||||
{
|
||||
//linear 1- .01
|
||||
m_volume = (float)volume / 100.f;
|
||||
// linear 1- .01
|
||||
m_volume = (float)volume / 100.f;
|
||||
|
||||
if (m_mastering_voice)
|
||||
m_mastering_voice->SetVolume(m_volume);
|
||||
if (m_mastering_voice)
|
||||
m_mastering_voice->SetVolume(m_volume);
|
||||
}
|
||||
|
||||
void XAudio2_7::Clear(bool mute)
|
||||
{
|
||||
m_muted = mute;
|
||||
m_muted = mute;
|
||||
|
||||
if (m_voice_context)
|
||||
{
|
||||
if (m_muted)
|
||||
m_voice_context->Stop();
|
||||
else
|
||||
m_voice_context->Play();
|
||||
}
|
||||
if (m_voice_context)
|
||||
{
|
||||
if (m_muted)
|
||||
m_voice_context->Stop();
|
||||
else
|
||||
m_voice_context->Play();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
m_mastering_voice->DestroyVoice();
|
||||
m_mastering_voice = nullptr;
|
||||
}
|
||||
if (m_mastering_voice)
|
||||
{
|
||||
m_mastering_voice->DestroyVoice();
|
||||
m_mastering_voice = nullptr;
|
||||
}
|
||||
|
||||
m_xaudio2.reset(); // release interface
|
||||
m_xaudio2.reset(); // release interface
|
||||
|
||||
if (m_xaudio2_dll)
|
||||
{
|
||||
::FreeLibrary(m_xaudio2_dll);
|
||||
m_xaudio2_dll = nullptr;
|
||||
}
|
||||
if (m_xaudio2_dll)
|
||||
{
|
||||
::FreeLibrary(m_xaudio2_dll);
|
||||
m_xaudio2_dll = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,41 +32,41 @@ class XAudio2_7 final : public SoundStream
|
|||
#ifdef _WIN32
|
||||
|
||||
private:
|
||||
static void ReleaseIXAudio2(IXAudio2 *ptr);
|
||||
static void ReleaseIXAudio2(IXAudio2* ptr);
|
||||
|
||||
class Releaser
|
||||
{
|
||||
public:
|
||||
template <typename R>
|
||||
void operator()(R *ptr)
|
||||
{
|
||||
ReleaseIXAudio2(ptr);
|
||||
}
|
||||
};
|
||||
class Releaser
|
||||
{
|
||||
public:
|
||||
template <typename R>
|
||||
void operator()(R* ptr)
|
||||
{
|
||||
ReleaseIXAudio2(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
|
||||
std::unique_ptr<StreamingVoiceContext2_7> m_voice_context;
|
||||
IXAudio2MasteringVoice *m_mastering_voice;
|
||||
std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
|
||||
std::unique_ptr<StreamingVoiceContext2_7> m_voice_context;
|
||||
IXAudio2MasteringVoice* m_mastering_voice;
|
||||
|
||||
Common::Event m_sound_sync_event;
|
||||
float m_volume;
|
||||
Common::Event m_sound_sync_event;
|
||||
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:
|
||||
XAudio2_7();
|
||||
virtual ~XAudio2_7();
|
||||
XAudio2_7();
|
||||
virtual ~XAudio2_7();
|
||||
|
||||
bool Start() override;
|
||||
void Stop() override;
|
||||
bool Start() override;
|
||||
void Stop() override;
|
||||
|
||||
void Clear(bool mute) override;
|
||||
void SetVolume(int volume) override;
|
||||
void Clear(bool mute) override;
|
||||
void SetVolume(int volume) override;
|
||||
|
||||
static bool isValid() { return InitLibrary(); }
|
||||
static bool isValid() { return InitLibrary(); }
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -6,23 +6,35 @@
|
|||
* Copyright (c) 2006, Creative Labs Inc.
|
||||
* 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:
|
||||
*
|
||||
* * 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.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
|
||||
* 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
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* 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
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
@ -42,101 +54,104 @@
|
|||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Init call
|
||||
*/
|
||||
ALDeviceList::ALDeviceList()
|
||||
{
|
||||
ALDEVICEINFO ALDeviceInfo;
|
||||
ALDEVICEINFO ALDeviceInfo;
|
||||
|
||||
// DeviceInfo vector stores, for each enumerated device, it's device name, selection status, spec version #, and extension support
|
||||
vDeviceInfo.clear();
|
||||
vDeviceInfo.reserve(10);
|
||||
// DeviceInfo vector stores, for each enumerated device, it's device name, selection status, spec
|
||||
// version #, and extension support
|
||||
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
|
||||
//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);
|
||||
// go through device list (each device terminated with a single nullptr, list terminated with double nullptr)
|
||||
for (s32 index = 0; devices != nullptr && strlen(devices) > 0; index++, devices += strlen(devices) + 1)
|
||||
{
|
||||
if (strcmp(defaultDeviceName, devices) == 0)
|
||||
{
|
||||
defaultDeviceIndex = index;
|
||||
}
|
||||
ALCdevice *device = alcOpenDevice(devices);
|
||||
if (device)
|
||||
{
|
||||
ALCcontext *context = alcCreateContext(device, nullptr);
|
||||
if (context)
|
||||
{
|
||||
alcMakeContextCurrent(context);
|
||||
// if new actual device name isn't already in the list, then add it...
|
||||
const char *actualDeviceName = alcGetString(device, ALC_DEVICE_SPECIFIER);
|
||||
bool bNewName = true;
|
||||
for (s32 i = 0; i < GetNumDevices(); i++)
|
||||
{
|
||||
if (strcmp(GetDeviceName(i), actualDeviceName) == 0)
|
||||
{
|
||||
bNewName = false;
|
||||
}
|
||||
}
|
||||
if ((bNewName) && (actualDeviceName != nullptr) && (strlen(actualDeviceName) > 0))
|
||||
{
|
||||
ALDeviceInfo.bSelected = true;
|
||||
ALDeviceInfo.strDeviceName = actualDeviceName;
|
||||
alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(s32), &ALDeviceInfo.iMajorVersion);
|
||||
alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(s32), &ALDeviceInfo.iMinorVersion);
|
||||
// grab function pointers for 1.0-API functions, and if successful proceed to enumerate all
|
||||
// devices
|
||||
// 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);
|
||||
// go through device list (each device terminated with a single nullptr, list terminated with
|
||||
// double nullptr)
|
||||
for (s32 index = 0; devices != nullptr && strlen(devices) > 0;
|
||||
index++, devices += strlen(devices) + 1)
|
||||
{
|
||||
if (strcmp(defaultDeviceName, devices) == 0)
|
||||
{
|
||||
defaultDeviceIndex = index;
|
||||
}
|
||||
ALCdevice* device = alcOpenDevice(devices);
|
||||
if (device)
|
||||
{
|
||||
ALCcontext* context = alcCreateContext(device, nullptr);
|
||||
if (context)
|
||||
{
|
||||
alcMakeContextCurrent(context);
|
||||
// if new actual device name isn't already in the list, then add it...
|
||||
const char* actualDeviceName = alcGetString(device, ALC_DEVICE_SPECIFIER);
|
||||
bool bNewName = true;
|
||||
for (s32 i = 0; i < GetNumDevices(); i++)
|
||||
{
|
||||
if (strcmp(GetDeviceName(i), actualDeviceName) == 0)
|
||||
{
|
||||
bNewName = false;
|
||||
}
|
||||
}
|
||||
if ((bNewName) && (actualDeviceName != nullptr) && (strlen(actualDeviceName) > 0))
|
||||
{
|
||||
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
|
||||
if (alcIsExtensionPresent(device, "ALC_EXT_CAPTURE") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_CAPTURE");
|
||||
if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_EFX");
|
||||
// Check for ALC Extensions
|
||||
if (alcIsExtensionPresent(device, "ALC_EXT_CAPTURE") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_CAPTURE");
|
||||
if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_EFX");
|
||||
|
||||
// Check for AL Extensions
|
||||
if (alIsExtensionPresent("AL_EXT_OFFSET") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_OFFSET");
|
||||
// Check for AL Extensions
|
||||
if (alIsExtensionPresent("AL_EXT_OFFSET") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_OFFSET");
|
||||
|
||||
if (alIsExtensionPresent("AL_EXT_LINEAR_DISTANCE") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_LINEAR_DISTANCE");
|
||||
if (alIsExtensionPresent("AL_EXT_EXPONENT_DISTANCE") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_EXPONENT_DISTANCE");
|
||||
if (alIsExtensionPresent("AL_EXT_LINEAR_DISTANCE") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_LINEAR_DISTANCE");
|
||||
if (alIsExtensionPresent("AL_EXT_EXPONENT_DISTANCE") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_EXPONENT_DISTANCE");
|
||||
|
||||
if (alIsExtensionPresent("EAX2.0") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("EAX2.0");
|
||||
if (alIsExtensionPresent("EAX3.0") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("EAX3.0");
|
||||
if (alIsExtensionPresent("EAX4.0") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("EAX4.0");
|
||||
if (alIsExtensionPresent("EAX5.0") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("EAX5.0");
|
||||
if (alIsExtensionPresent("EAX2.0") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("EAX2.0");
|
||||
if (alIsExtensionPresent("EAX3.0") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("EAX3.0");
|
||||
if (alIsExtensionPresent("EAX4.0") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("EAX4.0");
|
||||
if (alIsExtensionPresent("EAX5.0") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("EAX5.0");
|
||||
|
||||
if (alIsExtensionPresent("EAX-RAM") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("EAX-RAM");
|
||||
if (alIsExtensionPresent("EAX-RAM") == AL_TRUE)
|
||||
ALDeviceInfo.pvstrExtensions->push_back("EAX-RAM");
|
||||
|
||||
// Get Source Count
|
||||
ALDeviceInfo.uiSourceCount = GetMaxNumSources();
|
||||
// Get Source Count
|
||||
ALDeviceInfo.uiSourceCount = GetMaxNumSources();
|
||||
|
||||
vDeviceInfo.push_back(ALDeviceInfo);
|
||||
}
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(context);
|
||||
}
|
||||
alcCloseDevice(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
vDeviceInfo.push_back(ALDeviceInfo);
|
||||
}
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(context);
|
||||
}
|
||||
alcCloseDevice(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
ResetFilters();
|
||||
ResetFilters();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -144,16 +159,16 @@ ALDeviceList::ALDeviceList()
|
|||
*/
|
||||
ALDeviceList::~ALDeviceList()
|
||||
{
|
||||
for (auto& di : vDeviceInfo)
|
||||
{
|
||||
if (di.pvstrExtensions)
|
||||
{
|
||||
di.pvstrExtensions->clear();
|
||||
delete di.pvstrExtensions;
|
||||
}
|
||||
}
|
||||
for (auto& di : vDeviceInfo)
|
||||
{
|
||||
if (di.pvstrExtensions)
|
||||
{
|
||||
di.pvstrExtensions->clear();
|
||||
delete di.pvstrExtensions;
|
||||
}
|
||||
}
|
||||
|
||||
vDeviceInfo.clear();
|
||||
vDeviceInfo.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -161,32 +176,33 @@ ALDeviceList::~ALDeviceList()
|
|||
*/
|
||||
s32 ALDeviceList::GetNumDevices()
|
||||
{
|
||||
return (s32)vDeviceInfo.size();
|
||||
return (s32)vDeviceInfo.size();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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())
|
||||
return (char *)vDeviceInfo[index].strDeviceName.c_str();
|
||||
else
|
||||
return nullptr;
|
||||
if (index < GetNumDevices())
|
||||
return (char*)vDeviceInfo[index].strDeviceName.c_str();
|
||||
else
|
||||
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 (major)
|
||||
*major = vDeviceInfo[index].iMajorVersion;
|
||||
if (minor)
|
||||
*minor = vDeviceInfo[index].iMinorVersion;
|
||||
}
|
||||
if (index < GetNumDevices())
|
||||
{
|
||||
if (major)
|
||||
*major = vDeviceInfo[index].iMajorVersion;
|
||||
if (minor)
|
||||
*minor = vDeviceInfo[index].iMinorVersion;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -194,32 +210,32 @@ void ALDeviceList::GetDeviceVersion(s32 index, s32 *major, s32 *minor)
|
|||
*/
|
||||
u32 ALDeviceList::GetMaxNumSources(s32 index)
|
||||
{
|
||||
if (index < GetNumDevices())
|
||||
return vDeviceInfo[index].uiSourceCount;
|
||||
else
|
||||
return 0;
|
||||
if (index < GetNumDevices())
|
||||
return vDeviceInfo[index].uiSourceCount;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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())
|
||||
{
|
||||
for (auto& ext : *vDeviceInfo[index].pvstrExtensions)
|
||||
{
|
||||
if (!strcasecmp(ext.c_str(), szExtName))
|
||||
{
|
||||
bReturn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index < GetNumDevices())
|
||||
{
|
||||
for (auto& ext : *vDeviceInfo[index].pvstrExtensions)
|
||||
{
|
||||
if (!strcasecmp(ext.c_str(), szExtName))
|
||||
{
|
||||
bReturn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bReturn;
|
||||
return bReturn;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -227,7 +243,7 @@ bool ALDeviceList::IsExtensionSupported(s32 index, char *szExtName)
|
|||
*/
|
||||
s32 ALDeviceList::GetDefaultDevice()
|
||||
{
|
||||
return defaultDeviceIndex;
|
||||
return defaultDeviceIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -235,15 +251,15 @@ s32 ALDeviceList::GetDefaultDevice()
|
|||
*/
|
||||
void ALDeviceList::FilterDevicesMinVer(s32 major, s32 minor)
|
||||
{
|
||||
s32 dMajor = 0, dMinor = 0;
|
||||
for (u32 i = 0; i < vDeviceInfo.size(); i++)
|
||||
{
|
||||
GetDeviceVersion(i, &dMajor, &dMinor);
|
||||
if ((dMajor < major) || ((dMajor == major) && (dMinor < minor)))
|
||||
{
|
||||
vDeviceInfo[i].bSelected = false;
|
||||
}
|
||||
}
|
||||
s32 dMajor = 0, dMinor = 0;
|
||||
for (u32 i = 0; i < vDeviceInfo.size(); i++)
|
||||
{
|
||||
GetDeviceVersion(i, &dMajor, &dMinor);
|
||||
if ((dMajor < major) || ((dMajor == major) && (dMinor < minor)))
|
||||
{
|
||||
vDeviceInfo[i].bSelected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -251,39 +267,39 @@ void ALDeviceList::FilterDevicesMinVer(s32 major, s32 minor)
|
|||
*/
|
||||
void ALDeviceList::FilterDevicesMaxVer(s32 major, s32 minor)
|
||||
{
|
||||
s32 dMajor = 0, dMinor = 0;
|
||||
for (u32 i = 0; i < vDeviceInfo.size(); i++)
|
||||
{
|
||||
GetDeviceVersion(i, &dMajor, &dMinor);
|
||||
if ((dMajor > major) || ((dMajor == major) && (dMinor > minor)))
|
||||
{
|
||||
vDeviceInfo[i].bSelected = false;
|
||||
}
|
||||
}
|
||||
s32 dMajor = 0, dMinor = 0;
|
||||
for (u32 i = 0; i < vDeviceInfo.size(); i++)
|
||||
{
|
||||
GetDeviceVersion(i, &dMajor, &dMinor);
|
||||
if ((dMajor > major) || ((dMajor == major) && (dMinor > minor)))
|
||||
{
|
||||
vDeviceInfo[i].bSelected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
bFound = false;
|
||||
for (auto& ext : *di.pvstrExtensions)
|
||||
{
|
||||
if (!strcasecmp(ext.c_str(), szExtName))
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto& di : vDeviceInfo)
|
||||
{
|
||||
bFound = false;
|
||||
for (auto& ext : *di.pvstrExtensions)
|
||||
{
|
||||
if (!strcasecmp(ext.c_str(), szExtName))
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bFound)
|
||||
di.bSelected = false;
|
||||
}
|
||||
if (!bFound)
|
||||
di.bSelected = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -291,12 +307,12 @@ void ALDeviceList::FilterDevicesExtension(char *szExtName)
|
|||
*/
|
||||
void ALDeviceList::ResetFilters()
|
||||
{
|
||||
for (s32 i = 0; i < GetNumDevices(); i++)
|
||||
{
|
||||
vDeviceInfo[i].bSelected = true;
|
||||
}
|
||||
for (s32 i = 0; i < GetNumDevices(); i++)
|
||||
{
|
||||
vDeviceInfo[i].bSelected = true;
|
||||
}
|
||||
|
||||
filterIndex = 0;
|
||||
filterIndex = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -304,18 +320,18 @@ void ALDeviceList::ResetFilters()
|
|||
*/
|
||||
s32 ALDeviceList::GetFirstFilteredDevice()
|
||||
{
|
||||
s32 i;
|
||||
s32 i;
|
||||
|
||||
for (i = 0; i < GetNumDevices(); i++)
|
||||
{
|
||||
if (vDeviceInfo[i].bSelected == true)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < GetNumDevices(); i++)
|
||||
{
|
||||
if (vDeviceInfo[i].bSelected == true)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
filterIndex = i + 1;
|
||||
return i;
|
||||
filterIndex = i + 1;
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -323,18 +339,18 @@ s32 ALDeviceList::GetFirstFilteredDevice()
|
|||
*/
|
||||
s32 ALDeviceList::GetNextFilteredDevice()
|
||||
{
|
||||
s32 i;
|
||||
s32 i;
|
||||
|
||||
for (i = filterIndex; i < GetNumDevices(); i++)
|
||||
{
|
||||
if (vDeviceInfo[i].bSelected == true)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = filterIndex; i < GetNumDevices(); i++)
|
||||
{
|
||||
if (vDeviceInfo[i].bSelected == true)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
filterIndex = i + 1;
|
||||
return i;
|
||||
filterIndex = i + 1;
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -342,29 +358,29 @@ s32 ALDeviceList::GetNextFilteredDevice()
|
|||
*/
|
||||
u32 ALDeviceList::GetMaxNumSources()
|
||||
{
|
||||
ALuint uiSources[256];
|
||||
u32 iSourceCount = 0;
|
||||
ALuint uiSources[256];
|
||||
u32 iSourceCount = 0;
|
||||
|
||||
// Clear AL Error Code
|
||||
alGetError();
|
||||
// Clear AL Error Code
|
||||
alGetError();
|
||||
|
||||
// Generate up to 256 Sources, checking for any errors
|
||||
for (iSourceCount = 0; iSourceCount < 256; iSourceCount++)
|
||||
{
|
||||
alGenSources(1, &uiSources[iSourceCount]);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
break;
|
||||
}
|
||||
// Generate up to 256 Sources, checking for any errors
|
||||
for (iSourceCount = 0; iSourceCount < 256; iSourceCount++)
|
||||
{
|
||||
alGenSources(1, &uiSources[iSourceCount]);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
break;
|
||||
}
|
||||
|
||||
// Release the Sources
|
||||
alDeleteSources(iSourceCount, uiSources);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
for (auto& uiSource : uiSources)
|
||||
{
|
||||
alDeleteSources(1, &uiSource);
|
||||
}
|
||||
}
|
||||
// Release the Sources
|
||||
alDeleteSources(iSourceCount, uiSources);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
for (auto& uiSource : uiSources)
|
||||
{
|
||||
alDeleteSources(1, &uiSource);
|
||||
}
|
||||
}
|
||||
|
||||
return iSourceCount;
|
||||
return iSourceCount;
|
||||
}
|
||||
|
|
|
@ -10,43 +10,43 @@
|
|||
#include "Common/CommonTypes.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(disable: 4786) //disable warning "identifier was truncated to
|
||||
//'255' characters in the browser information"
|
||||
#pragma warning(disable : 4786) // disable warning "identifier was truncated to
|
||||
//'255' characters in the browser information"
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
std::string strDeviceName;
|
||||
s32 iMajorVersion;
|
||||
s32 iMinorVersion;
|
||||
u32 uiSourceCount;
|
||||
std::vector<std::string>* pvstrExtensions;
|
||||
bool bSelected;
|
||||
std::string strDeviceName;
|
||||
s32 iMajorVersion;
|
||||
s32 iMinorVersion;
|
||||
u32 uiSourceCount;
|
||||
std::vector<std::string>* pvstrExtensions;
|
||||
bool bSelected;
|
||||
} ALDEVICEINFO, *LPALDEVICEINFO;
|
||||
|
||||
class ALDeviceList
|
||||
{
|
||||
private:
|
||||
std::vector<ALDEVICEINFO> vDeviceInfo;
|
||||
s32 defaultDeviceIndex;
|
||||
s32 filterIndex;
|
||||
std::vector<ALDEVICEINFO> vDeviceInfo;
|
||||
s32 defaultDeviceIndex;
|
||||
s32 filterIndex;
|
||||
|
||||
public:
|
||||
ALDeviceList();
|
||||
~ALDeviceList();
|
||||
s32 GetNumDevices();
|
||||
char* GetDeviceName(s32 index);
|
||||
void GetDeviceVersion(s32 index, s32* major, s32* minor);
|
||||
u32 GetMaxNumSources(s32 index);
|
||||
bool IsExtensionSupported(s32 index, char* szExtName);
|
||||
s32 GetDefaultDevice();
|
||||
void FilterDevicesMinVer(s32 major, s32 minor);
|
||||
void FilterDevicesMaxVer(s32 major, s32 minor);
|
||||
void FilterDevicesExtension(char* szExtName);
|
||||
void ResetFilters();
|
||||
s32 GetFirstFilteredDevice();
|
||||
s32 GetNextFilteredDevice();
|
||||
ALDeviceList();
|
||||
~ALDeviceList();
|
||||
s32 GetNumDevices();
|
||||
char* GetDeviceName(s32 index);
|
||||
void GetDeviceVersion(s32 index, s32* major, s32* minor);
|
||||
u32 GetMaxNumSources(s32 index);
|
||||
bool IsExtensionSupported(s32 index, char* szExtName);
|
||||
s32 GetDefaultDevice();
|
||||
void FilterDevicesMinVer(s32 major, s32 minor);
|
||||
void FilterDevicesMaxVer(s32 major, s32 minor);
|
||||
void FilterDevicesExtension(char* szExtName);
|
||||
void ResetFilters();
|
||||
s32 GetFirstFilteredDevice();
|
||||
s32 GetNextFilteredDevice();
|
||||
|
||||
private:
|
||||
u32 GetMaxNumSources();
|
||||
u32 GetMaxNumSources();
|
||||
};
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <curl/curl.h>
|
||||
#include <string>
|
||||
|
||||
#include "Common/Analytics.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
@ -23,211 +23,201 @@ constexpr u8 WIRE_FORMAT_VERSION = 0;
|
|||
// format.
|
||||
enum class TypeId : u8
|
||||
{
|
||||
STRING = 0,
|
||||
BOOL = 1,
|
||||
UINT = 2,
|
||||
SINT = 3,
|
||||
FLOAT = 4,
|
||||
STRING = 0,
|
||||
BOOL = 1,
|
||||
UINT = 2,
|
||||
SINT = 3,
|
||||
FLOAT = 4,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
do
|
||||
{
|
||||
u8 current_byte = v & 0x7F;
|
||||
v >>= 7;
|
||||
current_byte |= (!!v) << 7;
|
||||
out->push_back(current_byte);
|
||||
}
|
||||
while (v);
|
||||
do
|
||||
{
|
||||
u8 current_byte = v & 0x7F;
|
||||
v >>= 7;
|
||||
current_byte |= (!!v) << 7;
|
||||
out->push_back(current_byte);
|
||||
} while (v);
|
||||
}
|
||||
|
||||
void AppendBytes(std::string* out, const u8* bytes, u32 length,
|
||||
bool encode_length = true)
|
||||
void AppendBytes(std::string* out, const u8* bytes, u32 length, bool encode_length = true)
|
||||
{
|
||||
if (encode_length)
|
||||
{
|
||||
AppendVarInt(out, length);
|
||||
}
|
||||
out->append(reinterpret_cast<const char*>(bytes), length);
|
||||
if (encode_length)
|
||||
{
|
||||
AppendVarInt(out, length);
|
||||
}
|
||||
out->append(reinterpret_cast<const char*>(bytes), length);
|
||||
}
|
||||
|
||||
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.
|
||||
size_t DummyCurlWriteFunction(char* ptr, size_t size, size_t nmemb, void* userdata)
|
||||
{
|
||||
return size * nmemb;
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AnalyticsReportBuilder::AnalyticsReportBuilder()
|
||||
{
|
||||
m_report.push_back(WIRE_FORMAT_VERSION);
|
||||
m_report.push_back(WIRE_FORMAT_VERSION);
|
||||
}
|
||||
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report,
|
||||
const std::string& v)
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, const std::string& v)
|
||||
{
|
||||
AppendType(report, TypeId::STRING);
|
||||
AppendBytes(report, reinterpret_cast<const u8*>(v.data()), static_cast<u32>(v.size()));
|
||||
AppendType(report, TypeId::STRING);
|
||||
AppendBytes(report, reinterpret_cast<const u8*>(v.data()), static_cast<u32>(v.size()));
|
||||
}
|
||||
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report,
|
||||
const char* v)
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, const char* v)
|
||||
{
|
||||
AppendSerializedValue(report, std::string(v));
|
||||
AppendSerializedValue(report, std::string(v));
|
||||
}
|
||||
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report,
|
||||
bool v)
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, bool v)
|
||||
{
|
||||
AppendType(report, TypeId::BOOL);
|
||||
AppendBool(report, v);
|
||||
AppendType(report, TypeId::BOOL);
|
||||
AppendBool(report, v);
|
||||
}
|
||||
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report,
|
||||
u64 v)
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, u64 v)
|
||||
{
|
||||
AppendType(report, TypeId::UINT);
|
||||
AppendVarInt(report, v);
|
||||
AppendType(report, TypeId::UINT);
|
||||
AppendVarInt(report, v);
|
||||
}
|
||||
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report,
|
||||
s64 v)
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, s64 v)
|
||||
{
|
||||
AppendType(report, TypeId::SINT);
|
||||
AppendBool(report, v >= 0);
|
||||
AppendVarInt(report, static_cast<u64>(std::abs(v)));
|
||||
AppendType(report, TypeId::SINT);
|
||||
AppendBool(report, v >= 0);
|
||||
AppendVarInt(report, static_cast<u64>(std::abs(v)));
|
||||
}
|
||||
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report,
|
||||
u32 v)
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, u32 v)
|
||||
{
|
||||
AppendSerializedValue(report, static_cast<u64>(v));
|
||||
AppendSerializedValue(report, static_cast<u64>(v));
|
||||
}
|
||||
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report,
|
||||
s32 v)
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, s32 v)
|
||||
{
|
||||
AppendSerializedValue(report, static_cast<s64>(v));
|
||||
AppendSerializedValue(report, static_cast<s64>(v));
|
||||
}
|
||||
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report,
|
||||
float v)
|
||||
void AnalyticsReportBuilder::AppendSerializedValue(std::string* report, float v)
|
||||
{
|
||||
AppendType(report, TypeId::FLOAT);
|
||||
AppendBytes(report, reinterpret_cast<u8*>(&v), sizeof (v), false);
|
||||
AppendType(report, TypeId::FLOAT);
|
||||
AppendBytes(report, reinterpret_cast<u8*>(&v), sizeof(v), false);
|
||||
}
|
||||
|
||||
AnalyticsReporter::AnalyticsReporter()
|
||||
{
|
||||
m_reporter_thread = std::thread(&AnalyticsReporter::ThreadProc, this);
|
||||
m_reporter_thread = std::thread(&AnalyticsReporter::ThreadProc, this);
|
||||
}
|
||||
|
||||
AnalyticsReporter::~AnalyticsReporter()
|
||||
{
|
||||
// Set the exit request flag and wait for the thread to honor it.
|
||||
m_reporter_stop_request.Set();
|
||||
m_reporter_event.Set();
|
||||
m_reporter_thread.join();
|
||||
// Set the exit request flag and wait for the thread to honor it.
|
||||
m_reporter_stop_request.Set();
|
||||
m_reporter_event.Set();
|
||||
m_reporter_thread.join();
|
||||
}
|
||||
|
||||
void AnalyticsReporter::Send(AnalyticsReportBuilder&& report)
|
||||
{
|
||||
// Put a bound on the size of the queue to avoid uncontrolled memory growth.
|
||||
constexpr u32 QUEUE_SIZE_LIMIT = 25;
|
||||
if (m_reports_queue.Size() < QUEUE_SIZE_LIMIT)
|
||||
{
|
||||
m_reports_queue.Push(report.Consume());
|
||||
m_reporter_event.Set();
|
||||
}
|
||||
// Put a bound on the size of the queue to avoid uncontrolled memory growth.
|
||||
constexpr u32 QUEUE_SIZE_LIMIT = 25;
|
||||
if (m_reports_queue.Size() < QUEUE_SIZE_LIMIT)
|
||||
{
|
||||
m_reports_queue.Push(report.Consume());
|
||||
m_reporter_event.Set();
|
||||
}
|
||||
}
|
||||
|
||||
void AnalyticsReporter::ThreadProc()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
m_reporter_event.Wait();
|
||||
if (m_reporter_stop_request.IsSet())
|
||||
{
|
||||
return;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
m_reporter_event.Wait();
|
||||
if (m_reporter_stop_request.IsSet())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (!m_reports_queue.Empty())
|
||||
{
|
||||
std::shared_ptr<AnalyticsReportingBackend> backend(m_backend);
|
||||
while (!m_reports_queue.Empty())
|
||||
{
|
||||
std::shared_ptr<AnalyticsReportingBackend> backend(m_backend);
|
||||
|
||||
if (backend)
|
||||
{
|
||||
std::string report;
|
||||
m_reports_queue.Pop(report);
|
||||
backend->Send(std::move(report));
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (backend)
|
||||
{
|
||||
std::string report;
|
||||
m_reports_queue.Pop(report);
|
||||
backend->Send(std::move(report));
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Recheck after each report sent.
|
||||
if (m_reporter_stop_request.IsSet())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Recheck after each report sent.
|
||||
if (m_reporter_stop_request.IsSet())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StdoutAnalyticsBackend::Send(std::string report)
|
||||
{
|
||||
printf("Analytics report sent:\n%s", HexDump(
|
||||
reinterpret_cast<const u8*>(report.data()), report.size()).c_str());
|
||||
printf("Analytics report sent:\n%s",
|
||||
HexDump(reinterpret_cast<const u8*>(report.data()), report.size()).c_str());
|
||||
}
|
||||
|
||||
HttpAnalyticsBackend::HttpAnalyticsBackend(const std::string& endpoint)
|
||||
{
|
||||
CURL* curl = curl_easy_init();
|
||||
if (curl)
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POST, true);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &DummyCurlWriteFunction);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 3000);
|
||||
CURL* curl = curl_easy_init();
|
||||
if (curl)
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POST, true);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &DummyCurlWriteFunction);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 3000);
|
||||
|
||||
#ifdef _WIN32
|
||||
// ALPN support is enabled by default but requires Windows >= 8.1.
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, false);
|
||||
// ALPN support is enabled by default but requires Windows >= 8.1.
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, false);
|
||||
#endif
|
||||
|
||||
m_curl = curl;
|
||||
}
|
||||
m_curl = curl;
|
||||
}
|
||||
}
|
||||
|
||||
HttpAnalyticsBackend::~HttpAnalyticsBackend()
|
||||
{
|
||||
if (m_curl)
|
||||
{
|
||||
curl_easy_cleanup(m_curl);
|
||||
}
|
||||
if (m_curl)
|
||||
{
|
||||
curl_easy_cleanup(m_curl);
|
||||
}
|
||||
}
|
||||
|
||||
void HttpAnalyticsBackend::Send(std::string report)
|
||||
{
|
||||
if (!m_curl)
|
||||
return;
|
||||
if (!m_curl)
|
||||
return;
|
||||
|
||||
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, report.c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, report.size());
|
||||
curl_easy_perform(m_curl);
|
||||
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, report.c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, report.size());
|
||||
curl_easy_perform(m_curl);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
@ -41,132 +41,123 @@ typedef void CURL;
|
|||
|
||||
namespace Common
|
||||
{
|
||||
|
||||
// Generic interface for an analytics reporting backends. The main
|
||||
// implementation used in Dolphin can be found in Core/Analytics.h.
|
||||
class AnalyticsReportingBackend
|
||||
{
|
||||
public:
|
||||
virtual ~AnalyticsReportingBackend() {}
|
||||
|
||||
// Called from the AnalyticsReporter backend thread.
|
||||
virtual void Send(std::string report) = 0;
|
||||
virtual ~AnalyticsReportingBackend() {}
|
||||
// Called from the AnalyticsReporter backend thread.
|
||||
virtual void Send(std::string report) = 0;
|
||||
};
|
||||
|
||||
// Builder object for an analytics report.
|
||||
class AnalyticsReportBuilder
|
||||
{
|
||||
public:
|
||||
AnalyticsReportBuilder();
|
||||
~AnalyticsReportBuilder() = default;
|
||||
AnalyticsReportBuilder();
|
||||
~AnalyticsReportBuilder() = default;
|
||||
|
||||
AnalyticsReportBuilder(const AnalyticsReportBuilder& other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
AnalyticsReportBuilder(const AnalyticsReportBuilder& other) { *this = other; }
|
||||
AnalyticsReportBuilder(AnalyticsReportBuilder&& other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(other.m_lock);
|
||||
m_report = std::move(other.m_report);
|
||||
}
|
||||
|
||||
AnalyticsReportBuilder(AnalyticsReportBuilder&& other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(other.m_lock);
|
||||
m_report = std::move(other.m_report);
|
||||
}
|
||||
const AnalyticsReportBuilder& operator=(const AnalyticsReportBuilder& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_lock);
|
||||
std::lock_guard<std::mutex> lk2(other.m_lock);
|
||||
m_report = other.m_report;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
// Append another builder to this one.
|
||||
AnalyticsReportBuilder& AddBuilder(const AnalyticsReportBuilder& other)
|
||||
{
|
||||
// Get before locking the object to avoid deadlocks with this += this.
|
||||
std::string other_report = other.Get();
|
||||
std::lock_guard<std::mutex> lk(m_lock);
|
||||
m_report += other_report;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Append another builder to this one.
|
||||
AnalyticsReportBuilder& AddBuilder(const AnalyticsReportBuilder& other)
|
||||
{
|
||||
// Get before locking the object to avoid deadlocks with this += this.
|
||||
std::string other_report = other.Get();
|
||||
std::lock_guard<std::mutex> lk(m_lock);
|
||||
m_report += other_report;
|
||||
return *this;
|
||||
}
|
||||
template <typename T>
|
||||
AnalyticsReportBuilder& AddData(const std::string& key, const T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_lock);
|
||||
AppendSerializedValue(&m_report, key);
|
||||
AppendSerializedValue(&m_report, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
AnalyticsReportBuilder& AddData(const std::string& key, const T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_lock);
|
||||
AppendSerializedValue(&m_report, key);
|
||||
AppendSerializedValue(&m_report, value);
|
||||
return *this;
|
||||
}
|
||||
std::string Get() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_lock);
|
||||
return m_report;
|
||||
}
|
||||
|
||||
std::string Get() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_lock);
|
||||
return m_report;
|
||||
}
|
||||
|
||||
// More efficient version of Get().
|
||||
std::string Consume()
|
||||
{
|
||||
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:
|
||||
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, bool v);
|
||||
static void AppendSerializedValue(std::string* report, u64 v);
|
||||
static void AppendSerializedValue(std::string* report, s64 v);
|
||||
static void AppendSerializedValue(std::string* report, u32 v);
|
||||
static void AppendSerializedValue(std::string* report, s32 v);
|
||||
static void AppendSerializedValue(std::string* report, float 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, bool v);
|
||||
static void AppendSerializedValue(std::string* report, u64 v);
|
||||
static void AppendSerializedValue(std::string* report, s64 v);
|
||||
static void AppendSerializedValue(std::string* report, u32 v);
|
||||
static void AppendSerializedValue(std::string* report, s32 v);
|
||||
static void AppendSerializedValue(std::string* report, float v);
|
||||
|
||||
// Should really be a std::shared_mutex, unfortunately that's C++17 only.
|
||||
mutable std::mutex m_lock;
|
||||
std::string m_report;
|
||||
// Should really be a std::shared_mutex, unfortunately that's C++17 only.
|
||||
mutable std::mutex m_lock;
|
||||
std::string m_report;
|
||||
};
|
||||
|
||||
class AnalyticsReporter
|
||||
{
|
||||
public:
|
||||
AnalyticsReporter();
|
||||
~AnalyticsReporter();
|
||||
AnalyticsReporter();
|
||||
~AnalyticsReporter();
|
||||
|
||||
// Sets a reporting backend and enables sending reports. Do not set a remote
|
||||
// backend without user consent.
|
||||
void SetBackend(std::unique_ptr<AnalyticsReportingBackend> backend)
|
||||
{
|
||||
m_backend = std::move(backend);
|
||||
m_reporter_event.Set(); // In case reports are waiting queued.
|
||||
}
|
||||
// Sets a reporting backend and enables sending reports. Do not set a remote
|
||||
// backend without user consent.
|
||||
void SetBackend(std::unique_ptr<AnalyticsReportingBackend> backend)
|
||||
{
|
||||
m_backend = std::move(backend);
|
||||
m_reporter_event.Set(); // In case reports are waiting queued.
|
||||
}
|
||||
|
||||
// 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
|
||||
// new fields that should be globally available.
|
||||
AnalyticsReportBuilder& BaseBuilder() { return m_base_builder; }
|
||||
|
||||
// Gets a cloned builder that can be used to send a report.
|
||||
AnalyticsReportBuilder Builder() const { return m_base_builder; }
|
||||
|
||||
// Enqueues a report for sending. Consumes the report builder.
|
||||
void Send(AnalyticsReportBuilder&& report);
|
||||
|
||||
// For convenience.
|
||||
void Send(AnalyticsReportBuilder& report) { Send(std::move(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
|
||||
// new fields that should be globally available.
|
||||
AnalyticsReportBuilder& BaseBuilder() { return m_base_builder; }
|
||||
// Gets a cloned builder that can be used to send a report.
|
||||
AnalyticsReportBuilder Builder() const { return m_base_builder; }
|
||||
// Enqueues a report for sending. Consumes the report builder.
|
||||
void Send(AnalyticsReportBuilder&& report);
|
||||
|
||||
// For convenience.
|
||||
void Send(AnalyticsReportBuilder& report) { Send(std::move(report)); }
|
||||
protected:
|
||||
void ThreadProc();
|
||||
void ThreadProc();
|
||||
|
||||
std::shared_ptr<AnalyticsReportingBackend> m_backend;
|
||||
AnalyticsReportBuilder m_base_builder;
|
||||
std::shared_ptr<AnalyticsReportingBackend> m_backend;
|
||||
AnalyticsReportBuilder m_base_builder;
|
||||
|
||||
std::thread m_reporter_thread;
|
||||
Common::Event m_reporter_event;
|
||||
Common::Flag m_reporter_stop_request;
|
||||
FifoQueue<std::string> m_reports_queue;
|
||||
std::thread m_reporter_thread;
|
||||
Common::Event m_reporter_event;
|
||||
Common::Flag m_reporter_stop_request;
|
||||
FifoQueue<std::string> m_reports_queue;
|
||||
};
|
||||
|
||||
// Analytics backend to be used for debugging purpose, which dumps reports to
|
||||
|
@ -174,7 +165,7 @@ protected:
|
|||
class StdoutAnalyticsBackend : public AnalyticsReportingBackend
|
||||
{
|
||||
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:
|
||||
|
@ -182,13 +173,13 @@ public:
|
|||
class HttpAnalyticsBackend : public AnalyticsReportingBackend
|
||||
{
|
||||
public:
|
||||
HttpAnalyticsBackend(const std::string& endpoint);
|
||||
~HttpAnalyticsBackend() override;
|
||||
HttpAnalyticsBackend(const std::string& endpoint);
|
||||
~HttpAnalyticsBackend() override;
|
||||
|
||||
void Send(std::string report) override;
|
||||
void Send(std::string report) override;
|
||||
|
||||
protected:
|
||||
CURL* m_curl = nullptr;
|
||||
CURL* m_curl = nullptr;
|
||||
};
|
||||
|
||||
} // 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+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <asm/hwcap.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <asm/hwcap.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
const char procfile[] = "/proc/cpuinfo";
|
||||
|
||||
static std::string GetCPUString()
|
||||
{
|
||||
const std::string marker = "Hardware\t: ";
|
||||
std::string cpu_string = "Unknown";
|
||||
const std::string marker = "Hardware\t: ";
|
||||
std::string cpu_string = "Unknown";
|
||||
|
||||
std::string line;
|
||||
std::ifstream file(procfile);
|
||||
std::string line;
|
||||
std::ifstream file(procfile);
|
||||
|
||||
if (!file)
|
||||
return cpu_string;
|
||||
if (!file)
|
||||
return cpu_string;
|
||||
|
||||
while (std::getline(file, line))
|
||||
{
|
||||
if (line.find(marker) != std::string::npos)
|
||||
{
|
||||
cpu_string = line.substr(marker.length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (std::getline(file, line))
|
||||
{
|
||||
if (line.find(marker) != std::string::npos)
|
||||
{
|
||||
cpu_string = line.substr(marker.length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return cpu_string;
|
||||
return cpu_string;
|
||||
}
|
||||
|
||||
CPUInfo cpu_info;
|
||||
|
||||
CPUInfo::CPUInfo()
|
||||
{
|
||||
Detect();
|
||||
Detect();
|
||||
}
|
||||
|
||||
// Detects the various CPU features
|
||||
void CPUInfo::Detect()
|
||||
{
|
||||
// Set some defaults here
|
||||
// When ARMv8 CPUs come out, these need to be updated.
|
||||
HTT = false;
|
||||
OS64bit = true;
|
||||
CPU64bit = true;
|
||||
Mode64bit = true;
|
||||
vendor = VENDOR_ARM;
|
||||
// Set some defaults here
|
||||
// When ARMv8 CPUs come out, these need to be updated.
|
||||
HTT = false;
|
||||
OS64bit = true;
|
||||
CPU64bit = true;
|
||||
Mode64bit = true;
|
||||
vendor = VENDOR_ARM;
|
||||
|
||||
// Get the information about the CPU
|
||||
num_cores = sysconf(_SC_NPROCESSORS_CONF);
|
||||
strncpy(cpu_string, GetCPUString().c_str(), sizeof(cpu_string));
|
||||
// Get the information about the CPU
|
||||
num_cores = sysconf(_SC_NPROCESSORS_CONF);
|
||||
strncpy(cpu_string, GetCPUString().c_str(), sizeof(cpu_string));
|
||||
|
||||
unsigned long hwcaps = getauxval(AT_HWCAP);
|
||||
bFP = hwcaps & HWCAP_FP;
|
||||
bASIMD = hwcaps & HWCAP_ASIMD;
|
||||
bAES = hwcaps & HWCAP_AES;
|
||||
bCRC32 = hwcaps & HWCAP_CRC32;
|
||||
bSHA1 = hwcaps & HWCAP_SHA1;
|
||||
bSHA2 = hwcaps & HWCAP_SHA2;
|
||||
unsigned long hwcaps = getauxval(AT_HWCAP);
|
||||
bFP = hwcaps & HWCAP_FP;
|
||||
bASIMD = hwcaps & HWCAP_ASIMD;
|
||||
bAES = hwcaps & HWCAP_AES;
|
||||
bCRC32 = hwcaps & HWCAP_CRC32;
|
||||
bSHA1 = hwcaps & HWCAP_SHA1;
|
||||
bSHA2 = hwcaps & HWCAP_SHA2;
|
||||
}
|
||||
|
||||
// Turn the CPU info into a string we can show
|
||||
std::string CPUInfo::Summarize()
|
||||
{
|
||||
std::string sum;
|
||||
if (num_cores == 1)
|
||||
sum = StringFromFormat("%s, %i core", cpu_string, num_cores);
|
||||
else
|
||||
sum = StringFromFormat("%s, %i cores", cpu_string, num_cores);
|
||||
std::string sum;
|
||||
if (num_cores == 1)
|
||||
sum = StringFromFormat("%s, %i core", cpu_string, num_cores);
|
||||
else
|
||||
sum = StringFromFormat("%s, %i cores", cpu_string, num_cores);
|
||||
|
||||
if (bAES) sum += ", AES";
|
||||
if (bCRC32) sum += ", CRC32";
|
||||
if (bSHA1) sum += ", SHA1";
|
||||
if (bSHA2) sum += ", SHA2";
|
||||
if (CPU64bit) sum += ", 64-bit";
|
||||
if (bAES)
|
||||
sum += ", AES";
|
||||
if (bCRC32)
|
||||
sum += ", CRC32";
|
||||
if (bSHA1)
|
||||
sum += ", SHA1";
|
||||
if (bSHA2)
|
||||
sum += ", SHA2";
|
||||
if (CPU64bit)
|
||||
sum += ", 64-bit";
|
||||
|
||||
return sum;
|
||||
return sum;
|
||||
}
|
||||
|
|
|
@ -6,23 +6,22 @@
|
|||
|
||||
enum CCFlags
|
||||
{
|
||||
CC_EQ = 0, // Equal
|
||||
CC_NEQ, // Not equal
|
||||
CC_CS, // Carry Set
|
||||
CC_CC, // Carry Clear
|
||||
CC_MI, // Minus (Negative)
|
||||
CC_PL, // Plus
|
||||
CC_VS, // Overflow
|
||||
CC_VC, // No Overflow
|
||||
CC_HI, // Unsigned higher
|
||||
CC_LS, // Unsigned lower or same
|
||||
CC_GE, // Signed greater than or equal
|
||||
CC_LT, // Signed less than
|
||||
CC_GT, // Signed greater than
|
||||
CC_LE, // Signed less than or equal
|
||||
CC_AL, // Always (unconditional) 14
|
||||
CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same
|
||||
CC_LO = CC_CC, // Alias of CC_CC Unsigned lower
|
||||
CC_EQ = 0, // Equal
|
||||
CC_NEQ, // Not equal
|
||||
CC_CS, // Carry Set
|
||||
CC_CC, // Carry Clear
|
||||
CC_MI, // Minus (Negative)
|
||||
CC_PL, // Plus
|
||||
CC_VS, // Overflow
|
||||
CC_VC, // No Overflow
|
||||
CC_HI, // Unsigned higher
|
||||
CC_LS, // Unsigned lower or same
|
||||
CC_GE, // Signed greater than or equal
|
||||
CC_LT, // Signed less than
|
||||
CC_GT, // Signed greater than
|
||||
CC_LE, // Signed less than or equal
|
||||
CC_AL, // Always (unconditional) 14
|
||||
CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same
|
||||
CC_LO = CC_CC, // Alias of CC_CC Unsigned lower
|
||||
};
|
||||
const u32 NO_COND = 0xE0000000;
|
||||
|
||||
|
|
|
@ -6,42 +6,46 @@
|
|||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define _assert_msg_(_t_, _a_, _fmt_, ...) \
|
||||
if (!(_a_)) {\
|
||||
if (!PanicYesNo(_fmt_, __VA_ARGS__)) \
|
||||
Crash(); \
|
||||
}
|
||||
#define _assert_msg_(_t_, _a_, _fmt_, ...) \
|
||||
if (!(_a_)) \
|
||||
{ \
|
||||
if (!PanicYesNo(_fmt_, __VA_ARGS__)) \
|
||||
Crash(); \
|
||||
}
|
||||
|
||||
#define _dbg_assert_msg_(_t_, _a_, _msg_, ...)\
|
||||
if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG && !(_a_)) {\
|
||||
ERROR_LOG(_t_, _msg_, __VA_ARGS__); \
|
||||
if (!PanicYesNo(_msg_, __VA_ARGS__)) \
|
||||
Crash(); \
|
||||
}
|
||||
#define _dbg_assert_msg_(_t_, _a_, _msg_, ...) \
|
||||
if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG && !(_a_)) \
|
||||
{ \
|
||||
ERROR_LOG(_t_, _msg_, __VA_ARGS__); \
|
||||
if (!PanicYesNo(_msg_, __VA_ARGS__)) \
|
||||
Crash(); \
|
||||
}
|
||||
#else
|
||||
#define _assert_msg_(_t_, _a_, _fmt_, ...) \
|
||||
if (!(_a_)) {\
|
||||
if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) \
|
||||
Crash(); \
|
||||
}
|
||||
#define _assert_msg_(_t_, _a_, _fmt_, ...) \
|
||||
if (!(_a_)) \
|
||||
{ \
|
||||
if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) \
|
||||
Crash(); \
|
||||
}
|
||||
|
||||
#define _dbg_assert_msg_(_t_, _a_, _msg_, ...)\
|
||||
if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG && !(_a_)) {\
|
||||
ERROR_LOG(_t_, _msg_, ##__VA_ARGS__); \
|
||||
if (!PanicYesNo(_msg_, ##__VA_ARGS__)) \
|
||||
Crash(); \
|
||||
}
|
||||
#define _dbg_assert_msg_(_t_, _a_, _msg_, ...) \
|
||||
if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG && !(_a_)) \
|
||||
{ \
|
||||
ERROR_LOG(_t_, _msg_, ##__VA_ARGS__); \
|
||||
if (!PanicYesNo(_msg_, ##__VA_ARGS__)) \
|
||||
Crash(); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define _assert_(_a_) \
|
||||
_assert_msg_(MASTER_LOG, _a_, \
|
||||
_trans("An error occurred.\n\n Line: %d\n File: %s\n\nIgnore and continue?"), \
|
||||
__LINE__, __FILE__)
|
||||
#define _assert_(_a_) \
|
||||
_assert_msg_(MASTER_LOG, _a_, \
|
||||
_trans("An error occurred.\n\n Line: %d\n File: %s\n\nIgnore and continue?"), \
|
||||
__LINE__, __FILE__)
|
||||
|
||||
#define _dbg_assert_(_t_, _a_) \
|
||||
if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG) \
|
||||
_assert_(_a_)
|
||||
#define _dbg_assert_(_t_, _a_) \
|
||||
if (MAX_LOGLEVEL >= LogTypes::LOG_LEVELS::LDEBUG) \
|
||||
_assert_(_a_)
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Common/Atomic_Win32.h" // IWYU pragma: export
|
||||
#include "Common/Atomic_Win32.h" // IWYU pragma: export
|
||||
|
||||
#else
|
||||
|
||||
// GCC-compatible compiler assumed!
|
||||
#include "Common/Atomic_GCC.h" // IWYU pragma: export
|
||||
#include "Common/Atomic_GCC.h" // IWYU pragma: export
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,30 +25,29 @@
|
|||
|
||||
namespace Common
|
||||
{
|
||||
|
||||
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)
|
||||
{
|
||||
__sync_and_and_fetch(&target, value);
|
||||
__sync_and_and_fetch(&target, value);
|
||||
}
|
||||
|
||||
inline void AtomicDecrement(volatile u32& target)
|
||||
{
|
||||
__sync_add_and_fetch(&target, -1);
|
||||
__sync_add_and_fetch(&target, -1);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
__sync_or_and_fetch(&target, value);
|
||||
__sync_or_and_fetch(&target, value);
|
||||
}
|
||||
|
||||
#ifndef __ATOMIC_RELAXED
|
||||
|
@ -58,31 +57,30 @@ inline void AtomicOr(volatile u32& target, u32 value)
|
|||
template <typename T>
|
||||
inline T AtomicLoad(volatile T& src)
|
||||
{
|
||||
return __atomic_load_n(&src, __ATOMIC_RELAXED);
|
||||
return __atomic_load_n(&src, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
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
|
||||
{
|
||||
|
||||
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)
|
||||
{
|
||||
_InterlockedAnd((volatile LONG*)&target, (LONG)value);
|
||||
_InterlockedAnd((volatile LONG*)&target, (LONG)value);
|
||||
}
|
||||
|
||||
inline void AtomicIncrement(volatile u32& target)
|
||||
{
|
||||
_InterlockedIncrement((volatile LONG*)&target);
|
||||
_InterlockedIncrement((volatile LONG*)&target);
|
||||
}
|
||||
|
||||
inline void AtomicDecrement(volatile u32& target)
|
||||
{
|
||||
_InterlockedDecrement((volatile LONG*)&target);
|
||||
_InterlockedDecrement((volatile LONG*)&target);
|
||||
}
|
||||
|
||||
inline void AtomicOr(volatile u32& target, u32 value)
|
||||
{
|
||||
_InterlockedOr((volatile LONG*)&target, (LONG)value);
|
||||
_InterlockedOr((volatile LONG*)&target, (LONG)value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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>
|
||||
inline T AtomicLoadAcquire(volatile T& src)
|
||||
{
|
||||
T result = src; // 32-bit reads are always atomic.
|
||||
_ReadBarrier(); // Compiler instruction only. x86 loads always have acquire semantics.
|
||||
return result;
|
||||
T result = src; // 32-bit reads are always atomic.
|
||||
_ReadBarrier(); // Compiler instruction only. x86 loads always have acquire semantics.
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
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>
|
||||
inline void AtomicStoreRelease(volatile T& dest, U value)
|
||||
{
|
||||
_WriteBarrier(); // Compiler instruction only. x86 stores always have release semantics.
|
||||
dest = (T) value; // 32-bit writes are always atomic.
|
||||
_WriteBarrier(); // Compiler instruction only. x86 stores always have release semantics.
|
||||
dest = (T)value; // 32-bit writes are always atomic.
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
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+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
|
@ -30,7 +29,6 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
|
@ -111,78 +109,73 @@
|
|||
* symptoms.
|
||||
*/
|
||||
#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
|
||||
{
|
||||
private:
|
||||
// This constructor might be considered ambiguous:
|
||||
// Would it initialize the storage or just the bitfield?
|
||||
// Hence, delete it. Use the assignment operator to set bitfield values!
|
||||
BitField(T val) = delete;
|
||||
// This constructor might be considered ambiguous:
|
||||
// Would it initialize the storage or just the bitfield?
|
||||
// Hence, delete it. Use the assignment operator to set bitfield values!
|
||||
BitField(T val) = delete;
|
||||
|
||||
public:
|
||||
// Force default constructor to be created
|
||||
// so that we can use this within unions
|
||||
BitField() = default;
|
||||
// Force default constructor to be created
|
||||
// so that we can use this within unions
|
||||
BitField() = default;
|
||||
|
||||
// We explicitly delete the copy assignment operator here, because the
|
||||
// default copy assignment would copy the full storage value, rather than
|
||||
// just the bits relevant to this particular bit field.
|
||||
// Ideally, we would just implement the copy assignment to copy only the
|
||||
// relevant bits, but this requires compiler support for unrestricted
|
||||
// unions.
|
||||
// TODO: Implement this operator properly once all target compilers
|
||||
// support unrestricted unions.
|
||||
BitField& operator=(const BitField&) = delete;
|
||||
// We explicitly delete the copy assignment operator here, because the
|
||||
// default copy assignment would copy the full storage value, rather than
|
||||
// just the bits relevant to this particular bit field.
|
||||
// Ideally, we would just implement the copy assignment to copy only the
|
||||
// relevant bits, but this requires compiler support for unrestricted
|
||||
// unions.
|
||||
// TODO: Implement this operator properly once all target compilers
|
||||
// support unrestricted unions.
|
||||
BitField& operator=(const BitField&) = delete;
|
||||
|
||||
__forceinline BitField& operator=(T val)
|
||||
{
|
||||
storage = (storage & ~GetMask()) | ((val << position) & GetMask());
|
||||
return *this;
|
||||
}
|
||||
__forceinline BitField& operator=(T val)
|
||||
{
|
||||
storage = (storage & ~GetMask()) | ((val << position) & GetMask());
|
||||
return *this;
|
||||
}
|
||||
|
||||
__forceinline T Value() const
|
||||
{
|
||||
if (std::numeric_limits<T>::is_signed)
|
||||
{
|
||||
std::size_t shift = 8 * sizeof(T) - bits;
|
||||
return (T)((storage << (shift - position)) >> shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (T)((storage & GetMask()) >> position);
|
||||
}
|
||||
}
|
||||
|
||||
__forceinline operator T() const
|
||||
{
|
||||
return Value();
|
||||
}
|
||||
__forceinline T Value() const
|
||||
{
|
||||
if (std::numeric_limits<T>::is_signed)
|
||||
{
|
||||
std::size_t shift = 8 * sizeof(T) - bits;
|
||||
return (T)((storage << (shift - position)) >> shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (T)((storage & GetMask()) >> position);
|
||||
}
|
||||
}
|
||||
|
||||
__forceinline operator T() const { return Value(); }
|
||||
private:
|
||||
// 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
|
||||
// former case to workaround compile errors which arise when using
|
||||
// std::underlying_type<T>::type directly.
|
||||
typedef typename std::conditional<std::is_enum<T>::value,
|
||||
std::underlying_type<T>,
|
||||
std::enable_if<true,T>>::type::type StorageType;
|
||||
// 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
|
||||
// former case to workaround compile errors which arise when using
|
||||
// std::underlying_type<T>::type directly.
|
||||
typedef typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
|
||||
std::enable_if<true, T>>::type::type StorageType;
|
||||
|
||||
// Unsigned version of StorageType
|
||||
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
|
||||
// Unsigned version of StorageType
|
||||
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
|
||||
|
||||
__forceinline StorageType GetMask() const
|
||||
{
|
||||
return (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
|
||||
}
|
||||
__forceinline StorageType GetMask() const
|
||||
{
|
||||
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
|
||||
static_assert(position < 8 * sizeof(T), "Invalid position");
|
||||
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
|
||||
static_assert(bits > 0, "Invalid number of bits");
|
||||
// And, you know, just in case people specify something stupid like bits=position=0x80000000
|
||||
static_assert(position < 8 * sizeof(T), "Invalid position");
|
||||
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
|
||||
static_assert(bits > 0, "Invalid number of bits");
|
||||
};
|
||||
#pragma pack()
|
||||
|
|
|
@ -16,53 +16,76 @@
|
|||
template <typename T>
|
||||
static inline int CountSetBits(T v)
|
||||
{
|
||||
// from https://graphics.stanford.edu/~seander/bithacks.html
|
||||
// GCC has this built in, but MSVC's intrinsic will only emit the actual
|
||||
// POPCNT instruction, which we're not depending on
|
||||
v = v - ((v >> 1) & (T)~(T)0/3);
|
||||
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3);
|
||||
v = (v + (v >> 4)) & (T)~(T)0/255*15;
|
||||
return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8;
|
||||
// from https://graphics.stanford.edu/~seander/bithacks.html
|
||||
// GCC has this built in, but MSVC's intrinsic will only emit the actual
|
||||
// POPCNT instruction, which we're not depending on
|
||||
v = v - ((v >> 1) & (T) ~(T)0 / 3);
|
||||
v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3);
|
||||
v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15;
|
||||
return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u8 val)
|
||||
{
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u16 val)
|
||||
{
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u32 val)
|
||||
{
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u64 val)
|
||||
{
|
||||
unsigned long index;
|
||||
_BitScanForward64(&index, val);
|
||||
return (int)index;
|
||||
unsigned long index;
|
||||
_BitScanForward64(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
#else
|
||||
static inline int CountSetBits(u8 val) { return __builtin_popcount(val); }
|
||||
static inline int CountSetBits(u16 val) { return __builtin_popcount(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); }
|
||||
static inline int CountSetBits(u8 val)
|
||||
{
|
||||
return __builtin_popcount(val);
|
||||
}
|
||||
static inline int CountSetBits(u16 val)
|
||||
{
|
||||
return __builtin_popcount(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
|
||||
|
||||
// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly
|
||||
namespace BS
|
||||
{
|
||||
|
||||
// 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
|
||||
// class, it acts like an array of bools:
|
||||
|
@ -86,101 +109,107 @@ namespace BS
|
|||
template <typename IntTy>
|
||||
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:
|
||||
// A reference to a particular bit, returned from operator[].
|
||||
class Ref
|
||||
{
|
||||
public:
|
||||
Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
|
||||
Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
|
||||
operator bool() const { return (m_bs->m_val & m_mask) != 0; }
|
||||
bool operator=(bool set)
|
||||
{
|
||||
m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
|
||||
return set;
|
||||
}
|
||||
private:
|
||||
BitSet* m_bs;
|
||||
IntTy m_mask;
|
||||
};
|
||||
// A reference to a particular bit, returned from operator[].
|
||||
class Ref
|
||||
{
|
||||
public:
|
||||
Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
|
||||
Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
|
||||
operator bool() const { return (m_bs->m_val & m_mask) != 0; }
|
||||
bool operator=(bool set)
|
||||
{
|
||||
m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
|
||||
return set;
|
||||
}
|
||||
|
||||
// A STL-like iterator is required to be able to use range-based for loops.
|
||||
class Iterator
|
||||
{
|
||||
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;
|
||||
};
|
||||
private:
|
||||
BitSet* m_bs;
|
||||
IntTy m_mask;
|
||||
};
|
||||
|
||||
BitSet() : m_val(0) {}
|
||||
explicit BitSet(IntTy val) : m_val(val) {}
|
||||
BitSet(std::initializer_list<int> init)
|
||||
{
|
||||
m_val = 0;
|
||||
for (int bit : init)
|
||||
m_val |= (IntTy)1 << bit;
|
||||
}
|
||||
// A STL-like iterator is required to be able to use range-based for loops.
|
||||
class Iterator
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
static BitSet AllTrue(size_t count)
|
||||
{
|
||||
return BitSet(count == sizeof(IntTy)*8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
|
||||
}
|
||||
BitSet() : m_val(0) {}
|
||||
explicit BitSet(IntTy val) : m_val(val) {}
|
||||
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); }
|
||||
const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; }
|
||||
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; }
|
||||
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; }
|
||||
static BitSet AllTrue(size_t count)
|
||||
{
|
||||
return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
|
||||
}
|
||||
|
||||
// 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;
|
||||
Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); }
|
||||
const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; }
|
||||
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; }
|
||||
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,
|
||||
// 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;
|
||||
|
|
|
@ -13,207 +13,197 @@
|
|||
|
||||
namespace Common
|
||||
{
|
||||
|
||||
// This class provides a synchronized loop.
|
||||
// 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.
|
||||
// Be careful when using Wait() and Wakeup() at the same time. Wait() may block forever while Wakeup() is called regularly.
|
||||
// It's optimized for high-usage iterations which usually are already running while it's triggered
|
||||
// often.
|
||||
// Be careful when using Wait() and Wakeup() at the same time. Wait() may block forever while
|
||||
// Wakeup() is called regularly.
|
||||
class BlockingLoop
|
||||
{
|
||||
public:
|
||||
BlockingLoop()
|
||||
{
|
||||
m_stopped.Set();
|
||||
}
|
||||
BlockingLoop() { m_stopped.Set(); }
|
||||
~BlockingLoop() { Stop(); }
|
||||
// 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()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
// 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.
|
||||
if (m_running_state.exchange(STATE_NEED_EXECUTION) != STATE_SLEEPING)
|
||||
return;
|
||||
|
||||
// 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;
|
||||
// Else as the worker thread may sleep now, we have to set the event.
|
||||
m_new_work_event.Set();
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (m_running_state.exchange(STATE_NEED_EXECUTION) != STATE_SLEEPING)
|
||||
return;
|
||||
// Wait for a complete payload run after the last Wakeup() call.
|
||||
// If stopped, this returns immediately.
|
||||
void Wait()
|
||||
{
|
||||
// already done
|
||||
if (IsDone())
|
||||
return;
|
||||
|
||||
// Else as the worker thread may sleep now, we have to set the event.
|
||||
m_new_work_event.Set();
|
||||
}
|
||||
// notifying this event will only wake up one thread, so use a mutex here to
|
||||
// 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.
|
||||
// If stopped, this returns immediately.
|
||||
void Wait()
|
||||
{
|
||||
// already done
|
||||
if (IsDone())
|
||||
return;
|
||||
// Wait for the worker thread to finish.
|
||||
while (!IsDone())
|
||||
{
|
||||
m_done_event.Wait();
|
||||
}
|
||||
|
||||
// notifying this event will only wake up one thread, so use a mutex here to
|
||||
// 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);
|
||||
// As we wanted to wait for the other thread, there is likely no work remaining.
|
||||
// So there is no need for a busy loop any more.
|
||||
m_may_sleep.Set();
|
||||
}
|
||||
|
||||
// Wait for the worker thread to finish.
|
||||
while (!IsDone())
|
||||
{
|
||||
m_done_event.Wait();
|
||||
}
|
||||
// Half start the worker.
|
||||
// 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
|
||||
// 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.
|
||||
// So there is no need for a busy loop any more.
|
||||
m_may_sleep.Set();
|
||||
}
|
||||
if (!m_stopped.TestAndClear())
|
||||
return;
|
||||
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.
|
||||
// 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 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);
|
||||
// Main loop of this object.
|
||||
// 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.
|
||||
// Use timeout = 0 to run without a timeout at all.
|
||||
template <class F>
|
||||
void Run(F payload, int64_t timeout = 0)
|
||||
{
|
||||
// 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())
|
||||
return;
|
||||
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();
|
||||
}
|
||||
while (!m_shutdown.IsSet())
|
||||
{
|
||||
payload();
|
||||
|
||||
// Main loop of this object.
|
||||
// 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.
|
||||
// Use timeout = 0 to run without a timeout at all.
|
||||
template<class F> void Run(F payload, int64_t timeout = 0)
|
||||
{
|
||||
// Asserts that Prepare is called at least once before we enter the loop.
|
||||
// But a good implementation should call this before already.
|
||||
Prepare();
|
||||
switch (m_running_state.load())
|
||||
{
|
||||
case STATE_NEED_EXECUTION:
|
||||
// We won't get notified while we are in the STATE_NEED_EXECUTION state, so maybe Wakeup was
|
||||
// called.
|
||||
// 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;
|
||||
|
||||
while (!m_shutdown.IsSet())
|
||||
{
|
||||
payload();
|
||||
case STATE_LAST_EXECUTION:
|
||||
// If we're still in the STATE_LAST_EXECUTION state, then Wakeup wasn't called within the
|
||||
// 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())
|
||||
{
|
||||
case STATE_NEED_EXECUTION:
|
||||
// We won't get notified while we are in the STATE_NEED_EXECUTION state, so maybe Wakeup was called.
|
||||
// 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;
|
||||
// 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
|
||||
// triggered so that we'll skip the next waiting call quite fast.
|
||||
m_done_event.Set();
|
||||
|
||||
case STATE_LAST_EXECUTION:
|
||||
// If we're still in the STATE_LAST_EXECUTION state, then Wakeup wasn't called within the 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;
|
||||
case STATE_DONE:
|
||||
// We're done now. So time to check if we want to sleep or if we want to stay in a busy
|
||||
// loop.
|
||||
if (m_may_sleep.TestAndClear())
|
||||
{
|
||||
// Try to set the sleeping state.
|
||||
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.
|
||||
// However, if we're not in the STATE_DONE state any more, the event should also be
|
||||
// triggered so that we'll skip the next waiting call quite fast.
|
||||
m_done_event.Set();
|
||||
case STATE_SLEEPING:
|
||||
// Just relax
|
||||
if (timeout > 0)
|
||||
{
|
||||
m_new_work_event.WaitFor(std::chrono::milliseconds(timeout));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_new_work_event.Wait();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case STATE_DONE:
|
||||
// We're done now. So time to check if we want to sleep or if we want to stay in a busy loop.
|
||||
if (m_may_sleep.TestAndClear())
|
||||
{
|
||||
// Try to set the sleeping state.
|
||||
if (m_running_state-- != STATE_DONE)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Busy loop.
|
||||
break;
|
||||
}
|
||||
// Shutdown down, so get a safe state
|
||||
m_running_state.store(STATE_DONE);
|
||||
m_stopped.Set();
|
||||
|
||||
case STATE_SLEEPING:
|
||||
// Just relax
|
||||
if (timeout > 0)
|
||||
{
|
||||
m_new_work_event.WaitFor(std::chrono::milliseconds(timeout));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_new_work_event.Wait();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Wake up the last Wait calls.
|
||||
m_done_event.Set();
|
||||
}
|
||||
|
||||
// Shutdown down, so get a safe state
|
||||
m_running_state.store(STATE_DONE);
|
||||
m_stopped.Set();
|
||||
// Quits the main loop.
|
||||
// By default, it will wait until the main loop quits.
|
||||
// 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_done_event.Set();
|
||||
}
|
||||
m_shutdown.Set();
|
||||
|
||||
// Quits the main loop.
|
||||
// By default, it will wait until the main loop quits.
|
||||
// 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;
|
||||
// We have to interrupt the sleeping call to let the worker shut down soon.
|
||||
Wakeup();
|
||||
|
||||
m_shutdown.Set();
|
||||
|
||||
// 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();
|
||||
}
|
||||
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(); }
|
||||
private:
|
||||
std::mutex m_wait_lock;
|
||||
std::mutex m_prepare_lock;
|
||||
std::mutex m_wait_lock;
|
||||
std::mutex m_prepare_lock;
|
||||
|
||||
Flag m_stopped; // If this is set, Wait() shall not block.
|
||||
Flag m_shutdown; // If this is set, the loop shall end.
|
||||
Flag m_stopped; // If this is set, Wait() shall not block.
|
||||
Flag m_shutdown; // If this is set, the loop shall end.
|
||||
|
||||
Event m_new_work_event;
|
||||
Event m_done_event;
|
||||
Event m_new_work_event;
|
||||
Event m_done_event;
|
||||
|
||||
enum RUNNING_TYPE {
|
||||
STATE_SLEEPING = 0,
|
||||
STATE_DONE = 1,
|
||||
STATE_LAST_EXECUTION = 2,
|
||||
STATE_NEED_EXECUTION = 3
|
||||
};
|
||||
std::atomic<int> m_running_state; // must be of type RUNNING_TYPE
|
||||
enum RUNNING_TYPE
|
||||
{
|
||||
STATE_SLEEPING = 0,
|
||||
STATE_DONE = 1,
|
||||
STATE_LAST_EXECUTION = 2,
|
||||
STATE_NEED_EXECUTION = 3
|
||||
};
|
||||
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
|
||||
{
|
||||
for (const TBreakPoint& bp : m_BreakPoints)
|
||||
if (bp.iAddress == address)
|
||||
return true;
|
||||
for (const TBreakPoint& bp : m_BreakPoints)
|
||||
if (bp.iAddress == address)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BreakPoints::IsTempBreakPoint(u32 address) const
|
||||
{
|
||||
for (const TBreakPoint& bp : m_BreakPoints)
|
||||
if (bp.iAddress == address && bp.bTemporary)
|
||||
return true;
|
||||
for (const TBreakPoint& bp : m_BreakPoints)
|
||||
if (bp.iAddress == address && bp.bTemporary)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const
|
||||
{
|
||||
TBreakPointsStr bps;
|
||||
for (const TBreakPoint& bp : m_BreakPoints)
|
||||
{
|
||||
if (!bp.bTemporary)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::hex << bp.iAddress << " " << (bp.bOn ? "n" : "");
|
||||
bps.push_back(ss.str());
|
||||
}
|
||||
}
|
||||
TBreakPointsStr bps;
|
||||
for (const TBreakPoint& bp : m_BreakPoints)
|
||||
{
|
||||
if (!bp.bTemporary)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::hex << bp.iAddress << " " << (bp.bOn ? "n" : "");
|
||||
bps.push_back(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
return bps;
|
||||
return bps;
|
||||
}
|
||||
|
||||
void BreakPoints::AddFromStrings(const TBreakPointsStr& bpstrs)
|
||||
{
|
||||
for (const std::string& bpstr : bpstrs)
|
||||
{
|
||||
TBreakPoint bp;
|
||||
std::stringstream ss;
|
||||
ss << std::hex << bpstr;
|
||||
ss >> bp.iAddress;
|
||||
bp.bOn = bpstr.find("n") != bpstr.npos;
|
||||
bp.bTemporary = false;
|
||||
Add(bp);
|
||||
}
|
||||
for (const std::string& bpstr : bpstrs)
|
||||
{
|
||||
TBreakPoint bp;
|
||||
std::stringstream ss;
|
||||
ss << std::hex << bpstr;
|
||||
ss >> bp.iAddress;
|
||||
bp.bOn = bpstr.find("n") != bpstr.npos;
|
||||
bp.bTemporary = false;
|
||||
Add(bp);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Add(const TBreakPoint& bp)
|
||||
{
|
||||
if (!IsAddressBreakPoint(bp.iAddress))
|
||||
{
|
||||
m_BreakPoints.push_back(bp);
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true);
|
||||
}
|
||||
if (!IsAddressBreakPoint(bp.iAddress))
|
||||
{
|
||||
m_BreakPoints.push_back(bp);
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Add(u32 em_address, bool temp)
|
||||
{
|
||||
if (!IsAddressBreakPoint(em_address)) // only add new addresses
|
||||
{
|
||||
TBreakPoint pt; // breakpoint settings
|
||||
pt.bOn = true;
|
||||
pt.bTemporary = temp;
|
||||
pt.iAddress = em_address;
|
||||
if (!IsAddressBreakPoint(em_address)) // only add new addresses
|
||||
{
|
||||
TBreakPoint pt; // breakpoint settings
|
||||
pt.bOn = true;
|
||||
pt.bTemporary = temp;
|
||||
pt.iAddress = em_address;
|
||||
|
||||
m_BreakPoints.push_back(pt);
|
||||
m_BreakPoints.push_back(pt);
|
||||
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(em_address, 4, true);
|
||||
}
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(em_address, 4, true);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Remove(u32 em_address)
|
||||
{
|
||||
for (auto i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i)
|
||||
{
|
||||
if (i->iAddress == em_address)
|
||||
{
|
||||
m_BreakPoints.erase(i);
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(em_address, 4, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i)
|
||||
{
|
||||
if (i->iAddress == em_address)
|
||||
{
|
||||
m_BreakPoints.erase(i);
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(em_address, 4, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Clear()
|
||||
{
|
||||
if (jit)
|
||||
{
|
||||
for (const TBreakPoint& bp : m_BreakPoints)
|
||||
{
|
||||
jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true);
|
||||
}
|
||||
}
|
||||
if (jit)
|
||||
{
|
||||
for (const TBreakPoint& bp : m_BreakPoints)
|
||||
{
|
||||
jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true);
|
||||
}
|
||||
}
|
||||
|
||||
m_BreakPoints.clear();
|
||||
m_BreakPoints.clear();
|
||||
}
|
||||
|
||||
void BreakPoints::ClearAllTemporary()
|
||||
{
|
||||
for (const TBreakPoint& bp : m_BreakPoints)
|
||||
{
|
||||
if (bp.bTemporary)
|
||||
{
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true);
|
||||
Remove(bp.iAddress);
|
||||
}
|
||||
}
|
||||
for (const TBreakPoint& bp : m_BreakPoints)
|
||||
{
|
||||
if (bp.bTemporary)
|
||||
{
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4, true);
|
||||
Remove(bp.iAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MemChecks::TMemChecksStr MemChecks::GetStrings() const
|
||||
{
|
||||
TMemChecksStr mcs;
|
||||
for (const TMemCheck& bp : m_MemChecks)
|
||||
{
|
||||
std::stringstream mc;
|
||||
mc << std::hex << bp.StartAddress;
|
||||
mc << " " << (bp.bRange ? bp.EndAddress : bp.StartAddress) << " " <<
|
||||
(bp.bRange ? "n" : "") << (bp.OnRead ? "r" : "") <<
|
||||
(bp.OnWrite ? "w" : "") << (bp.Log ? "l" : "") << (bp.Break ? "p" : "");
|
||||
mcs.push_back(mc.str());
|
||||
}
|
||||
TMemChecksStr mcs;
|
||||
for (const TMemCheck& bp : m_MemChecks)
|
||||
{
|
||||
std::stringstream mc;
|
||||
mc << std::hex << bp.StartAddress;
|
||||
mc << " " << (bp.bRange ? bp.EndAddress : bp.StartAddress) << " " << (bp.bRange ? "n" : "")
|
||||
<< (bp.OnRead ? "r" : "") << (bp.OnWrite ? "w" : "") << (bp.Log ? "l" : "")
|
||||
<< (bp.Break ? "p" : "");
|
||||
mcs.push_back(mc.str());
|
||||
}
|
||||
|
||||
return mcs;
|
||||
return mcs;
|
||||
}
|
||||
|
||||
void MemChecks::AddFromStrings(const TMemChecksStr& mcstrs)
|
||||
{
|
||||
for (const std::string& mcstr : mcstrs)
|
||||
{
|
||||
TMemCheck mc;
|
||||
std::stringstream ss;
|
||||
ss << std::hex << mcstr;
|
||||
ss >> mc.StartAddress;
|
||||
mc.bRange = mcstr.find("n") != mcstr.npos;
|
||||
mc.OnRead = mcstr.find("r") != mcstr.npos;
|
||||
mc.OnWrite = mcstr.find("w") != mcstr.npos;
|
||||
mc.Log = mcstr.find("l") != mcstr.npos;
|
||||
mc.Break = mcstr.find("p") != mcstr.npos;
|
||||
if (mc.bRange)
|
||||
ss >> mc.EndAddress;
|
||||
else
|
||||
mc.EndAddress = mc.StartAddress;
|
||||
Add(mc);
|
||||
}
|
||||
for (const std::string& mcstr : mcstrs)
|
||||
{
|
||||
TMemCheck mc;
|
||||
std::stringstream ss;
|
||||
ss << std::hex << mcstr;
|
||||
ss >> mc.StartAddress;
|
||||
mc.bRange = mcstr.find("n") != mcstr.npos;
|
||||
mc.OnRead = mcstr.find("r") != mcstr.npos;
|
||||
mc.OnWrite = mcstr.find("w") != mcstr.npos;
|
||||
mc.Log = mcstr.find("l") != mcstr.npos;
|
||||
mc.Break = mcstr.find("p") != mcstr.npos;
|
||||
if (mc.bRange)
|
||||
ss >> mc.EndAddress;
|
||||
else
|
||||
mc.EndAddress = mc.StartAddress;
|
||||
Add(mc);
|
||||
}
|
||||
}
|
||||
|
||||
void MemChecks::Add(const TMemCheck& _rMemoryCheck)
|
||||
{
|
||||
bool had_any = HasAny();
|
||||
if (GetMemCheck(_rMemoryCheck.StartAddress) == nullptr)
|
||||
m_MemChecks.push_back(_rMemoryCheck);
|
||||
// If this is the first one, clear the JIT cache so it can switch to
|
||||
// watchpoint-compatible code.
|
||||
if (!had_any && jit)
|
||||
jit->ClearCache();
|
||||
bool had_any = HasAny();
|
||||
if (GetMemCheck(_rMemoryCheck.StartAddress) == nullptr)
|
||||
m_MemChecks.push_back(_rMemoryCheck);
|
||||
// If this is the first one, clear the JIT cache so it can switch to
|
||||
// watchpoint-compatible code.
|
||||
if (!had_any && jit)
|
||||
jit->ClearCache();
|
||||
}
|
||||
|
||||
void MemChecks::Remove(u32 _Address)
|
||||
{
|
||||
for (auto i = m_MemChecks.begin(); i != m_MemChecks.end(); ++i)
|
||||
{
|
||||
if (i->StartAddress == _Address)
|
||||
{
|
||||
m_MemChecks.erase(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!HasAny() && jit)
|
||||
jit->ClearCache();
|
||||
for (auto i = m_MemChecks.begin(); i != m_MemChecks.end(); ++i)
|
||||
{
|
||||
if (i->StartAddress == _Address)
|
||||
{
|
||||
m_MemChecks.erase(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!HasAny() && jit)
|
||||
jit->ClearCache();
|
||||
}
|
||||
|
||||
TMemCheck* MemChecks::GetMemCheck(u32 address)
|
||||
{
|
||||
for (TMemCheck& bp : m_MemChecks)
|
||||
{
|
||||
if (bp.bRange)
|
||||
{
|
||||
if (address >= bp.StartAddress && address <= bp.EndAddress)
|
||||
return &(bp);
|
||||
}
|
||||
else if (bp.StartAddress == address)
|
||||
{
|
||||
return &(bp);
|
||||
}
|
||||
}
|
||||
for (TMemCheck& bp : m_MemChecks)
|
||||
{
|
||||
if (bp.bRange)
|
||||
{
|
||||
if (address >= bp.StartAddress && address <= bp.EndAddress)
|
||||
return &(bp);
|
||||
}
|
||||
else if (bp.StartAddress == address)
|
||||
{
|
||||
return &(bp);
|
||||
}
|
||||
}
|
||||
|
||||
// none found
|
||||
return nullptr;
|
||||
// none found
|
||||
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 (Log)
|
||||
{
|
||||
INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)",
|
||||
pc, debug_interface->GetDescription(pc).c_str(),
|
||||
write ? "Write" : "Read", size*8, size*2, iValue, addr,
|
||||
debug_interface->GetDescription(addr).c_str()
|
||||
);
|
||||
}
|
||||
if ((write && OnWrite) || (!write && OnRead))
|
||||
{
|
||||
if (Log)
|
||||
{
|
||||
INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)", pc,
|
||||
debug_interface->GetDescription(pc).c_str(), write ? "Write" : "Read", size * 8,
|
||||
size * 2, iValue, addr, debug_interface->GetDescription(addr).c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Watches::IsAddressWatch(u32 _iAddress) const
|
||||
{
|
||||
for (const TWatch& bp : m_Watches)
|
||||
if (bp.iAddress == _iAddress)
|
||||
return true;
|
||||
for (const TWatch& bp : m_Watches)
|
||||
if (bp.iAddress == _iAddress)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Watches::TWatchesStr Watches::GetStrings() const
|
||||
{
|
||||
TWatchesStr bps;
|
||||
for (const TWatch& bp : m_Watches)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::hex << bp.iAddress << " " << bp.name;
|
||||
bps.push_back(ss.str());
|
||||
}
|
||||
TWatchesStr bps;
|
||||
for (const TWatch& bp : m_Watches)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::hex << bp.iAddress << " " << bp.name;
|
||||
bps.push_back(ss.str());
|
||||
}
|
||||
|
||||
return bps;
|
||||
return bps;
|
||||
}
|
||||
|
||||
void Watches::AddFromStrings(const TWatchesStr& bpstrs)
|
||||
{
|
||||
for (const std::string& bpstr : bpstrs)
|
||||
{
|
||||
TWatch bp;
|
||||
std::stringstream ss;
|
||||
ss << std::hex << bpstr;
|
||||
ss >> bp.iAddress;
|
||||
ss >> std::ws;
|
||||
getline(ss, bp.name);
|
||||
Add(bp);
|
||||
}
|
||||
for (const std::string& bpstr : bpstrs)
|
||||
{
|
||||
TWatch bp;
|
||||
std::stringstream ss;
|
||||
ss << std::hex << bpstr;
|
||||
ss >> bp.iAddress;
|
||||
ss >> std::ws;
|
||||
getline(ss, bp.name);
|
||||
Add(bp);
|
||||
}
|
||||
}
|
||||
|
||||
void Watches::Add(const TWatch& bp)
|
||||
{
|
||||
if (!IsAddressWatch(bp.iAddress))
|
||||
{
|
||||
m_Watches.push_back(bp);
|
||||
}
|
||||
if (!IsAddressWatch(bp.iAddress))
|
||||
{
|
||||
m_Watches.push_back(bp);
|
||||
}
|
||||
}
|
||||
|
||||
void Watches::Add(u32 em_address)
|
||||
{
|
||||
if (!IsAddressWatch(em_address)) // only add new addresses
|
||||
{
|
||||
TWatch pt; // breakpoint settings
|
||||
pt.bOn = true;
|
||||
pt.iAddress = em_address;
|
||||
if (!IsAddressWatch(em_address)) // only add new addresses
|
||||
{
|
||||
TWatch pt; // breakpoint settings
|
||||
pt.bOn = true;
|
||||
pt.iAddress = em_address;
|
||||
|
||||
m_Watches.push_back(pt);
|
||||
}
|
||||
m_Watches.push_back(pt);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
m_Watches.at(count).name = name;
|
||||
m_Watches.at(count).name = name;
|
||||
}
|
||||
|
||||
void Watches::Remove(u32 em_address)
|
||||
{
|
||||
for (auto i = m_Watches.begin(); i != m_Watches.end(); ++i)
|
||||
{
|
||||
if (i->iAddress == em_address)
|
||||
{
|
||||
m_Watches.erase(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto i = m_Watches.begin(); i != m_Watches.end(); ++i)
|
||||
{
|
||||
if (i->iAddress == em_address)
|
||||
{
|
||||
m_Watches.erase(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Watches::Clear()
|
||||
{
|
||||
m_Watches.clear();
|
||||
m_Watches.clear();
|
||||
}
|
||||
|
|
|
@ -13,124 +13,118 @@ class DebugInterface;
|
|||
|
||||
struct TBreakPoint
|
||||
{
|
||||
u32 iAddress;
|
||||
bool bOn;
|
||||
bool bTemporary;
|
||||
u32 iAddress;
|
||||
bool bOn;
|
||||
bool bTemporary;
|
||||
};
|
||||
|
||||
struct TMemCheck
|
||||
{
|
||||
TMemCheck()
|
||||
{
|
||||
numHits = 0;
|
||||
StartAddress = EndAddress = 0;
|
||||
bRange = OnRead = OnWrite = Log = Break = false;
|
||||
}
|
||||
TMemCheck()
|
||||
{
|
||||
numHits = 0;
|
||||
StartAddress = EndAddress = 0;
|
||||
bRange = OnRead = OnWrite = Log = Break = false;
|
||||
}
|
||||
|
||||
u32 StartAddress;
|
||||
u32 EndAddress;
|
||||
u32 StartAddress;
|
||||
u32 EndAddress;
|
||||
|
||||
bool bRange;
|
||||
bool bRange;
|
||||
|
||||
bool OnRead;
|
||||
bool OnWrite;
|
||||
bool OnRead;
|
||||
bool OnWrite;
|
||||
|
||||
bool Log;
|
||||
bool Break;
|
||||
bool Log;
|
||||
bool Break;
|
||||
|
||||
u32 numHits;
|
||||
u32 numHits;
|
||||
|
||||
// returns whether to break
|
||||
bool Action(DebugInterface* dbg_interface, u32 _iValue, u32 addr,
|
||||
bool write, int size, u32 pc);
|
||||
// returns whether to break
|
||||
bool Action(DebugInterface* dbg_interface, u32 _iValue, u32 addr, bool write, int size, u32 pc);
|
||||
};
|
||||
|
||||
struct TWatch
|
||||
{
|
||||
std::string name = "";
|
||||
u32 iAddress;
|
||||
bool bOn;
|
||||
std::string name = "";
|
||||
u32 iAddress;
|
||||
bool bOn;
|
||||
};
|
||||
|
||||
// Code breakpoints.
|
||||
class BreakPoints
|
||||
{
|
||||
public:
|
||||
typedef std::vector<TBreakPoint> TBreakPoints;
|
||||
typedef std::vector<std::string> TBreakPointsStr;
|
||||
typedef std::vector<TBreakPoint> TBreakPoints;
|
||||
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;
|
||||
void AddFromStrings(const TBreakPointsStr& bps);
|
||||
// is address breakpoint
|
||||
bool IsAddressBreakPoint(u32 address) const;
|
||||
bool IsTempBreakPoint(u32 address) const;
|
||||
|
||||
// is address breakpoint
|
||||
bool IsAddressBreakPoint(u32 address) const;
|
||||
bool IsTempBreakPoint(u32 address) const;
|
||||
// Add BreakPoint
|
||||
void Add(u32 em_address, bool temp = false);
|
||||
void Add(const TBreakPoint& bp);
|
||||
|
||||
// Add BreakPoint
|
||||
void Add(u32 em_address, bool temp = false);
|
||||
void Add(const TBreakPoint& bp);
|
||||
|
||||
// Remove Breakpoint
|
||||
void Remove(u32 _iAddress);
|
||||
void Clear();
|
||||
void ClearAllTemporary();
|
||||
// Remove Breakpoint
|
||||
void Remove(u32 _iAddress);
|
||||
void Clear();
|
||||
void ClearAllTemporary();
|
||||
|
||||
private:
|
||||
TBreakPoints m_BreakPoints;
|
||||
TBreakPoints m_BreakPoints;
|
||||
};
|
||||
|
||||
|
||||
// Memory breakpoints
|
||||
class MemChecks
|
||||
{
|
||||
public:
|
||||
typedef std::vector<TMemCheck> TMemChecks;
|
||||
typedef std::vector<std::string> TMemChecksStr;
|
||||
typedef std::vector<TMemCheck> TMemChecks;
|
||||
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 AddFromStrings(const TMemChecksStr& mcs);
|
||||
void Add(const TMemCheck& _rMemoryCheck);
|
||||
|
||||
void Add(const TMemCheck& _rMemoryCheck);
|
||||
// memory breakpoint
|
||||
TMemCheck* GetMemCheck(u32 address);
|
||||
void Remove(u32 _Address);
|
||||
|
||||
// memory breakpoint
|
||||
TMemCheck* GetMemCheck(u32 address);
|
||||
void Remove(u32 _Address);
|
||||
|
||||
void Clear() { m_MemChecks.clear(); }
|
||||
|
||||
bool HasAny() const { return !m_MemChecks.empty(); }
|
||||
void Clear() { m_MemChecks.clear(); }
|
||||
bool HasAny() const { return !m_MemChecks.empty(); }
|
||||
};
|
||||
|
||||
class Watches
|
||||
{
|
||||
public:
|
||||
typedef std::vector<TWatch> TWatches;
|
||||
typedef std::vector<std::string> TWatchesStr;
|
||||
typedef std::vector<TWatch> TWatches;
|
||||
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;
|
||||
void AddFromStrings(const TWatchesStr& bps);
|
||||
bool IsAddressWatch(u32 _iAddress) const;
|
||||
|
||||
bool IsAddressWatch(u32 _iAddress) const;
|
||||
// Add BreakPoint
|
||||
void Add(u32 em_address);
|
||||
void Add(const TWatch& bp);
|
||||
|
||||
// Add BreakPoint
|
||||
void Add(u32 em_address);
|
||||
void Add(const TWatch& bp);
|
||||
void Update(int count, u32 em_address);
|
||||
void UpdateName(int count, const std::string name);
|
||||
|
||||
void Update(int count, u32 em_address);
|
||||
void UpdateName(int count, const std::string name);
|
||||
|
||||
// Remove Breakpoint
|
||||
void Remove(u32 _iAddress);
|
||||
void Clear();
|
||||
// Remove Breakpoint
|
||||
void Remove(u32 _iAddress);
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
TWatches m_Watches;
|
||||
TWatches m_Watches;
|
||||
};
|
||||
|
|
|
@ -25,10 +25,10 @@
|
|||
#include <paths.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif // WIN32
|
||||
#endif // WIN32
|
||||
|
||||
#ifdef __linux__
|
||||
#include <linux/cdrom.h>
|
||||
|
@ -38,171 +38,163 @@
|
|||
// takes a root drive path, returns true if it is a cdrom drive
|
||||
bool is_cdrom(const TCHAR* drive)
|
||||
{
|
||||
return (DRIVE_CDROM == GetDriveType(drive));
|
||||
return (DRIVE_CDROM == GetDriveType(drive));
|
||||
}
|
||||
|
||||
// Returns a vector with the device names
|
||||
std::vector<std::string> cdio_get_devices()
|
||||
{
|
||||
std::vector<std::string> drives;
|
||||
std::vector<std::string> drives;
|
||||
|
||||
const DWORD buffsize = GetLogicalDriveStrings(0, nullptr);
|
||||
std::vector<TCHAR> buff(buffsize);
|
||||
if (GetLogicalDriveStrings(buffsize, buff.data()) == buffsize - 1)
|
||||
{
|
||||
auto drive = buff.data();
|
||||
while (*drive)
|
||||
{
|
||||
if (is_cdrom(drive))
|
||||
{
|
||||
std::string str(TStrToUTF8(drive));
|
||||
str.pop_back(); // we don't want the final backslash
|
||||
drives.push_back(std::move(str));
|
||||
}
|
||||
const DWORD buffsize = GetLogicalDriveStrings(0, nullptr);
|
||||
std::vector<TCHAR> buff(buffsize);
|
||||
if (GetLogicalDriveStrings(buffsize, buff.data()) == buffsize - 1)
|
||||
{
|
||||
auto drive = buff.data();
|
||||
while (*drive)
|
||||
{
|
||||
if (is_cdrom(drive))
|
||||
{
|
||||
std::string str(TStrToUTF8(drive));
|
||||
str.pop_back(); // we don't want the final backslash
|
||||
drives.push_back(std::move(str));
|
||||
}
|
||||
|
||||
// advance to next drive
|
||||
while (*drive++) {}
|
||||
}
|
||||
}
|
||||
return drives;
|
||||
// advance to next drive
|
||||
while (*drive++)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
return drives;
|
||||
}
|
||||
#elif defined __APPLE__
|
||||
// Returns a pointer to an array of strings with the device names
|
||||
std::vector<std::string> cdio_get_devices()
|
||||
{
|
||||
io_object_t next_media;
|
||||
mach_port_t master_port;
|
||||
kern_return_t kern_result;
|
||||
io_iterator_t media_iterator;
|
||||
CFMutableDictionaryRef classes_to_match;
|
||||
std::vector<std::string> drives;
|
||||
io_object_t next_media;
|
||||
mach_port_t master_port;
|
||||
kern_return_t kern_result;
|
||||
io_iterator_t media_iterator;
|
||||
CFMutableDictionaryRef classes_to_match;
|
||||
std::vector<std::string> drives;
|
||||
|
||||
kern_result = IOMasterPort(MACH_PORT_NULL, &master_port);
|
||||
if (kern_result != KERN_SUCCESS)
|
||||
return drives;
|
||||
kern_result = IOMasterPort(MACH_PORT_NULL, &master_port);
|
||||
if (kern_result != KERN_SUCCESS)
|
||||
return drives;
|
||||
|
||||
classes_to_match = IOServiceMatching(kIOCDMediaClass);
|
||||
if (classes_to_match == nullptr)
|
||||
return drives;
|
||||
classes_to_match = IOServiceMatching(kIOCDMediaClass);
|
||||
if (classes_to_match == nullptr)
|
||||
return drives;
|
||||
|
||||
CFDictionarySetValue(classes_to_match,
|
||||
CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
|
||||
CFDictionarySetValue(classes_to_match, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
|
||||
|
||||
kern_result = IOServiceGetMatchingServices(master_port,
|
||||
classes_to_match, &media_iterator);
|
||||
if (kern_result != KERN_SUCCESS)
|
||||
return drives;
|
||||
kern_result = IOServiceGetMatchingServices(master_port, classes_to_match, &media_iterator);
|
||||
if (kern_result != KERN_SUCCESS)
|
||||
return drives;
|
||||
|
||||
next_media = IOIteratorNext(media_iterator);
|
||||
if (next_media != 0)
|
||||
{
|
||||
CFTypeRef str_bsd_path;
|
||||
next_media = IOIteratorNext(media_iterator);
|
||||
if (next_media != 0)
|
||||
{
|
||||
CFTypeRef str_bsd_path;
|
||||
|
||||
do
|
||||
{
|
||||
str_bsd_path =
|
||||
IORegistryEntryCreateCFProperty(next_media,
|
||||
CFSTR(kIOBSDNameKey), kCFAllocatorDefault,
|
||||
0);
|
||||
if (str_bsd_path == nullptr)
|
||||
{
|
||||
IOObjectRelease(next_media);
|
||||
continue;
|
||||
}
|
||||
do
|
||||
{
|
||||
str_bsd_path =
|
||||
IORegistryEntryCreateCFProperty(next_media, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
|
||||
if (str_bsd_path == nullptr)
|
||||
{
|
||||
IOObjectRelease(next_media);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CFGetTypeID(str_bsd_path) == CFStringGetTypeID())
|
||||
{
|
||||
size_t buf_size = CFStringGetLength((CFStringRef)str_bsd_path) * 4 + 1;
|
||||
char* buf = new char[buf_size];
|
||||
if (CFGetTypeID(str_bsd_path) == CFStringGetTypeID())
|
||||
{
|
||||
size_t buf_size = CFStringGetLength((CFStringRef)str_bsd_path) * 4 + 1;
|
||||
char* buf = new char[buf_size];
|
||||
|
||||
if (CFStringGetCString((CFStringRef)str_bsd_path, buf, buf_size, kCFStringEncodingUTF8))
|
||||
{
|
||||
// Below, by appending 'r' to the BSD node name, we indicate
|
||||
// a raw disk. Raw disks receive I/O requests directly and
|
||||
// don't go through a buffer cache.
|
||||
drives.push_back(std::string(_PATH_DEV "r") + buf);
|
||||
}
|
||||
if (CFStringGetCString((CFStringRef)str_bsd_path, buf, buf_size, kCFStringEncodingUTF8))
|
||||
{
|
||||
// Below, by appending 'r' to the BSD node name, we indicate
|
||||
// a raw disk. Raw disks receive I/O requests directly and
|
||||
// don't go through a buffer cache.
|
||||
drives.push_back(std::string(_PATH_DEV "r") + buf);
|
||||
}
|
||||
|
||||
delete[] buf;
|
||||
}
|
||||
CFRelease(str_bsd_path);
|
||||
IOObjectRelease(next_media);
|
||||
} while ((next_media = IOIteratorNext(media_iterator)) != 0);
|
||||
}
|
||||
IOObjectRelease(media_iterator);
|
||||
return drives;
|
||||
delete[] buf;
|
||||
}
|
||||
CFRelease(str_bsd_path);
|
||||
IOObjectRelease(next_media);
|
||||
} while ((next_media = IOIteratorNext(media_iterator)) != 0);
|
||||
}
|
||||
IOObjectRelease(media_iterator);
|
||||
return drives;
|
||||
}
|
||||
#else
|
||||
// checklist: /dev/cdrom, /dev/dvd /dev/hd?, /dev/scd? /dev/sr?
|
||||
static struct
|
||||
{
|
||||
const char* format;
|
||||
unsigned int num_min;
|
||||
unsigned int num_max;
|
||||
} checklist[] =
|
||||
{
|
||||
const char* format;
|
||||
unsigned int num_min;
|
||||
unsigned int num_max;
|
||||
} checklist[] = {
|
||||
#ifdef __linux__
|
||||
{ "/dev/cdrom", 0, 0 },
|
||||
{ "/dev/dvd", 0, 0 },
|
||||
{ "/dev/hd%c", 'a', 'z' },
|
||||
{ "/dev/scd%d", 0, 27 },
|
||||
{ "/dev/sr%d", 0, 27 },
|
||||
{"/dev/cdrom", 0, 0}, {"/dev/dvd", 0, 0}, {"/dev/hd%c", 'a', 'z'},
|
||||
{"/dev/scd%d", 0, 27}, {"/dev/sr%d", 0, 27},
|
||||
#else
|
||||
{ "/dev/acd%d", 0, 27 },
|
||||
{ "/dev/cd%d", 0, 27 },
|
||||
{"/dev/acd%d", 0, 27},
|
||||
{"/dev/cd%d", 0, 27},
|
||||
#endif
|
||||
{ nullptr, 0, 0 }
|
||||
};
|
||||
{nullptr, 0, 0}};
|
||||
|
||||
// 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)
|
||||
{
|
||||
struct stat buf;
|
||||
if (0 != lstat(source_name.c_str(), &buf))
|
||||
return false;
|
||||
struct stat buf;
|
||||
if (0 != lstat(source_name.c_str(), &buf))
|
||||
return false;
|
||||
|
||||
return ((S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)) &&
|
||||
!S_ISLNK(buf.st_mode));
|
||||
return ((S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)) && !S_ISLNK(buf.st_mode));
|
||||
}
|
||||
|
||||
// Check a device to see if it is a DVD/CD-ROM drive
|
||||
static bool is_cdrom(const std::string& drive, char* mnttype)
|
||||
{
|
||||
// Check if the device exists
|
||||
if (!is_device(drive))
|
||||
return false;
|
||||
// Check if the device exists
|
||||
if (!is_device(drive))
|
||||
return false;
|
||||
|
||||
bool is_cd = false;
|
||||
// If it does exist, verify that it is a cdrom/dvd drive
|
||||
int cdfd = open(drive.c_str(), (O_RDONLY | O_NONBLOCK), 0);
|
||||
if (cdfd >= 0)
|
||||
{
|
||||
bool is_cd = false;
|
||||
// If it does exist, verify that it is a cdrom/dvd drive
|
||||
int cdfd = open(drive.c_str(), (O_RDONLY | O_NONBLOCK), 0);
|
||||
if (cdfd >= 0)
|
||||
{
|
||||
#ifdef __linux__
|
||||
if (ioctl(cdfd, CDROM_GET_CAPABILITY, 0) != -1)
|
||||
if (ioctl(cdfd, CDROM_GET_CAPABILITY, 0) != -1)
|
||||
#endif
|
||||
is_cd = true;
|
||||
close(cdfd);
|
||||
}
|
||||
return is_cd;
|
||||
is_cd = true;
|
||||
close(cdfd);
|
||||
}
|
||||
return is_cd;
|
||||
}
|
||||
|
||||
// Returns a pointer to an array of strings with the device names
|
||||
std::vector<std::string> cdio_get_devices()
|
||||
{
|
||||
std::vector<std::string> drives;
|
||||
// Scan the system for DVD/CD-ROM drives.
|
||||
for (unsigned int i = 0; checklist[i].format; ++i)
|
||||
{
|
||||
for (unsigned int j = checklist[i].num_min; j <= checklist[i].num_max; ++j)
|
||||
{
|
||||
std::string drive = StringFromFormat(checklist[i].format, j);
|
||||
if (is_cdrom(drive, nullptr))
|
||||
{
|
||||
drives.push_back(std::move(drive));
|
||||
}
|
||||
}
|
||||
}
|
||||
return drives;
|
||||
std::vector<std::string> drives;
|
||||
// Scan the system for DVD/CD-ROM drives.
|
||||
for (unsigned int i = 0; checklist[i].format; ++i)
|
||||
{
|
||||
for (unsigned int j = checklist[i].num_min; j <= checklist[i].num_max; ++j)
|
||||
{
|
||||
std::string drive = StringFromFormat(checklist[i].format, j);
|
||||
if (is_cdrom(drive, nullptr))
|
||||
{
|
||||
drives.push_back(std::move(drive));
|
||||
}
|
||||
}
|
||||
}
|
||||
return drives;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -210,20 +202,20 @@ std::vector<std::string> cdio_get_devices()
|
|||
bool cdio_is_cdrom(std::string device)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
// Resolve symbolic links. This allows symbolic links to valid
|
||||
// drives to be passed from the command line with the -e flag.
|
||||
char resolved_path[MAX_PATH];
|
||||
char* devname = realpath(device.c_str(), resolved_path);
|
||||
if (!devname)
|
||||
return false;
|
||||
device = devname;
|
||||
// Resolve symbolic links. This allows symbolic links to valid
|
||||
// drives to be passed from the command line with the -e flag.
|
||||
char resolved_path[MAX_PATH];
|
||||
char* devname = realpath(device.c_str(), resolved_path);
|
||||
if (!devname)
|
||||
return false;
|
||||
device = devname;
|
||||
#endif
|
||||
|
||||
std::vector<std::string> devices = cdio_get_devices();
|
||||
for (const std::string& d : devices)
|
||||
{
|
||||
if (d == device)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
std::vector<std::string> devices = cdio_get_devices();
|
||||
for (const std::string& d : devices)
|
||||
{
|
||||
if (d == device)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
||||
// Detect the CPU, so we'll know which optimizations to use
|
||||
#pragma once
|
||||
|
||||
|
@ -10,68 +9,68 @@
|
|||
|
||||
enum CPUVendor
|
||||
{
|
||||
VENDOR_INTEL = 0,
|
||||
VENDOR_AMD = 1,
|
||||
VENDOR_ARM = 2,
|
||||
VENDOR_OTHER = 3,
|
||||
VENDOR_INTEL = 0,
|
||||
VENDOR_AMD = 1,
|
||||
VENDOR_ARM = 2,
|
||||
VENDOR_OTHER = 3,
|
||||
};
|
||||
|
||||
struct CPUInfo
|
||||
{
|
||||
CPUVendor vendor = VENDOR_INTEL;
|
||||
CPUVendor vendor = VENDOR_INTEL;
|
||||
|
||||
char cpu_string[0x41] = {};
|
||||
char brand_string[0x21] = {};
|
||||
bool OS64bit = false;
|
||||
bool CPU64bit = false;
|
||||
bool Mode64bit = false;
|
||||
char cpu_string[0x41] = {};
|
||||
char brand_string[0x21] = {};
|
||||
bool OS64bit = false;
|
||||
bool CPU64bit = false;
|
||||
bool Mode64bit = false;
|
||||
|
||||
bool HTT = false;
|
||||
int num_cores = 0;
|
||||
int logical_cpu_count = 0;
|
||||
bool HTT = false;
|
||||
int num_cores = 0;
|
||||
int logical_cpu_count = 0;
|
||||
|
||||
bool bSSE = false;
|
||||
bool bSSE2 = false;
|
||||
bool bSSE3 = false;
|
||||
bool bSSSE3 = false;
|
||||
bool bPOPCNT = false;
|
||||
bool bSSE4_1 = false;
|
||||
bool bSSE4_2 = false;
|
||||
bool bLZCNT = false;
|
||||
bool bSSE4A = false;
|
||||
bool bAVX = false;
|
||||
bool bAVX2 = false;
|
||||
bool bBMI1 = false;
|
||||
bool bBMI2 = false;
|
||||
bool bFMA = false;
|
||||
bool bFMA4 = false;
|
||||
bool bAES = false;
|
||||
// FXSAVE/FXRSTOR
|
||||
bool bFXSR = false;
|
||||
bool bMOVBE = false;
|
||||
// This flag indicates that the hardware supports some mode
|
||||
// in which denormal inputs _and_ outputs are automatically set to (signed) zero.
|
||||
bool bFlushToZero = false;
|
||||
bool bLAHFSAHF64 = false;
|
||||
bool bLongMode = false;
|
||||
bool bAtom = false;
|
||||
bool bSSE = false;
|
||||
bool bSSE2 = false;
|
||||
bool bSSE3 = false;
|
||||
bool bSSSE3 = false;
|
||||
bool bPOPCNT = false;
|
||||
bool bSSE4_1 = false;
|
||||
bool bSSE4_2 = false;
|
||||
bool bLZCNT = false;
|
||||
bool bSSE4A = false;
|
||||
bool bAVX = false;
|
||||
bool bAVX2 = false;
|
||||
bool bBMI1 = false;
|
||||
bool bBMI2 = false;
|
||||
bool bFMA = false;
|
||||
bool bFMA4 = false;
|
||||
bool bAES = false;
|
||||
// FXSAVE/FXRSTOR
|
||||
bool bFXSR = false;
|
||||
bool bMOVBE = false;
|
||||
// This flag indicates that the hardware supports some mode
|
||||
// in which denormal inputs _and_ outputs are automatically set to (signed) zero.
|
||||
bool bFlushToZero = false;
|
||||
bool bLAHFSAHF64 = false;
|
||||
bool bLongMode = false;
|
||||
bool bAtom = false;
|
||||
|
||||
// ARMv8 specific
|
||||
bool bFP = false;
|
||||
bool bASIMD = false;
|
||||
bool bCRC32 = false;
|
||||
bool bSHA1 = false;
|
||||
bool bSHA2 = false;
|
||||
// ARMv8 specific
|
||||
bool bFP = false;
|
||||
bool bASIMD = false;
|
||||
bool bCRC32 = false;
|
||||
bool bSHA1 = false;
|
||||
bool bSHA2 = false;
|
||||
|
||||
// Call Detect()
|
||||
explicit CPUInfo();
|
||||
// Call Detect()
|
||||
explicit CPUInfo();
|
||||
|
||||
// Turn the CPU info into a string we can show
|
||||
std::string Summarize();
|
||||
// Turn the CPU info into a string we can show
|
||||
std::string Summarize();
|
||||
|
||||
private:
|
||||
// Detects the various CPU features
|
||||
void Detect();
|
||||
// Detects the various CPU features
|
||||
void Detect();
|
||||
};
|
||||
|
||||
extern CPUInfo cpu_info;
|
||||
|
|
|
@ -34,11 +34,14 @@
|
|||
// ewww
|
||||
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(x) (0)
|
||||
#define __has_feature(x) (0)
|
||||
#endif
|
||||
|
||||
#if (__has_feature(is_trivially_copyable) && (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || (defined(__GNUC__) && __GNUC__ >= 5)
|
||||
#define IsTriviallyCopyable(T) std::is_trivially_copyable<typename std::remove_volatile<T>::type>::value
|
||||
#if (__has_feature(is_trivially_copyable) && \
|
||||
(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__
|
||||
#define IsTriviallyCopyable(T) std::has_trivial_copy_constructor<T>::value
|
||||
#elif _MSC_VER
|
||||
|
@ -48,312 +51,310 @@
|
|||
#error No version of is_trivially_copyable
|
||||
#endif
|
||||
|
||||
|
||||
template <class T>
|
||||
struct LinkedListItem : public T
|
||||
{
|
||||
LinkedListItem<T> *next;
|
||||
LinkedListItem<T>* next;
|
||||
};
|
||||
|
||||
// Wrapper class
|
||||
class PointerWrap
|
||||
{
|
||||
public:
|
||||
enum Mode
|
||||
{
|
||||
MODE_READ = 1, // load
|
||||
MODE_WRITE, // save
|
||||
MODE_MEASURE, // calculate size
|
||||
MODE_VERIFY, // compare
|
||||
};
|
||||
enum Mode
|
||||
{
|
||||
MODE_READ = 1, // load
|
||||
MODE_WRITE, // save
|
||||
MODE_MEASURE, // calculate size
|
||||
MODE_VERIFY, // compare
|
||||
};
|
||||
|
||||
u8 **ptr;
|
||||
Mode mode;
|
||||
u8** ptr;
|
||||
Mode mode;
|
||||
|
||||
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_; }
|
||||
Mode GetMode() const { return mode; }
|
||||
switch (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>
|
||||
void Do(std::map<K, V>& x)
|
||||
{
|
||||
u32 count = (u32)x.size();
|
||||
Do(count);
|
||||
case MODE_WRITE:
|
||||
case MODE_MEASURE:
|
||||
case MODE_VERIFY:
|
||||
for (auto& elem : x)
|
||||
{
|
||||
Do(elem.first);
|
||||
Do(elem.second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (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 V>
|
||||
void Do(std::set<V>& x)
|
||||
{
|
||||
u32 count = (u32)x.size();
|
||||
Do(count);
|
||||
|
||||
case MODE_WRITE:
|
||||
case MODE_MEASURE:
|
||||
case MODE_VERIFY:
|
||||
for (auto& elem : x)
|
||||
{
|
||||
Do(elem.first);
|
||||
Do(elem.second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (mode)
|
||||
{
|
||||
case MODE_READ:
|
||||
for (x.clear(); count != 0; --count)
|
||||
{
|
||||
V value;
|
||||
Do(value);
|
||||
x.insert(value);
|
||||
}
|
||||
break;
|
||||
|
||||
template <typename V>
|
||||
void Do(std::set<V>& x)
|
||||
{
|
||||
u32 count = (u32)x.size();
|
||||
Do(count);
|
||||
case MODE_WRITE:
|
||||
case MODE_MEASURE:
|
||||
case MODE_VERIFY:
|
||||
for (V& val : x)
|
||||
{
|
||||
Do(val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case MODE_READ:
|
||||
for (x.clear(); count != 0; --count)
|
||||
{
|
||||
V value;
|
||||
Do(value);
|
||||
x.insert(value);
|
||||
}
|
||||
break;
|
||||
template <typename T>
|
||||
void Do(std::vector<T>& x)
|
||||
{
|
||||
DoContainer(x);
|
||||
}
|
||||
|
||||
case MODE_WRITE:
|
||||
case MODE_MEASURE:
|
||||
case MODE_VERIFY:
|
||||
for (V& val : x)
|
||||
{
|
||||
Do(val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
void Do(std::list<T>& x)
|
||||
{
|
||||
DoContainer(x);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Do(std::vector<T>& x)
|
||||
{
|
||||
DoContainer(x);
|
||||
}
|
||||
template <typename T>
|
||||
void Do(std::deque<T>& x)
|
||||
{
|
||||
DoContainer(x);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Do(std::list<T>& x)
|
||||
{
|
||||
DoContainer(x);
|
||||
}
|
||||
template <typename T>
|
||||
void Do(std::basic_string<T>& x)
|
||||
{
|
||||
DoContainer(x);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Do(std::deque<T>& x)
|
||||
{
|
||||
DoContainer(x);
|
||||
}
|
||||
template <typename T, typename U>
|
||||
void Do(std::pair<T, U>& x)
|
||||
{
|
||||
Do(x.first);
|
||||
Do(x.second);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Do(std::basic_string<T>& x)
|
||||
{
|
||||
DoContainer(x);
|
||||
}
|
||||
template <typename T, std::size_t N>
|
||||
void DoArray(std::array<T, N>& x)
|
||||
{
|
||||
DoArray(x.data(), (u32)x.size());
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
void Do(std::pair<T, U>& x)
|
||||
{
|
||||
Do(x.first);
|
||||
Do(x.second);
|
||||
}
|
||||
template <typename T>
|
||||
void DoArray(T* x, u32 count)
|
||||
{
|
||||
static_assert(IsTriviallyCopyable(T), "Only sane for trivially copyable types");
|
||||
DoVoid(x, count * sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
void DoArray(std::array<T, N>& x)
|
||||
{
|
||||
DoArray(x.data(), (u32)x.size());
|
||||
}
|
||||
template <typename T, std::size_t N>
|
||||
void DoArray(T (&arr)[N])
|
||||
{
|
||||
DoArray(arr, static_cast<u32>(N));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DoArray(T* x, u32 count)
|
||||
{
|
||||
static_assert(IsTriviallyCopyable(T), "Only sane for trivially copyable types");
|
||||
DoVoid(x, count * sizeof(T));
|
||||
}
|
||||
void Do(Common::Flag& flag)
|
||||
{
|
||||
bool s = flag.IsSet();
|
||||
Do(s);
|
||||
if (mode == MODE_READ)
|
||||
flag.Set(s);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
void DoArray(T (&arr)[N])
|
||||
{
|
||||
DoArray(arr, static_cast<u32>(N));
|
||||
}
|
||||
template <typename T>
|
||||
void Do(std::atomic<T>& atomic)
|
||||
{
|
||||
T temp = atomic.load();
|
||||
Do(temp);
|
||||
if (mode == MODE_READ)
|
||||
atomic.store(temp);
|
||||
}
|
||||
|
||||
void Do(Common::Flag& flag)
|
||||
{
|
||||
bool s = flag.IsSet();
|
||||
Do(s);
|
||||
if (mode == MODE_READ)
|
||||
flag.Set(s);
|
||||
}
|
||||
template <typename T>
|
||||
void Do(T& x)
|
||||
{
|
||||
static_assert(IsTriviallyCopyable(T), "Only sane for trivially copyable types");
|
||||
// Note:
|
||||
// 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>
|
||||
void Do(std::atomic<T>& atomic)
|
||||
{
|
||||
T temp = atomic.load();
|
||||
Do(temp);
|
||||
if (mode == MODE_READ)
|
||||
atomic.store(temp);
|
||||
}
|
||||
template <typename T>
|
||||
void DoPOD(T& x)
|
||||
{
|
||||
DoVoid((void*)&x, sizeof(x));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Do(T& x)
|
||||
{
|
||||
static_assert(IsTriviallyCopyable(T), "Only sane for trivially copyable types");
|
||||
// Note:
|
||||
// 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));
|
||||
}
|
||||
void Do(bool& x)
|
||||
{
|
||||
// bool's size can vary depending on platform, which can
|
||||
// cause breakages. This treats all bools as if they were
|
||||
// 8 bits in size.
|
||||
u8 stable = static_cast<u8>(x);
|
||||
|
||||
template <typename T>
|
||||
void DoPOD(T& x)
|
||||
{
|
||||
DoVoid((void*)&x, sizeof(x));
|
||||
}
|
||||
Do(stable);
|
||||
|
||||
if (mode == MODE_READ)
|
||||
x = stable != 0;
|
||||
}
|
||||
|
||||
void Do(bool& x)
|
||||
{
|
||||
// bool's size can vary depending on platform, which can
|
||||
// cause breakages. This treats all bools as if they were
|
||||
// 8 bits in size.
|
||||
u8 stable = static_cast<u8>(x);
|
||||
template <typename T>
|
||||
void DoPointer(T*& x, T* const base)
|
||||
{
|
||||
// pointers can be more than 2^31 apart, but you're using this function wrong if you need that
|
||||
// much range
|
||||
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)
|
||||
x = stable != 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DoPointer(T*& x, T* const base)
|
||||
{
|
||||
// pointers can be more than 2^31 apart, but you're using this function wrong if you need that much range
|
||||
ptrdiff_t offset = x - base;
|
||||
Do(offset);
|
||||
if (mode == MODE_READ)
|
||||
{
|
||||
x = base + offset;
|
||||
}
|
||||
}
|
||||
void DoMarker(const std::string& prevName, u32 arbitraryNumber = 0x42)
|
||||
{
|
||||
u32 cookie = arbitraryNumber;
|
||||
Do(cookie);
|
||||
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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:
|
||||
template <typename T>
|
||||
void DoContainer(T& x)
|
||||
{
|
||||
u32 size = (u32)x.size();
|
||||
Do(size);
|
||||
x.resize(size);
|
||||
template <typename T>
|
||||
void DoContainer(T& x)
|
||||
{
|
||||
u32 size = (u32)x.size();
|
||||
Do(size);
|
||||
x.resize(size);
|
||||
|
||||
for (auto& elem : x)
|
||||
Do(elem);
|
||||
}
|
||||
for (auto& elem : x)
|
||||
Do(elem);
|
||||
}
|
||||
|
||||
__forceinline
|
||||
void DoVoid(void* data, u32 size)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case MODE_READ:
|
||||
memcpy(data, *ptr, size);
|
||||
break;
|
||||
__forceinline void DoVoid(void* data, u32 size)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case MODE_READ:
|
||||
memcpy(data, *ptr, size);
|
||||
break;
|
||||
|
||||
case MODE_WRITE:
|
||||
memcpy(*ptr, data, size);
|
||||
break;
|
||||
case MODE_WRITE:
|
||||
memcpy(*ptr, data, size);
|
||||
break;
|
||||
|
||||
case MODE_MEASURE:
|
||||
break;
|
||||
case MODE_MEASURE:
|
||||
break;
|
||||
|
||||
case MODE_VERIFY:
|
||||
_dbg_assert_msg_(COMMON, !memcmp(data, *ptr, size),
|
||||
"Savestate verification failure: buf %p != %p (size %u).\n",
|
||||
data, *ptr, size);
|
||||
break;
|
||||
}
|
||||
case MODE_VERIFY:
|
||||
_dbg_assert_msg_(COMMON, !memcmp(data, *ptr, size),
|
||||
"Savestate verification failure: buf %p != %p (size %u).\n", data, *ptr,
|
||||
size);
|
||||
break;
|
||||
}
|
||||
|
||||
*ptr += size;
|
||||
}
|
||||
*ptr += size;
|
||||
}
|
||||
};
|
||||
|
||||
// NOTE: this class is only used in DolphinWX/ISOFile.cpp for caching loaded
|
||||
|
@ -361,120 +362,119 @@ private:
|
|||
class CChunkFileReader
|
||||
{
|
||||
public:
|
||||
// Load file template
|
||||
template<class T>
|
||||
static bool Load(const std::string& _rFilename, u32 _Revision, T& _class)
|
||||
{
|
||||
INFO_LOG(COMMON, "ChunkReader: Loading %s", _rFilename.c_str());
|
||||
// Load file template
|
||||
template <class T>
|
||||
static bool Load(const std::string& _rFilename, u32 _Revision, T& _class)
|
||||
{
|
||||
INFO_LOG(COMMON, "ChunkReader: Loading %s", _rFilename.c_str());
|
||||
|
||||
if (!File::Exists(_rFilename))
|
||||
return false;
|
||||
if (!File::Exists(_rFilename))
|
||||
return false;
|
||||
|
||||
// Check file size
|
||||
const u64 fileSize = File::GetSize(_rFilename);
|
||||
static const u64 headerSize = sizeof(SChunkHeader);
|
||||
if (fileSize < headerSize)
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: File too small");
|
||||
return false;
|
||||
}
|
||||
// Check file size
|
||||
const u64 fileSize = File::GetSize(_rFilename);
|
||||
static const u64 headerSize = sizeof(SChunkHeader);
|
||||
if (fileSize < headerSize)
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: File too small");
|
||||
return false;
|
||||
}
|
||||
|
||||
File::IOFile pFile(_rFilename, "rb");
|
||||
if (!pFile)
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Can't open file for reading");
|
||||
return false;
|
||||
}
|
||||
File::IOFile pFile(_rFilename, "rb");
|
||||
if (!pFile)
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Can't open file for reading");
|
||||
return false;
|
||||
}
|
||||
|
||||
// read the header
|
||||
SChunkHeader header;
|
||||
if (!pFile.ReadArray(&header, 1))
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Bad header size");
|
||||
return false;
|
||||
}
|
||||
// read the header
|
||||
SChunkHeader header;
|
||||
if (!pFile.ReadArray(&header, 1))
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Bad header size");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check revision
|
||||
if (header.Revision != _Revision)
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Wrong file revision, got %d expected %d",
|
||||
header.Revision, _Revision);
|
||||
return false;
|
||||
}
|
||||
// Check revision
|
||||
if (header.Revision != _Revision)
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Wrong file revision, got %d expected %d", header.Revision,
|
||||
_Revision);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get size
|
||||
const u32 sz = (u32)(fileSize - headerSize);
|
||||
if (header.ExpectedSize != sz)
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Bad file size, got %d expected %d",
|
||||
sz, header.ExpectedSize);
|
||||
return false;
|
||||
}
|
||||
// get size
|
||||
const u32 sz = (u32)(fileSize - headerSize);
|
||||
if (header.ExpectedSize != sz)
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Bad file size, got %d expected %d", sz, header.ExpectedSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
// read the state
|
||||
std::vector<u8> buffer(sz);
|
||||
if (!pFile.ReadArray(&buffer[0], sz))
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Error reading file");
|
||||
return false;
|
||||
}
|
||||
// read the state
|
||||
std::vector<u8> buffer(sz);
|
||||
if (!pFile.ReadArray(&buffer[0], sz))
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Error reading file");
|
||||
return false;
|
||||
}
|
||||
|
||||
u8* ptr = &buffer[0];
|
||||
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
||||
_class.DoState(p);
|
||||
u8* ptr = &buffer[0];
|
||||
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
||||
_class.DoState(p);
|
||||
|
||||
INFO_LOG(COMMON, "ChunkReader: Done loading %s", _rFilename.c_str());
|
||||
return true;
|
||||
}
|
||||
INFO_LOG(COMMON, "ChunkReader: Done loading %s", _rFilename.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Save file template
|
||||
template<class T>
|
||||
static bool Save(const std::string& _rFilename, u32 _Revision, T& _class)
|
||||
{
|
||||
INFO_LOG(COMMON, "ChunkReader: Writing %s", _rFilename.c_str());
|
||||
File::IOFile pFile(_rFilename, "wb");
|
||||
if (!pFile)
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Error opening file for write");
|
||||
return false;
|
||||
}
|
||||
// Save file template
|
||||
template <class T>
|
||||
static bool Save(const std::string& _rFilename, u32 _Revision, T& _class)
|
||||
{
|
||||
INFO_LOG(COMMON, "ChunkReader: Writing %s", _rFilename.c_str());
|
||||
File::IOFile pFile(_rFilename, "wb");
|
||||
if (!pFile)
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Error opening file for write");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get data
|
||||
u8* ptr = nullptr;
|
||||
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
|
||||
_class.DoState(p);
|
||||
size_t const sz = (size_t)ptr;
|
||||
std::vector<u8> buffer(sz);
|
||||
ptr = &buffer[0];
|
||||
p.SetMode(PointerWrap::MODE_WRITE);
|
||||
_class.DoState(p);
|
||||
// Get data
|
||||
u8* ptr = nullptr;
|
||||
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
|
||||
_class.DoState(p);
|
||||
size_t const sz = (size_t)ptr;
|
||||
std::vector<u8> buffer(sz);
|
||||
ptr = &buffer[0];
|
||||
p.SetMode(PointerWrap::MODE_WRITE);
|
||||
_class.DoState(p);
|
||||
|
||||
// Create header
|
||||
SChunkHeader header;
|
||||
header.Revision = _Revision;
|
||||
header.ExpectedSize = (u32)sz;
|
||||
// Create header
|
||||
SChunkHeader header;
|
||||
header.Revision = _Revision;
|
||||
header.ExpectedSize = (u32)sz;
|
||||
|
||||
// Write to file
|
||||
if (!pFile.WriteArray(&header, 1))
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Failed writing header");
|
||||
return false;
|
||||
}
|
||||
// Write to file
|
||||
if (!pFile.WriteArray(&header, 1))
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Failed writing header");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pFile.WriteArray(&buffer[0], sz))
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Failed writing data");
|
||||
return false;
|
||||
}
|
||||
if (!pFile.WriteArray(&buffer[0], sz))
|
||||
{
|
||||
ERROR_LOG(COMMON, "ChunkReader: Failed writing data");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO_LOG(COMMON, "ChunkReader: Done writing %s", _rFilename.c_str());
|
||||
return true;
|
||||
}
|
||||
INFO_LOG(COMMON, "ChunkReader: Done writing %s", _rFilename.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
struct SChunkHeader
|
||||
{
|
||||
u32 Revision;
|
||||
u32 ExpectedSize;
|
||||
};
|
||||
struct SChunkHeader
|
||||
{
|
||||
u32 Revision;
|
||||
u32 ExpectedSize;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -13,98 +13,92 @@
|
|||
// having to prefix them with gen-> or something similar.
|
||||
// Example implementation:
|
||||
// class JIT : public CodeBlock<ARMXEmitter> {}
|
||||
template<class T> class CodeBlock : public T, NonCopyable
|
||||
template <class T>
|
||||
class CodeBlock : public T, NonCopyable
|
||||
{
|
||||
private:
|
||||
// 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
|
||||
virtual void PoisonMemory() = 0;
|
||||
// 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
|
||||
virtual void PoisonMemory() = 0;
|
||||
|
||||
protected:
|
||||
u8* region;
|
||||
size_t region_size;
|
||||
size_t parent_region_size;
|
||||
u8* region;
|
||||
size_t region_size;
|
||||
size_t parent_region_size;
|
||||
|
||||
bool m_has_child;
|
||||
bool m_is_child;
|
||||
CodeBlock* m_child;
|
||||
bool m_has_child;
|
||||
bool m_is_child;
|
||||
CodeBlock* m_child;
|
||||
|
||||
public:
|
||||
CodeBlock()
|
||||
: region(nullptr), region_size(0), parent_region_size(0),
|
||||
m_has_child(false), m_is_child(false), m_child(nullptr)
|
||||
{
|
||||
}
|
||||
CodeBlock()
|
||||
: region(nullptr), region_size(0), parent_region_size(0), m_has_child(false),
|
||||
m_is_child(false), m_child(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~CodeBlock() { if (region) FreeCodeSpace(); }
|
||||
virtual ~CodeBlock()
|
||||
{
|
||||
if (region)
|
||||
FreeCodeSpace();
|
||||
}
|
||||
|
||||
// Call this before you generate any code.
|
||||
void AllocCodeSpace(int size, bool need_low = true)
|
||||
{
|
||||
region_size = size;
|
||||
region = (u8*)AllocateExecutableMemory(region_size, need_low);
|
||||
T::SetCodePtr(region);
|
||||
}
|
||||
// Call this before you generate any code.
|
||||
void AllocCodeSpace(int size, bool need_low = true)
|
||||
{
|
||||
region_size = size;
|
||||
region = (u8*)AllocateExecutableMemory(region_size, need_low);
|
||||
T::SetCodePtr(region);
|
||||
}
|
||||
|
||||
// Always clear code space with breakpoints, so that if someone accidentally executes
|
||||
// uninitialized, it just breaks into the debugger.
|
||||
void ClearCodeSpace()
|
||||
{
|
||||
PoisonMemory();
|
||||
ResetCodePtr();
|
||||
}
|
||||
// Always clear code space with breakpoints, so that if someone accidentally executes
|
||||
// uninitialized, it just breaks into the debugger.
|
||||
void ClearCodeSpace()
|
||||
{
|
||||
PoisonMemory();
|
||||
ResetCodePtr();
|
||||
}
|
||||
|
||||
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
|
||||
void FreeCodeSpace()
|
||||
{
|
||||
FreeMemoryPages(region, region_size);
|
||||
region = nullptr;
|
||||
region_size = 0;
|
||||
parent_region_size = 0;
|
||||
if (m_has_child)
|
||||
{
|
||||
m_child->region = nullptr;
|
||||
m_child->region_size = 0;
|
||||
}
|
||||
}
|
||||
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
|
||||
void FreeCodeSpace()
|
||||
{
|
||||
FreeMemoryPages(region, region_size);
|
||||
region = nullptr;
|
||||
region_size = 0;
|
||||
parent_region_size = 0;
|
||||
if (m_has_child)
|
||||
{
|
||||
m_child->region = nullptr;
|
||||
m_child->region_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsInSpace(u8* ptr) const
|
||||
{
|
||||
return (ptr >= region) && (ptr < (region + region_size));
|
||||
}
|
||||
bool IsInSpace(u8* ptr) const { return (ptr >= region) && (ptr < (region + region_size)); }
|
||||
// Cannot currently be undone. Will write protect the entire code region.
|
||||
// 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.
|
||||
// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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
|
||||
{
|
||||
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[] = {
|
||||
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_lut4to8[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
||||
|
||||
static const int s_lut4to8[] = {
|
||||
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 const int s_lut3to8[] = {0x00, 0x24, 0x48, 0x6D, 0x91, 0xB6, 0xDA, 0xFF};
|
||||
|
||||
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)
|
||||
{
|
||||
r = s_lut5to8[(val >> 10) & 0x1f];
|
||||
g = s_lut5to8[(val >> 5) & 0x1f];
|
||||
b = s_lut5to8[(val) & 0x1f];
|
||||
a = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = s_lut3to8[(val >> 12) & 0x7];
|
||||
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;
|
||||
b = (s_lut4to8[(val) & 0xf] * a + ((bg_color >> 16) & 0xFF) * (255 - a)) / 255;
|
||||
a = 0xFF;
|
||||
}
|
||||
return (a << 24) | (r << 16) | (g << 8) | b;
|
||||
if (val & 0x8000)
|
||||
{
|
||||
r = s_lut5to8[(val >> 10) & 0x1f];
|
||||
g = s_lut5to8[(val >> 5) & 0x1f];
|
||||
b = s_lut5to8[(val)&0x1f];
|
||||
a = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = s_lut3to8[(val >> 12) & 0x7];
|
||||
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;
|
||||
b = (s_lut4to8[(val)&0xf] * a + ((bg_color >> 16) & 0xFF) * (255 - a)) / 255;
|
||||
a = 0xFF;
|
||||
}
|
||||
return (a << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
void decode5A3image(u32* dst, u16* src, int width, int height)
|
||||
{
|
||||
for (int y = 0; y < height; y += 4)
|
||||
{
|
||||
for (int x = 0; x < width; x += 4)
|
||||
{
|
||||
for (int iy = 0; iy < 4; iy++, src += 4)
|
||||
{
|
||||
for (int ix = 0; ix < 4; ix++)
|
||||
{
|
||||
u32 RGBA = Decode5A3(Common::swap16(src[ix]));
|
||||
dst[(y + iy) * width + (x + ix)] = RGBA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int y = 0; y < height; y += 4)
|
||||
{
|
||||
for (int x = 0; x < width; x += 4)
|
||||
{
|
||||
for (int iy = 0; iy < 4; iy++, src += 4)
|
||||
{
|
||||
for (int ix = 0; ix < 4; ix++)
|
||||
{
|
||||
u32 RGBA = Decode5A3(Common::swap16(src[ix]));
|
||||
dst[(y + iy) * width + (x + ix)] = RGBA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decodeCI8image(u32* dst, u8* src, u16* pal, int width, int height)
|
||||
{
|
||||
for (int y = 0; y < height; y += 4)
|
||||
{
|
||||
for (int x = 0; x < width; x += 8)
|
||||
{
|
||||
for (int iy = 0; iy < 4; iy++, src += 8)
|
||||
{
|
||||
u32* tdst = dst+(y+iy)*width+x;
|
||||
for (int ix = 0; ix < 8; ix++)
|
||||
{
|
||||
// huh, this seems wrong. CI8, not 5A3, no?
|
||||
tdst[ix] = ColorUtil::Decode5A3(Common::swap16(pal[src[ix]]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int y = 0; y < height; y += 4)
|
||||
{
|
||||
for (int x = 0; x < width; x += 8)
|
||||
{
|
||||
for (int iy = 0; iy < 4; iy++, src += 8)
|
||||
{
|
||||
u32* tdst = dst + (y + iy) * width + x;
|
||||
for (int ix = 0; ix < 8; ix++)
|
||||
{
|
||||
// huh, this seems wrong. CI8, not 5A3, no?
|
||||
tdst[ix] = ColorUtil::Decode5A3(Common::swap16(pal[src[ix]]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
namespace ColorUtil
|
||||
{
|
||||
|
||||
void decode5A3image(u32* dst, u16* src, 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
|
||||
|
||||
// Memory leak checks
|
||||
#define CHECK_HEAP_INTEGRITY()
|
||||
#define CHECK_HEAP_INTEGRITY()
|
||||
|
||||
// Since they are always around on Windows
|
||||
#define HAVE_WX 1
|
||||
#define HAVE_OPENAL 1
|
||||
#define HAVE_WX 1
|
||||
#define HAVE_OPENAL 1
|
||||
|
||||
#define HAVE_PORTAUDIO 1
|
||||
#define HAVE_PORTAUDIO 1
|
||||
|
||||
// Debug definitions
|
||||
#if defined(_DEBUG)
|
||||
#include <crtdbg.h>
|
||||
#undef CHECK_HEAP_INTEGRITY
|
||||
#define CHECK_HEAP_INTEGRITY() {if (!_CrtCheckMemory()) PanicAlert("memory corruption detected. see log.");}
|
||||
// If you want to see how much a pain in the ass singletons are, for example:
|
||||
// {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
|
||||
#if defined(_DEBUG)
|
||||
#include <crtdbg.h>
|
||||
#undef CHECK_HEAP_INTEGRITY
|
||||
#define CHECK_HEAP_INTEGRITY() \
|
||||
{ \
|
||||
if (!_CrtCheckMemory()) \
|
||||
PanicAlert("memory corruption detected. see log."); \
|
||||
}
|
||||
// If you want to see how much a pain in the ass singletons are, for example:
|
||||
// {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
|
||||
|
||||
|
@ -80,17 +87,17 @@ extern const std::string scm_distributor_str;
|
|||
// Host communication.
|
||||
enum HOST_COMM
|
||||
{
|
||||
// Begin at 10 in case there is already messages with wParam = 0, 1, 2 and so on
|
||||
WM_USER_STOP = 10,
|
||||
WM_USER_CREATE,
|
||||
WM_USER_SETCURSOR,
|
||||
WM_USER_JOB_DISPATCH,
|
||||
// Begin at 10 in case there is already messages with wParam = 0, 1, 2 and so on
|
||||
WM_USER_STOP = 10,
|
||||
WM_USER_CREATE,
|
||||
WM_USER_SETCURSOR,
|
||||
WM_USER_JOB_DISPATCH,
|
||||
};
|
||||
|
||||
// Used for notification on emulation state
|
||||
enum EMUSTATE_CHANGE
|
||||
{
|
||||
EMUSTATE_CHANGE_PLAY = 1,
|
||||
EMUSTATE_CHANGE_PAUSE,
|
||||
EMUSTATE_CHANGE_STOP
|
||||
EMUSTATE_CHANGE_PLAY = 1,
|
||||
EMUSTATE_CHANGE_PAUSE,
|
||||
EMUSTATE_CHANGE_STOP
|
||||
};
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
template <typename T, size_t N>
|
||||
constexpr size_t ArraySize(T (&arr)[N])
|
||||
{
|
||||
return N;
|
||||
return N;
|
||||
}
|
||||
|
||||
#define b2(x) ( (x) | ( (x) >> 1) )
|
||||
#define b4(x) ( b2(x) | ( b2(x) >> 2) )
|
||||
#define b8(x) ( b4(x) | ( b4(x) >> 4) )
|
||||
#define b16(x) ( b8(x) | ( b8(x) >> 8) )
|
||||
#define b32(x) (b16(x) | (b16(x) >>16) )
|
||||
#define ROUND_UP_POW2(x) (b32(x - 1) + 1)
|
||||
#define b2(x) ((x) | ((x) >> 1))
|
||||
#define b4(x) (b2(x) | (b2(x) >> 2))
|
||||
#define b8(x) (b4(x) | (b4(x) >> 4))
|
||||
#define b16(x) (b8(x) | (b8(x) >> 8))
|
||||
#define b32(x) (b16(x) | (b16(x) >> 16))
|
||||
#define ROUND_UP_POW2(x) (b32(x - 1) + 1)
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
|
@ -36,59 +36,66 @@ constexpr size_t ArraySize(T (&arr)[N])
|
|||
#endif
|
||||
|
||||
// go to debugger mode
|
||||
#define Crash() { __builtin_trap(); }
|
||||
#define Crash() \
|
||||
{ \
|
||||
__builtin_trap(); \
|
||||
}
|
||||
|
||||
// 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
|
||||
#ifndef _rotl
|
||||
inline u32 _rotl(u32 x, int shift)
|
||||
{
|
||||
shift &= 31;
|
||||
if (!shift) return x;
|
||||
return (x << shift) | (x >> (32 - shift));
|
||||
shift &= 31;
|
||||
if (!shift)
|
||||
return x;
|
||||
return (x << shift) | (x >> (32 - shift));
|
||||
}
|
||||
|
||||
inline u32 _rotr(u32 x, int shift)
|
||||
{
|
||||
shift &= 31;
|
||||
if (!shift) return x;
|
||||
return (x >> shift) | (x << (32 - shift));
|
||||
shift &= 31;
|
||||
if (!shift)
|
||||
return x;
|
||||
return (x >> shift) | (x << (32 - shift));
|
||||
}
|
||||
#endif
|
||||
|
||||
inline u64 _rotl64(u64 x, unsigned int shift)
|
||||
{
|
||||
unsigned int n = shift % 64;
|
||||
return (x << n) | (x >> (64 - n));
|
||||
unsigned int n = shift % 64;
|
||||
return (x << n) | (x >> (64 - n));
|
||||
}
|
||||
|
||||
inline u64 _rotr64(u64 x, unsigned int shift)
|
||||
{
|
||||
unsigned int n = shift % 64;
|
||||
return (x >> n) | (x << (64 - n));
|
||||
unsigned int n = shift % 64;
|
||||
return (x >> n) | (x << (64 - n));
|
||||
}
|
||||
|
||||
#else // WIN32
|
||||
#else // WIN32
|
||||
// Function Cross-Compatibility
|
||||
#define strcasecmp _stricmp
|
||||
#define strncasecmp _strnicmp
|
||||
#define unlink _unlink
|
||||
#define vscprintf _vscprintf
|
||||
#define strcasecmp _stricmp
|
||||
#define strncasecmp _strnicmp
|
||||
#define unlink _unlink
|
||||
#define vscprintf _vscprintf
|
||||
|
||||
// 64 bit offsets for Windows
|
||||
#define fseeko _fseeki64
|
||||
#define ftello _ftelli64
|
||||
#define atoll _atoi64
|
||||
#define stat64 _stat64
|
||||
#define fstat64 _fstat64
|
||||
#define fileno _fileno
|
||||
#define fseeko _fseeki64
|
||||
#define ftello _ftelli64
|
||||
#define atoll _atoi64
|
||||
#define stat64 _stat64
|
||||
#define fstat64 _fstat64
|
||||
#define fileno _fileno
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
extern "C" {
|
||||
__declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
}
|
||||
#define Crash() {DebugBreak();}
|
||||
#endif // WIN32 ndef
|
||||
#define Crash() \
|
||||
{ \
|
||||
DebugBreak(); \
|
||||
}
|
||||
#endif // WIN32 ndef
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
|
@ -98,8 +105,14 @@ std::string GetLastErrorMsg();
|
|||
|
||||
namespace Common
|
||||
{
|
||||
inline u8 swap8(u8 _data) {return _data;}
|
||||
inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) | _data[2];}
|
||||
inline u8 swap8(u8 _data)
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
inline u32 swap24(const u8* _data)
|
||||
{
|
||||
return (_data[0] << 16) | (_data[1] << 8) | _data[2];
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
#undef swap16
|
||||
|
@ -108,69 +121,121 @@ inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) |
|
|||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);}
|
||||
inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);}
|
||||
inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);}
|
||||
inline u16 swap16(u16 _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)
|
||||
// Android NDK r10c has broken builtin byte swap routines
|
||||
// Disabled for now.
|
||||
inline u16 swap16(u16 _data) {return bswap_16(_data);}
|
||||
inline u32 swap32(u32 _data) {return bswap_32(_data);}
|
||||
inline u64 swap64(u64 _data) {return bswap_64(_data);}
|
||||
inline u16 swap16(u16 _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__
|
||||
inline __attribute__((always_inline)) u16 swap16(u16 _data)
|
||||
{return OSSwapInt16(_data);}
|
||||
{
|
||||
return OSSwapInt16(_data);
|
||||
}
|
||||
inline __attribute__((always_inline)) u32 swap32(u32 _data)
|
||||
{return OSSwapInt32(_data);}
|
||||
{
|
||||
return OSSwapInt32(_data);
|
||||
}
|
||||
inline __attribute__((always_inline)) u64 swap64(u64 _data)
|
||||
{return OSSwapInt64(_data);}
|
||||
{
|
||||
return OSSwapInt64(_data);
|
||||
}
|
||||
#elif __FreeBSD__
|
||||
inline u16 swap16(u16 _data) {return bswap16(_data);}
|
||||
inline u32 swap32(u32 _data) {return bswap32(_data);}
|
||||
inline u64 swap64(u64 _data) {return bswap64(_data);}
|
||||
inline u16 swap16(u16 _data)
|
||||
{
|
||||
return bswap16(_data);
|
||||
}
|
||||
inline u32 swap32(u32 _data)
|
||||
{
|
||||
return bswap32(_data);
|
||||
}
|
||||
inline u64 swap64(u64 _data)
|
||||
{
|
||||
return bswap64(_data);
|
||||
}
|
||||
#else
|
||||
// Slow generic implementation.
|
||||
inline u16 swap16(u16 data) {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);}
|
||||
inline u16 swap16(u16 data)
|
||||
{
|
||||
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
|
||||
|
||||
inline u16 swap16(const u8* _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);}
|
||||
inline u16 swap16(const u8* _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>
|
||||
void swap(u8*);
|
||||
|
||||
template <>
|
||||
inline void swap<1>(u8* data)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void swap<2>(u8* data)
|
||||
{
|
||||
*reinterpret_cast<u16*>(data) = swap16(data);
|
||||
*reinterpret_cast<u16*>(data) = swap16(data);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void swap<4>(u8* data)
|
||||
{
|
||||
*reinterpret_cast<u32*>(data) = swap32(data);
|
||||
*reinterpret_cast<u32*>(data) = swap32(data);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void swap<8>(u8* data)
|
||||
{
|
||||
*reinterpret_cast<u64*>(data) = swap64(data);
|
||||
*reinterpret_cast<u64*>(data) = swap64(data);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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));
|
||||
return data;
|
||||
swap<sizeof(data)>(reinterpret_cast<u8*>(&data));
|
||||
return data;
|
||||
}
|
||||
|
||||
} // Namespace Common
|
||||
|
|
|
@ -11,35 +11,35 @@
|
|||
// The user data dir
|
||||
#define ROOT_DIR "."
|
||||
#ifdef _WIN32
|
||||
#define USERDATA_DIR "User"
|
||||
#define DOLPHIN_DATA_DIR "Dolphin"
|
||||
#define USERDATA_DIR "User"
|
||||
#define DOLPHIN_DATA_DIR "Dolphin"
|
||||
#elif defined __APPLE__
|
||||
// On OS X, USERDATA_DIR exists within the .app, but *always* reference
|
||||
// the copy in Application Support instead! (Copied on first run)
|
||||
// You can use the File::GetUserPath() util for this
|
||||
#define USERDATA_DIR "Contents/Resources/User"
|
||||
#define DOLPHIN_DATA_DIR "Library/Application Support/Dolphin"
|
||||
// On OS X, USERDATA_DIR exists within the .app, but *always* reference
|
||||
// the copy in Application Support instead! (Copied on first run)
|
||||
// You can use the File::GetUserPath() util for this
|
||||
#define USERDATA_DIR "Contents/Resources/User"
|
||||
#define DOLPHIN_DATA_DIR "Library/Application Support/Dolphin"
|
||||
#elif defined ANDROID
|
||||
#define USERDATA_DIR "user"
|
||||
#define DOLPHIN_DATA_DIR "/sdcard/dolphin-emu"
|
||||
#define USERDATA_DIR "user"
|
||||
#define DOLPHIN_DATA_DIR "/sdcard/dolphin-emu"
|
||||
#else
|
||||
#define USERDATA_DIR "user"
|
||||
#define DOLPHIN_DATA_DIR "dolphin-emu"
|
||||
#define USERDATA_DIR "user"
|
||||
#define DOLPHIN_DATA_DIR "dolphin-emu"
|
||||
#endif
|
||||
|
||||
// Shared data dirs (Sys and shared User for Linux)
|
||||
#if defined(_WIN32) || defined(LINUX_LOCAL_DEV)
|
||||
#define SYSDATA_DIR "Sys"
|
||||
#define SYSDATA_DIR "Sys"
|
||||
#elif defined __APPLE__
|
||||
#define SYSDATA_DIR "Contents/Resources/Sys"
|
||||
#define SYSDATA_DIR "Contents/Resources/Sys"
|
||||
#elif defined ANDROID
|
||||
#define SYSDATA_DIR "/sdcard/dolphin-emu"
|
||||
#define SYSDATA_DIR "/sdcard/dolphin-emu"
|
||||
#else
|
||||
#ifdef DATA_DIR
|
||||
#define SYSDATA_DIR DATA_DIR "sys"
|
||||
#else
|
||||
#define SYSDATA_DIR "sys"
|
||||
#endif
|
||||
#ifdef DATA_DIR
|
||||
#define SYSDATA_DIR DATA_DIR "sys"
|
||||
#else
|
||||
#define SYSDATA_DIR "sys"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Dirs in both User and Sys
|
||||
|
@ -48,72 +48,72 @@
|
|||
#define JAP_DIR "JAP"
|
||||
|
||||
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
|
||||
#define GC_USER_DIR "GC"
|
||||
#define WII_USER_DIR "Wii"
|
||||
#define CONFIG_DIR "Config"
|
||||
#define GAMESETTINGS_DIR "GameSettings"
|
||||
#define MAPS_DIR "Maps"
|
||||
#define CACHE_DIR "Cache"
|
||||
#define SHADERCACHE_DIR "Shaders"
|
||||
#define STATESAVES_DIR "StateSaves"
|
||||
#define SCREENSHOTS_DIR "ScreenShots"
|
||||
#define LOAD_DIR "Load"
|
||||
#define HIRES_TEXTURES_DIR "Textures"
|
||||
#define DUMP_DIR "Dump"
|
||||
#define DUMP_TEXTURES_DIR "Textures"
|
||||
#define DUMP_FRAMES_DIR "Frames"
|
||||
#define DUMP_AUDIO_DIR "Audio"
|
||||
#define DUMP_DSP_DIR "DSP"
|
||||
#define LOGS_DIR "Logs"
|
||||
#define MAIL_LOGS_DIR "Mail"
|
||||
#define SHADERS_DIR "Shaders"
|
||||
#define WII_SYSCONF_DIR "shared2" DIR_SEP "sys"
|
||||
#define WII_WC24CONF_DIR "shared2" DIR_SEP "wc24"
|
||||
#define RESOURCES_DIR "Resources"
|
||||
#define THEMES_DIR "Themes"
|
||||
#define ANAGLYPH_DIR "Anaglyph"
|
||||
#define PIPES_DIR "Pipes"
|
||||
#define MEMORYWATCHER_DIR "MemoryWatcher"
|
||||
#define GC_USER_DIR "GC"
|
||||
#define WII_USER_DIR "Wii"
|
||||
#define CONFIG_DIR "Config"
|
||||
#define GAMESETTINGS_DIR "GameSettings"
|
||||
#define MAPS_DIR "Maps"
|
||||
#define CACHE_DIR "Cache"
|
||||
#define SHADERCACHE_DIR "Shaders"
|
||||
#define STATESAVES_DIR "StateSaves"
|
||||
#define SCREENSHOTS_DIR "ScreenShots"
|
||||
#define LOAD_DIR "Load"
|
||||
#define HIRES_TEXTURES_DIR "Textures"
|
||||
#define DUMP_DIR "Dump"
|
||||
#define DUMP_TEXTURES_DIR "Textures"
|
||||
#define DUMP_FRAMES_DIR "Frames"
|
||||
#define DUMP_AUDIO_DIR "Audio"
|
||||
#define DUMP_DSP_DIR "DSP"
|
||||
#define LOGS_DIR "Logs"
|
||||
#define MAIL_LOGS_DIR "Mail"
|
||||
#define SHADERS_DIR "Shaders"
|
||||
#define WII_SYSCONF_DIR "shared2" DIR_SEP "sys"
|
||||
#define WII_WC24CONF_DIR "shared2" DIR_SEP "wc24"
|
||||
#define RESOURCES_DIR "Resources"
|
||||
#define THEMES_DIR "Themes"
|
||||
#define ANAGLYPH_DIR "Anaglyph"
|
||||
#define PIPES_DIR "Pipes"
|
||||
#define MEMORYWATCHER_DIR "MemoryWatcher"
|
||||
|
||||
// This one is only used to remove it if it was present
|
||||
#define SHADERCACHE_LEGACY_DIR "ShaderCache"
|
||||
|
||||
// Filenames
|
||||
// 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 LOGGER_CONFIG "Logger.ini"
|
||||
#define LOGGER_CONFIG "Logger.ini"
|
||||
|
||||
// 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)
|
||||
#define WII_SYSCONF "SYSCONF"
|
||||
|
||||
// Files in the directory returned by GetUserPath(D_DUMP_IDX)
|
||||
#define RAM_DUMP "ram.raw"
|
||||
#define ARAM_DUMP "aram.raw"
|
||||
#define RAM_DUMP "ram.raw"
|
||||
#define ARAM_DUMP "aram.raw"
|
||||
#define FAKEVMEM_DUMP "fakevmem.raw"
|
||||
|
||||
// Files in the directory returned by GetUserPath(D_MEMORYWATCHER_IDX)
|
||||
#define MEMORYWATCHER_LOCATIONS "Locations.txt"
|
||||
#define MEMORYWATCHER_SOCKET "MemoryWatcher"
|
||||
#define MEMORYWATCHER_SOCKET "MemoryWatcher"
|
||||
|
||||
// Sys files
|
||||
#define TOTALDB "totaldb.dsy"
|
||||
#define TOTALDB "totaldb.dsy"
|
||||
|
||||
#define FONT_ANSI "font_ansi.bin"
|
||||
#define FONT_SJIS "font_sjis.bin"
|
||||
#define FONT_ANSI "font_ansi.bin"
|
||||
#define FONT_SJIS "font_sjis.bin"
|
||||
|
||||
#define DSP_IROM "dsp_rom.bin"
|
||||
#define DSP_COEF "dsp_coef.bin"
|
||||
#define DSP_IROM "dsp_rom.bin"
|
||||
#define DSP_COEF "dsp_coef.bin"
|
||||
|
||||
#define GC_IPL "IPL.bin"
|
||||
#define GC_SRAM "SRAM.raw"
|
||||
#define GC_IPL "IPL.bin"
|
||||
#define GC_SRAM "SRAM.raw"
|
||||
#define GC_MEMCARDA "MemoryCardA"
|
||||
#define GC_MEMCARDB "MemoryCardB"
|
||||
|
||||
#define WII_STATE "state.dat"
|
||||
#define WII_STATE "state.dat"
|
||||
|
||||
#define WII_SETTING "setting.txt"
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
||||
// 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
|
||||
// placed in "Common.h" instead.
|
||||
|
@ -19,12 +18,12 @@
|
|||
#define LONG int
|
||||
#endif
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef int8_t s8;
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
|
|
@ -10,103 +10,108 @@
|
|||
|
||||
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)
|
||||
{
|
||||
memcpy(d, a, n);
|
||||
memcpy(d, a, n);
|
||||
}
|
||||
|
||||
int bn_compare(const u8* a, const u8* b, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (a[i] < b[i])
|
||||
return -1;
|
||||
if (a[i] > b[i])
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (a[i] < b[i])
|
||||
return -1;
|
||||
if (a[i] > b[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bn_sub_modulus(u8* a, const u8* N, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
u32 dig;
|
||||
u8 c;
|
||||
u32 i;
|
||||
u32 dig;
|
||||
u8 c;
|
||||
|
||||
c = 0;
|
||||
for (i = n - 1; i < n; i--) {
|
||||
dig = N[i] + c;
|
||||
c = (a[i] < dig);
|
||||
a[i] -= dig;
|
||||
}
|
||||
c = 0;
|
||||
for (i = n - 1; i < n; i--)
|
||||
{
|
||||
dig = N[i] + c;
|
||||
c = (a[i] < dig);
|
||||
a[i] -= dig;
|
||||
}
|
||||
}
|
||||
|
||||
void bn_add(u8* d, const u8* a, const u8* b, const u8* N, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
u32 dig;
|
||||
u8 c;
|
||||
u32 i;
|
||||
u32 dig;
|
||||
u8 c;
|
||||
|
||||
c = 0;
|
||||
for (i = n - 1; i < n; i--) {
|
||||
dig = a[i] + b[i] + c;
|
||||
c = (dig >= 0x100);
|
||||
d[i] = dig;
|
||||
}
|
||||
c = 0;
|
||||
for (i = n - 1; i < n; i--)
|
||||
{
|
||||
dig = a[i] + b[i] + c;
|
||||
c = (dig >= 0x100);
|
||||
d[i] = dig;
|
||||
}
|
||||
|
||||
if (c)
|
||||
bn_sub_modulus(d, N, n);
|
||||
if (c)
|
||||
bn_sub_modulus(d, N, n);
|
||||
|
||||
if (bn_compare(d, N, n) >= 0)
|
||||
bn_sub_modulus(d, N, n);
|
||||
if (bn_compare(d, N, n) >= 0)
|
||||
bn_sub_modulus(d, N, n);
|
||||
}
|
||||
|
||||
void bn_mul(u8* d, const u8* a, const u8* b, const u8* N, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
u8 mask;
|
||||
u32 i;
|
||||
u8 mask;
|
||||
|
||||
bn_zero(d, n);
|
||||
bn_zero(d, n);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1) {
|
||||
bn_add(d, d, d, N, n);
|
||||
if ((a[i] & mask) != 0)
|
||||
bn_add(d, d, b, N, n);
|
||||
}
|
||||
for (i = 0; i < n; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1)
|
||||
{
|
||||
bn_add(d, d, d, 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)
|
||||
{
|
||||
u8 t[512];
|
||||
u32 i;
|
||||
u8 mask;
|
||||
u8 t[512];
|
||||
u32 i;
|
||||
u8 mask;
|
||||
|
||||
bn_zero(d, n);
|
||||
d[n-1] = 1;
|
||||
for (i = 0; i < en; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1) {
|
||||
bn_mul(t, d, d, N, n);
|
||||
if ((e[i] & mask) != 0)
|
||||
bn_mul(d, t, a, N, n);
|
||||
else
|
||||
bn_copy(d, t, n);
|
||||
}
|
||||
bn_zero(d, n);
|
||||
d[n - 1] = 1;
|
||||
for (i = 0; i < en; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1)
|
||||
{
|
||||
bn_mul(t, d, d, N, n);
|
||||
if ((e[i] & mask) != 0)
|
||||
bn_mul(d, t, a, N, n);
|
||||
else
|
||||
bn_copy(d, t, n);
|
||||
}
|
||||
}
|
||||
|
||||
// only for prime N -- stupid but lazy, see if I care
|
||||
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_zero(s, n);
|
||||
s[n-1] = 2;
|
||||
bn_sub_modulus(t, s, n);
|
||||
bn_exp(d, a, N, n, t, n);
|
||||
bn_copy(t, N, n);
|
||||
bn_zero(s, n);
|
||||
s[n - 1] = 2;
|
||||
bn_sub_modulus(t, s, 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_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_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);
|
||||
|
|
|
@ -16,379 +16,391 @@
|
|||
#include "Common/Crypto/ec.h"
|
||||
|
||||
// y**2 + x*y = x**3 + x + b
|
||||
UNUSED static const u8 ec_b[30] =
|
||||
{0x00,0x66,0x64,0x7e,0xde,0x6c,0x33,0x2c,0x7f,0x8c,0x09,0x23,0xbb,0x58,0x21
|
||||
,0x3b,0x33,0x3b,0x20,0xe9,0xce,0x42,0x81,0xfe,0x11,0x5f,0x7d,0x8f,0x90,0xad};
|
||||
UNUSED static const u8 ec_b[30] = {0x00, 0x66, 0x64, 0x7e, 0xde, 0x6c, 0x33, 0x2c, 0x7f, 0x8c,
|
||||
0x09, 0x23, 0xbb, 0x58, 0x21, 0x3b, 0x33, 0x3b, 0x20, 0xe9,
|
||||
0xce, 0x42, 0x81, 0xfe, 0x11, 0x5f, 0x7d, 0x8f, 0x90, 0xad};
|
||||
|
||||
// order of the addition group of points
|
||||
static const u8 ec_N[30] =
|
||||
{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
|
||||
,0x13,0xe9,0x74,0xe7,0x2f,0x8a,0x69,0x22,0x03,0x1d,0x26,0x03,0xcf,0xe0,0xd7};
|
||||
static const u8 ec_N[30] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xe9, 0x74, 0xe7, 0x2f,
|
||||
0x8a, 0x69, 0x22, 0x03, 0x1d, 0x26, 0x03, 0xcf, 0xe0, 0xd7};
|
||||
|
||||
// base point
|
||||
static const u8 ec_G[60] =
|
||||
{0x00,0xfa,0xc9,0xdf,0xcb,0xac,0x83,0x13,0xbb,0x21,0x39,0xf1,0xbb,0x75,0x5f
|
||||
,0xef,0x65,0xbc,0x39,0x1f,0x8b,0x36,0xf8,0xf8,0xeb,0x73,0x71,0xfd,0x55,0x8b
|
||||
,0x01,0x00,0x6a,0x08,0xa4,0x19,0x03,0x35,0x06,0x78,0xe5,0x85,0x28,0xbe,0xbf
|
||||
,0x8a,0x0b,0xef,0xf8,0x67,0xa7,0xca,0x36,0x71,0x6f,0x7e,0x01,0xf8,0x10,0x52};
|
||||
static const u8 ec_G[60] = {0x00, 0xfa, 0xc9, 0xdf, 0xcb, 0xac, 0x83, 0x13, 0xbb, 0x21, 0x39, 0xf1,
|
||||
0xbb, 0x75, 0x5f, 0xef, 0x65, 0xbc, 0x39, 0x1f, 0x8b, 0x36, 0xf8, 0xf8,
|
||||
0xeb, 0x73, 0x71, 0xfd, 0x55, 0x8b, 0x01, 0x00, 0x6a, 0x08, 0xa4, 0x19,
|
||||
0x03, 0x35, 0x06, 0x78, 0xe5, 0x85, 0x28, 0xbe, 0xbf, 0x8a, 0x0b, 0xef,
|
||||
0xf8, 0x67, 0xa7, 0xca, 0x36, 0x71, 0x6f, 0x7e, 0x01, 0xf8, 0x10, 0x52};
|
||||
|
||||
static void elt_copy(u8* d, const u8* a)
|
||||
{
|
||||
memcpy(d, a, 30);
|
||||
memcpy(d, a, 30);
|
||||
}
|
||||
|
||||
static void elt_zero(u8* d)
|
||||
{
|
||||
memset(d, 0, 30);
|
||||
memset(d, 0, 30);
|
||||
}
|
||||
|
||||
static int elt_is_zero(const u8* d)
|
||||
{
|
||||
u32 i;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
if (d[i] != 0)
|
||||
return 0;
|
||||
for (i = 0; i < 30; i++)
|
||||
if (d[i] != 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void elt_add(u8* d, const u8* a, const u8* b)
|
||||
{
|
||||
u32 i;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
d[i] = a[i] ^ b[i];
|
||||
for (i = 0; i < 30; i++)
|
||||
d[i] = a[i] ^ b[i];
|
||||
}
|
||||
|
||||
static void elt_mul_x(u8* d, const u8* a)
|
||||
{
|
||||
u8 carry, x, y;
|
||||
u32 i;
|
||||
u8 carry, x, y;
|
||||
u32 i;
|
||||
|
||||
carry = a[0] & 1;
|
||||
carry = a[0] & 1;
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < 29; i++) {
|
||||
y = a[i + 1];
|
||||
d[i] = x ^ (y >> 7);
|
||||
x = y << 1;
|
||||
}
|
||||
d[29] = x ^ carry;
|
||||
x = 0;
|
||||
for (i = 0; i < 29; i++)
|
||||
{
|
||||
y = a[i + 1];
|
||||
d[i] = x ^ (y >> 7);
|
||||
x = y << 1;
|
||||
}
|
||||
d[29] = x ^ carry;
|
||||
|
||||
d[20] ^= carry << 2;
|
||||
d[20] ^= carry << 2;
|
||||
}
|
||||
|
||||
static void elt_mul(u8* d, const u8* a, const u8* b)
|
||||
{
|
||||
u32 i, n;
|
||||
u8 mask;
|
||||
u32 i, n;
|
||||
u8 mask;
|
||||
|
||||
elt_zero(d);
|
||||
elt_zero(d);
|
||||
|
||||
i = 0;
|
||||
mask = 1;
|
||||
for (n = 0; n < 233; n++) {
|
||||
elt_mul_x(d, d);
|
||||
i = 0;
|
||||
mask = 1;
|
||||
for (n = 0; n < 233; n++)
|
||||
{
|
||||
elt_mul_x(d, d);
|
||||
|
||||
if ((a[i] & mask) != 0)
|
||||
elt_add(d, d, b);
|
||||
if ((a[i] & mask) != 0)
|
||||
elt_add(d, d, b);
|
||||
|
||||
mask >>= 1;
|
||||
if (mask == 0) {
|
||||
mask = 0x80;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
mask >>= 1;
|
||||
if (mask == 0)
|
||||
{
|
||||
mask = 0x80;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const u8 square[16] =
|
||||
{0x00,0x01,0x04,0x05,0x10,0x11,0x14,0x15,0x40,0x41,0x44,0x45,0x50,0x51,0x54,0x55};
|
||||
static const u8 square[16] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15,
|
||||
0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55};
|
||||
|
||||
static void elt_square_to_wide(u8* d, const u8* a)
|
||||
{
|
||||
u32 i;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < 30; i++) {
|
||||
d[2*i] = square[a[i] >> 4];
|
||||
d[2*i + 1] = square[a[i] & 15];
|
||||
}
|
||||
for (i = 0; i < 30; i++)
|
||||
{
|
||||
d[2 * i] = square[a[i] >> 4];
|
||||
d[2 * i + 1] = square[a[i] & 15];
|
||||
}
|
||||
}
|
||||
|
||||
static void wide_reduce(u8* d)
|
||||
{
|
||||
u32 i;
|
||||
u8 x;
|
||||
u32 i;
|
||||
u8 x;
|
||||
|
||||
for (i = 0; i < 30; i++) {
|
||||
x = d[i];
|
||||
for (i = 0; i < 30; i++)
|
||||
{
|
||||
x = d[i];
|
||||
|
||||
d[i + 19] ^= x >> 7;
|
||||
d[i + 20] ^= x << 1;
|
||||
d[i + 19] ^= x >> 7;
|
||||
d[i + 20] ^= x << 1;
|
||||
|
||||
d[i + 29] ^= x >> 1;
|
||||
d[i + 30] ^= x << 7;
|
||||
}
|
||||
d[i + 29] ^= x >> 1;
|
||||
d[i + 30] ^= x << 7;
|
||||
}
|
||||
|
||||
x = d[30] & ~1;
|
||||
x = d[30] & ~1;
|
||||
|
||||
d[49] ^= x >> 7;
|
||||
d[50] ^= x << 1;
|
||||
d[49] ^= x >> 7;
|
||||
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)
|
||||
{
|
||||
u8 wide[60];
|
||||
u8 wide[60];
|
||||
|
||||
elt_square_to_wide(wide, a);
|
||||
wide_reduce(wide);
|
||||
elt_square_to_wide(wide, a);
|
||||
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)
|
||||
{
|
||||
u8 t[30];
|
||||
u8 t[30];
|
||||
|
||||
elt_copy(t, a);
|
||||
while (j--) {
|
||||
elt_square(d, t);
|
||||
elt_copy(t, d);
|
||||
}
|
||||
elt_copy(t, a);
|
||||
while (j--)
|
||||
{
|
||||
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)
|
||||
{
|
||||
u8 t[30];
|
||||
u8 s[30];
|
||||
u8 t[30];
|
||||
u8 s[30];
|
||||
|
||||
itoh_tsujii(t, a, a, 1);
|
||||
itoh_tsujii(s, t, a, 1);
|
||||
itoh_tsujii(t, s, s, 3);
|
||||
itoh_tsujii(s, t, a, 1);
|
||||
itoh_tsujii(t, s, s, 7);
|
||||
itoh_tsujii(s, t, t, 14);
|
||||
itoh_tsujii(t, s, a, 1);
|
||||
itoh_tsujii(s, t, t, 29);
|
||||
itoh_tsujii(t, s, s, 58);
|
||||
itoh_tsujii(s, t, t, 116);
|
||||
elt_square(d, s);
|
||||
itoh_tsujii(t, a, a, 1);
|
||||
itoh_tsujii(s, t, a, 1);
|
||||
itoh_tsujii(t, s, s, 3);
|
||||
itoh_tsujii(s, t, a, 1);
|
||||
itoh_tsujii(t, s, s, 7);
|
||||
itoh_tsujii(s, t, t, 14);
|
||||
itoh_tsujii(t, s, a, 1);
|
||||
itoh_tsujii(s, t, t, 29);
|
||||
itoh_tsujii(t, s, s, 58);
|
||||
itoh_tsujii(s, t, t, 116);
|
||||
elt_square(d, s);
|
||||
}
|
||||
|
||||
UNUSED static int point_is_on_curve(u8* p)
|
||||
{
|
||||
u8 s[30], t[30];
|
||||
u8* x, *y;
|
||||
u8 s[30], t[30];
|
||||
u8 *x, *y;
|
||||
|
||||
x = p;
|
||||
y = p + 30;
|
||||
x = p;
|
||||
y = p + 30;
|
||||
|
||||
elt_square(t, x);
|
||||
elt_mul(s, t, x);
|
||||
elt_square(t, x);
|
||||
elt_mul(s, t, x);
|
||||
|
||||
elt_add(s, s, t);
|
||||
elt_add(s, s, t);
|
||||
|
||||
elt_square(t, y);
|
||||
elt_add(s, s, t);
|
||||
elt_square(t, y);
|
||||
elt_add(s, s, t);
|
||||
|
||||
elt_mul(t, x, y);
|
||||
elt_add(s, s, t);
|
||||
elt_mul(t, x, y);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
u8 s[30], t[30];
|
||||
const u8* px, *py;
|
||||
u8* rx, *ry;
|
||||
u8 s[30], t[30];
|
||||
const u8 *px, *py;
|
||||
u8 *rx, *ry;
|
||||
|
||||
px = p;
|
||||
py = p + 30;
|
||||
rx = r;
|
||||
ry = r + 30;
|
||||
px = p;
|
||||
py = p + 30;
|
||||
rx = r;
|
||||
ry = r + 30;
|
||||
|
||||
if (elt_is_zero(px)) {
|
||||
elt_zero(rx);
|
||||
elt_zero(ry);
|
||||
if (elt_is_zero(px))
|
||||
{
|
||||
elt_zero(rx);
|
||||
elt_zero(ry);
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
elt_inv(t, px);
|
||||
elt_mul(s, py, t);
|
||||
elt_add(s, s, px);
|
||||
elt_inv(t, px);
|
||||
elt_mul(s, py, t);
|
||||
elt_add(s, s, px);
|
||||
|
||||
elt_square(t, px);
|
||||
elt_square(t, px);
|
||||
|
||||
elt_square(rx, s);
|
||||
elt_add(rx, rx, s);
|
||||
rx[29] ^= 1;
|
||||
elt_square(rx, s);
|
||||
elt_add(rx, rx, s);
|
||||
rx[29] ^= 1;
|
||||
|
||||
elt_mul(ry, s, rx);
|
||||
elt_add(ry, ry, rx);
|
||||
elt_add(ry, ry, t);
|
||||
elt_mul(ry, s, rx);
|
||||
elt_add(ry, ry, rx);
|
||||
elt_add(ry, ry, t);
|
||||
}
|
||||
|
||||
static void point_add(u8* r, const u8* p, const u8* q)
|
||||
{
|
||||
u8 s[30], t[30], u[30];
|
||||
const u8* px, *py, *qx, *qy;
|
||||
u8* rx, *ry;
|
||||
u8 s[30], t[30], u[30];
|
||||
const u8 *px, *py, *qx, *qy;
|
||||
u8 *rx, *ry;
|
||||
|
||||
px = p;
|
||||
py = p + 30;
|
||||
qx = q;
|
||||
qy = q + 30;
|
||||
rx = r;
|
||||
ry = r + 30;
|
||||
px = p;
|
||||
py = p + 30;
|
||||
qx = q;
|
||||
qy = q + 30;
|
||||
rx = r;
|
||||
ry = r + 30;
|
||||
|
||||
if (point_is_zero(p)) {
|
||||
elt_copy(rx, qx);
|
||||
elt_copy(ry, qy);
|
||||
return;
|
||||
}
|
||||
if (point_is_zero(p))
|
||||
{
|
||||
elt_copy(rx, qx);
|
||||
elt_copy(ry, qy);
|
||||
return;
|
||||
}
|
||||
|
||||
if (point_is_zero(q)) {
|
||||
elt_copy(rx, px);
|
||||
elt_copy(ry, py);
|
||||
return;
|
||||
}
|
||||
if (point_is_zero(q))
|
||||
{
|
||||
elt_copy(rx, px);
|
||||
elt_copy(ry, py);
|
||||
return;
|
||||
}
|
||||
|
||||
elt_add(u, px, qx);
|
||||
elt_add(u, px, qx);
|
||||
|
||||
if (elt_is_zero(u)) {
|
||||
elt_add(u, py, qy);
|
||||
if (elt_is_zero(u))
|
||||
point_double(r, p);
|
||||
else {
|
||||
elt_zero(rx);
|
||||
elt_zero(ry);
|
||||
}
|
||||
if (elt_is_zero(u))
|
||||
{
|
||||
elt_add(u, py, qy);
|
||||
if (elt_is_zero(u))
|
||||
point_double(r, p);
|
||||
else
|
||||
{
|
||||
elt_zero(rx);
|
||||
elt_zero(ry);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
elt_inv(t, u);
|
||||
elt_add(u, py, qy);
|
||||
elt_mul(s, t, u);
|
||||
elt_inv(t, u);
|
||||
elt_add(u, py, qy);
|
||||
elt_mul(s, t, u);
|
||||
|
||||
elt_square(t, s);
|
||||
elt_add(t, t, s);
|
||||
elt_add(t, t, qx);
|
||||
t[29] ^= 1;
|
||||
elt_square(t, s);
|
||||
elt_add(t, t, s);
|
||||
elt_add(t, t, qx);
|
||||
t[29] ^= 1;
|
||||
|
||||
elt_mul(u, s, t);
|
||||
elt_add(s, u, py);
|
||||
elt_add(rx, t, px);
|
||||
elt_add(ry, s, rx);
|
||||
elt_mul(u, s, t);
|
||||
elt_add(s, u, py);
|
||||
elt_add(rx, t, px);
|
||||
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;
|
||||
u8 mask;
|
||||
u32 i;
|
||||
u8 mask;
|
||||
|
||||
elt_zero(d);
|
||||
elt_zero(d + 30);
|
||||
elt_zero(d);
|
||||
elt_zero(d + 30);
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1) {
|
||||
point_double(d, d);
|
||||
if ((a[i] & mask) != 0)
|
||||
point_add(d, d, b);
|
||||
}
|
||||
for (i = 0; i < 30; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1)
|
||||
{
|
||||
point_double(d, d);
|
||||
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;
|
||||
srand((unsigned) (time(nullptr)));
|
||||
u16 i;
|
||||
srand((unsigned)(time(nullptr)));
|
||||
|
||||
for (i=0;i<count;i++)
|
||||
{
|
||||
rndArea[i]=rand();
|
||||
}
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
rndArea[i] = rand();
|
||||
}
|
||||
}
|
||||
|
||||
void generate_ecdsa(u8* R, u8* S, const u8* k, const u8* hash)
|
||||
{
|
||||
u8 e[30];
|
||||
u8 kk[30];
|
||||
u8 m[30];
|
||||
u8 minv[30];
|
||||
u8 mG[60];
|
||||
//FILE *fp;
|
||||
u8 e[30];
|
||||
u8 kk[30];
|
||||
u8 m[30];
|
||||
u8 minv[30];
|
||||
u8 mG[60];
|
||||
// FILE *fp;
|
||||
|
||||
elt_zero(e);
|
||||
memcpy(e + 10, hash, 20);
|
||||
elt_zero(e);
|
||||
memcpy(e + 10, hash, 20);
|
||||
|
||||
//Changing random number generator to a lame one...
|
||||
silly_random(m, sizeof(m));
|
||||
//fp = fopen("/dev/random", "rb");
|
||||
//if (fread(m, sizeof m, 1, fp) != 1)
|
||||
// fatal("reading random");
|
||||
//fclose(fp);
|
||||
m[0] = 0;
|
||||
// Changing random number generator to a lame one...
|
||||
silly_random(m, sizeof(m));
|
||||
// fp = fopen("/dev/random", "rb");
|
||||
// if (fread(m, sizeof m, 1, fp) != 1)
|
||||
// fatal("reading random");
|
||||
// fclose(fp);
|
||||
m[0] = 0;
|
||||
|
||||
// R = (mG).x
|
||||
// R = (mG).x
|
||||
|
||||
point_mul(mG, m, ec_G);
|
||||
elt_copy(R, mG);
|
||||
if (bn_compare(R, ec_N, 30) >= 0)
|
||||
bn_sub_modulus(R, ec_N, 30);
|
||||
point_mul(mG, m, ec_G);
|
||||
elt_copy(R, mG);
|
||||
if (bn_compare(R, ec_N, 30) >= 0)
|
||||
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);
|
||||
if (bn_compare(kk, ec_N, 30) >= 0)
|
||||
bn_sub_modulus(kk, ec_N, 30);
|
||||
bn_mul(S, R, kk, ec_N, 30);
|
||||
bn_add(kk, S, e, ec_N, 30);
|
||||
bn_inv(minv, m, ec_N, 30);
|
||||
bn_mul(S, minv, kk, ec_N, 30);
|
||||
elt_copy(kk, k);
|
||||
if (bn_compare(kk, ec_N, 30) >= 0)
|
||||
bn_sub_modulus(kk, ec_N, 30);
|
||||
bn_mul(S, R, kk, ec_N, 30);
|
||||
bn_add(kk, S, e, ec_N, 30);
|
||||
bn_inv(minv, m, 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)
|
||||
{
|
||||
u8 Sinv[30];
|
||||
u8 e[30];
|
||||
u8 w1[30], w2[30];
|
||||
u8 r1[60], r2[60];
|
||||
u8 Sinv[30];
|
||||
u8 e[30];
|
||||
u8 w1[30], w2[30];
|
||||
u8 r1[60], r2[60];
|
||||
|
||||
bn_inv(Sinv, S, ec_N, 30);
|
||||
bn_inv(Sinv, S, ec_N, 30);
|
||||
|
||||
elt_zero(e);
|
||||
memcpy(e + 10, hash, 20);
|
||||
elt_zero(e);
|
||||
memcpy(e + 10, hash, 20);
|
||||
|
||||
bn_mul(w1, e, Sinv, ec_N, 30);
|
||||
bn_mul(w2, R, Sinv, ec_N, 30);
|
||||
bn_mul(w1, e, Sinv, ec_N, 30);
|
||||
bn_mul(w2, R, Sinv, ec_N, 30);
|
||||
|
||||
point_mul(r1, w1, ec_G);
|
||||
point_mul(r2, w2, Q);
|
||||
point_mul(r1, w1, ec_G);
|
||||
point_mul(r2, w2, Q);
|
||||
|
||||
point_add(r1, r1, r2);
|
||||
point_add(r1, r1, r2);
|
||||
|
||||
if (bn_compare(r1, ec_N, 30) >= 0)
|
||||
bn_sub_modulus(r1, ec_N, 30);
|
||||
if (bn_compare(r1, ec_N, 30) >= 0)
|
||||
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)
|
||||
{
|
||||
point_mul(Q, k, ec_G);
|
||||
point_mul(Q, k, ec_G);
|
||||
}
|
||||
|
|
|
@ -10,31 +10,34 @@
|
|||
class DebugInterface
|
||||
{
|
||||
protected:
|
||||
virtual ~DebugInterface() {}
|
||||
|
||||
virtual ~DebugInterface() {}
|
||||
public:
|
||||
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 int GetInstructionSize(int /*instruction*/) {return 1;}
|
||||
virtual bool IsAlive() {return true;}
|
||||
virtual bool IsBreakpoint(unsigned int /*address*/) {return false;}
|
||||
virtual void SetBreakpoint(unsigned int /*address*/){}
|
||||
virtual void ClearBreakpoint(unsigned int /*address*/){}
|
||||
virtual void ClearAllBreakpoints() {}
|
||||
virtual void ToggleBreakpoint(unsigned int /*address*/){}
|
||||
virtual void AddWatch(unsigned int /*address*/){}
|
||||
virtual void ClearAllMemChecks() {}
|
||||
virtual bool IsMemCheck(unsigned int /*address*/) {return false;}
|
||||
virtual void ToggleMemCheck(unsigned int /*address*/){}
|
||||
virtual unsigned int ReadMemory(unsigned int /*address*/){return 0;}
|
||||
virtual void WriteExtraMemory(int /*memory*/, unsigned int /*value*/, unsigned int /*address*/) {}
|
||||
virtual unsigned int ReadExtraMemory(int /*memory*/, unsigned int /*address*/){return 0;}
|
||||
virtual unsigned int ReadInstruction(unsigned int /*address*/){return 0;}
|
||||
virtual unsigned int GetPC() {return 0;}
|
||||
virtual void SetPC(unsigned int /*address*/) {}
|
||||
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;
|
||||
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 int GetInstructionSize(int /*instruction*/) { return 1; }
|
||||
virtual bool IsAlive() { return true; }
|
||||
virtual bool IsBreakpoint(unsigned int /*address*/) { return false; }
|
||||
virtual void SetBreakpoint(unsigned int /*address*/) {}
|
||||
virtual void ClearBreakpoint(unsigned int /*address*/) {}
|
||||
virtual void ClearAllBreakpoints() {}
|
||||
virtual void ToggleBreakpoint(unsigned int /*address*/) {}
|
||||
virtual void AddWatch(unsigned int /*address*/) {}
|
||||
virtual void ClearAllMemChecks() {}
|
||||
virtual bool IsMemCheck(unsigned int /*address*/) { return false; }
|
||||
virtual void ToggleMemCheck(unsigned int /*address*/) {}
|
||||
virtual unsigned int ReadMemory(unsigned int /*address*/) { return 0; }
|
||||
virtual void WriteExtraMemory(int /*memory*/, unsigned int /*value*/, unsigned int /*address*/) {}
|
||||
virtual unsigned int ReadExtraMemory(int /*memory*/, unsigned int /*address*/) { return 0; }
|
||||
virtual unsigned int ReadInstruction(unsigned int /*address*/) { return 0; }
|
||||
virtual unsigned int GetPC() { return 0; }
|
||||
virtual void SetPC(unsigned int /*address*/) {}
|
||||
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
|
||||
{
|
||||
|
||||
void WakeupThread(ENetHost* host)
|
||||
{
|
||||
// Send ourselves a spurious message. This is hackier than it should be.
|
||||
// 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.
|
||||
ENetAddress address;
|
||||
if (host->address.port != 0)
|
||||
address.port = host->address.port;
|
||||
else
|
||||
enet_socket_get_address(host->socket, &address);
|
||||
address.host = 0x0100007f; // localhost
|
||||
u8 byte = 0;
|
||||
ENetBuffer buf;
|
||||
buf.data = &byte;
|
||||
buf.dataLength = 1;
|
||||
enet_socket_send(host->socket, &address, &buf, 1);
|
||||
// Send ourselves a spurious message. This is hackier than it should be.
|
||||
// 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.
|
||||
ENetAddress address;
|
||||
if (host->address.port != 0)
|
||||
address.port = host->address.port;
|
||||
else
|
||||
enet_socket_get_address(host->socket, &address);
|
||||
address.host = 0x0100007f; // localhost
|
||||
u8 byte = 0;
|
||||
ENetBuffer buf;
|
||||
buf.data = &byte;
|
||||
buf.dataLength = 1;
|
||||
enet_socket_send(host->socket, &address, &buf, 1);
|
||||
}
|
||||
|
||||
int ENET_CALLBACK InterceptCallback(ENetHost* host, ENetEvent* event)
|
||||
{
|
||||
// wakeup packet received
|
||||
if (host->receivedDataLength == 1 && host->receivedData[0] == 0)
|
||||
{
|
||||
event->type = (ENetEventType) 42;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
// wakeup packet received
|
||||
if (host->receivedDataLength == 1 && host->receivedData[0] == 0)
|
||||
{
|
||||
event->type = (ENetEventType)42;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
namespace ENetUtil
|
||||
{
|
||||
|
||||
void WakeupThread(ENetHost* host);
|
||||
int ENET_CALLBACK InterceptCallback(ENetHost* host, ENetEvent* event);
|
||||
|
||||
}
|
||||
|
|
|
@ -22,54 +22,53 @@
|
|||
|
||||
#include "Common/Flag.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace Common
|
||||
{
|
||||
class Event final
|
||||
{
|
||||
public:
|
||||
void Set()
|
||||
{
|
||||
if (m_flag.TestAndSet())
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mutex);
|
||||
m_condvar.notify_one();
|
||||
}
|
||||
}
|
||||
void Set()
|
||||
{
|
||||
if (m_flag.TestAndSet())
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m_mutex);
|
||||
m_condvar.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void Wait()
|
||||
{
|
||||
if (m_flag.TestAndClear())
|
||||
return;
|
||||
void Wait()
|
||||
{
|
||||
if (m_flag.TestAndClear())
|
||||
return;
|
||||
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
m_condvar.wait(lk, [&]{ return m_flag.TestAndClear(); });
|
||||
}
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
m_condvar.wait(lk, [&] { return m_flag.TestAndClear(); });
|
||||
}
|
||||
|
||||
template<class Rep, class Period>
|
||||
bool WaitFor(const std::chrono::duration<Rep, Period>& rel_time)
|
||||
{
|
||||
if (m_flag.TestAndClear())
|
||||
return true;
|
||||
template <class Rep, class Period>
|
||||
bool WaitFor(const std::chrono::duration<Rep, Period>& rel_time)
|
||||
{
|
||||
if (m_flag.TestAndClear())
|
||||
return true;
|
||||
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
bool signaled = m_condvar.wait_for(lk, rel_time,
|
||||
[&]{ return m_flag.TestAndClear(); });
|
||||
std::unique_lock<std::mutex> lk(m_mutex);
|
||||
bool signaled = m_condvar.wait_for(lk, rel_time, [&] { return m_flag.TestAndClear(); });
|
||||
|
||||
return signaled;
|
||||
}
|
||||
return signaled;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
// no other action required, since wait loops on
|
||||
// the predicate and any lingering signal will get
|
||||
// cleared on the first iteration
|
||||
m_flag.Clear();
|
||||
}
|
||||
void Reset()
|
||||
{
|
||||
// no other action required, since wait loops on
|
||||
// the predicate and any lingering signal will get
|
||||
// cleared on the first iteration
|
||||
m_flag.Clear();
|
||||
}
|
||||
|
||||
private:
|
||||
Flag m_flag;
|
||||
std::condition_variable m_condvar;
|
||||
std::mutex m_mutex;
|
||||
Flag m_flag;
|
||||
std::condition_variable m_condvar;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
@ -8,29 +8,30 @@
|
|||
|
||||
namespace FPURoundMode
|
||||
{
|
||||
// TODO: MSVC currently produces broken code:
|
||||
// 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'.
|
||||
// TODO: MSVC currently produces broken code:
|
||||
// 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'.
|
||||
|
||||
enum RoundMode
|
||||
{
|
||||
ROUND_NEAR = 0,
|
||||
ROUND_CHOP = 1,
|
||||
ROUND_UP = 2,
|
||||
ROUND_DOWN = 3
|
||||
};
|
||||
enum PrecisionMode
|
||||
{
|
||||
PREC_24 = 0,
|
||||
PREC_53 = 1,
|
||||
PREC_64 = 2
|
||||
};
|
||||
enum RoundMode
|
||||
{
|
||||
ROUND_NEAR = 0,
|
||||
ROUND_CHOP = 1,
|
||||
ROUND_UP = 2,
|
||||
ROUND_DOWN = 3
|
||||
};
|
||||
enum PrecisionMode
|
||||
{
|
||||
PREC_24 = 0,
|
||||
PREC_53 = 1,
|
||||
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:
|
||||
|
@ -39,7 +40,7 @@ namespace FPURoundMode
|
|||
* The first rounds according to the MXCSR rounding bits.
|
||||
* The second one always uses round towards zero.
|
||||
*/
|
||||
void SaveSIMDState();
|
||||
void LoadSIMDState();
|
||||
void LoadDefaultSIMDState();
|
||||
void SaveSIMDState();
|
||||
void LoadSIMDState();
|
||||
void LoadDefaultSIMDState();
|
||||
}
|
||||
|
|
|
@ -15,111 +15,96 @@
|
|||
|
||||
namespace Common
|
||||
{
|
||||
|
||||
template <typename T, bool NeedSize = true>
|
||||
class FifoQueue
|
||||
{
|
||||
public:
|
||||
FifoQueue() : m_size(0)
|
||||
{
|
||||
m_write_ptr = m_read_ptr = new ElementPtr();
|
||||
}
|
||||
FifoQueue() : m_size(0) { m_write_ptr = m_read_ptr = new ElementPtr(); }
|
||||
~FifoQueue()
|
||||
{
|
||||
// this will empty out the whole queue
|
||||
delete m_read_ptr;
|
||||
}
|
||||
|
||||
~FifoQueue()
|
||||
{
|
||||
// this will empty out the whole queue
|
||||
delete m_read_ptr;
|
||||
}
|
||||
u32 Size() const
|
||||
{
|
||||
static_assert(NeedSize, "using Size() on FifoQueue without NeedSize");
|
||||
return m_size.load();
|
||||
}
|
||||
|
||||
u32 Size() const
|
||||
{
|
||||
static_assert(NeedSize, "using Size() on FifoQueue without NeedSize");
|
||||
return m_size.load();
|
||||
}
|
||||
bool Empty() const { return !m_read_ptr->next.load(); }
|
||||
T& Front() const { return m_read_ptr->current; }
|
||||
template <typename Arg>
|
||||
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
|
||||
{
|
||||
return !m_read_ptr->next.load();
|
||||
}
|
||||
void Pop()
|
||||
{
|
||||
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
|
||||
{
|
||||
return m_read_ptr->current;
|
||||
}
|
||||
bool Pop(T& t)
|
||||
{
|
||||
if (Empty())
|
||||
return false;
|
||||
|
||||
template <typename Arg>
|
||||
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++;
|
||||
}
|
||||
if (NeedSize)
|
||||
m_size--;
|
||||
|
||||
void Pop()
|
||||
{
|
||||
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
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
bool Pop(T& t)
|
||||
{
|
||||
if (Empty())
|
||||
return false;
|
||||
|
||||
if (NeedSize)
|
||||
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();
|
||||
}
|
||||
// not thread-safe
|
||||
void Clear()
|
||||
{
|
||||
m_size.store(0);
|
||||
delete m_read_ptr;
|
||||
m_write_ptr = m_read_ptr = new ElementPtr();
|
||||
}
|
||||
|
||||
private:
|
||||
// stores a pointer to element
|
||||
// and a pointer to the next ElementPtr
|
||||
class ElementPtr
|
||||
{
|
||||
public:
|
||||
ElementPtr() : next(nullptr) {}
|
||||
// stores a pointer to element
|
||||
// and a pointer to the next ElementPtr
|
||||
class ElementPtr
|
||||
{
|
||||
public:
|
||||
ElementPtr() : next(nullptr) {}
|
||||
~ElementPtr()
|
||||
{
|
||||
ElementPtr* next_ptr = next.load();
|
||||
|
||||
~ElementPtr()
|
||||
{
|
||||
ElementPtr* next_ptr = next.load();
|
||||
if (next_ptr)
|
||||
delete next_ptr;
|
||||
}
|
||||
|
||||
if (next_ptr)
|
||||
delete next_ptr;
|
||||
}
|
||||
T current;
|
||||
std::atomic<ElementPtr*> next;
|
||||
};
|
||||
|
||||
T current;
|
||||
std::atomic<ElementPtr*> next;
|
||||
};
|
||||
|
||||
ElementPtr* m_write_ptr;
|
||||
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/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;
|
||||
for (const std::string& directory : directories)
|
||||
{
|
||||
File::FSTEntry top = File::ScanDirectoryTree(directory, recursive);
|
||||
std::vector<std::string> result;
|
||||
for (const std::string& directory : directories)
|
||||
{
|
||||
File::FSTEntry top = File::ScanDirectoryTree(directory, recursive);
|
||||
|
||||
std::function<void(File::FSTEntry&)> DoEntry;
|
||||
DoEntry = [&](File::FSTEntry& entry) {
|
||||
if (callback(entry))
|
||||
result.push_back(entry.physicalName);
|
||||
for (auto& child : entry.children)
|
||||
DoEntry(child);
|
||||
};
|
||||
for (auto& child : top.children)
|
||||
DoEntry(child);
|
||||
}
|
||||
// remove duplicates
|
||||
std::sort(result.begin(), result.end());
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
return result;
|
||||
std::function<void(File::FSTEntry&)> DoEntry;
|
||||
DoEntry = [&](File::FSTEntry& entry) {
|
||||
if (callback(entry))
|
||||
result.push_back(entry.physicalName);
|
||||
for (auto& child : entry.children)
|
||||
DoEntry(child);
|
||||
};
|
||||
for (auto& child : top.children)
|
||||
DoEntry(child);
|
||||
}
|
||||
// remove duplicates
|
||||
std::sort(result.begin(), result.end());
|
||||
result.erase(std::unique(result.begin(), result.end()), result.end());
|
||||
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();
|
||||
return FileSearchWithTest(directories, recursive, [&](const File::FSTEntry& entry) {
|
||||
if (accept_all)
|
||||
return true;
|
||||
std::string name = entry.virtualName;
|
||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
||||
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;
|
||||
});
|
||||
});
|
||||
bool accept_all = std::find(exts.begin(), exts.end(), "") != exts.end();
|
||||
return FileSearchWithTest(directories, recursive, [&](const File::FSTEntry& entry) {
|
||||
if (accept_all)
|
||||
return true;
|
||||
std::string name = entry.virtualName;
|
||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
||||
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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 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 entry.isDirectory;
|
||||
});
|
||||
return FileSearchWithTest(directories, true,
|
||||
[&](const File::FSTEntry& entry) { return entry.isDirectory; });
|
||||
}
|
||||
|
|
|
@ -7,5 +7,8 @@
|
|||
#include <string>
|
||||
#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> FindSubdirectories(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 = 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
|
||||
|
||||
// User directory indices for GetUserPath
|
||||
enum {
|
||||
D_USER_IDX,
|
||||
D_GCUSER_IDX,
|
||||
D_WIIROOT_IDX, // always points to User/Wii or global user-configured directory
|
||||
D_SESSION_WIIROOT_IDX, // may point to minimal temporary directory for determinism
|
||||
D_CONFIG_IDX, // global settings
|
||||
D_GAMESETTINGS_IDX, // user-specified settings which override both the global and the default settings (per game)
|
||||
D_MAPS_IDX,
|
||||
D_CACHE_IDX,
|
||||
D_SHADERCACHE_IDX,
|
||||
D_SHADERS_IDX,
|
||||
D_STATESAVES_IDX,
|
||||
D_SCREENSHOTS_IDX,
|
||||
D_HIRESTEXTURES_IDX,
|
||||
D_DUMP_IDX,
|
||||
D_DUMPFRAMES_IDX,
|
||||
D_DUMPAUDIO_IDX,
|
||||
D_DUMPTEXTURES_IDX,
|
||||
D_DUMPDSP_IDX,
|
||||
D_LOAD_IDX,
|
||||
D_LOGS_IDX,
|
||||
D_MAILLOGS_IDX,
|
||||
D_THEMES_IDX,
|
||||
D_PIPES_IDX,
|
||||
D_MEMORYWATCHER_IDX,
|
||||
F_DOLPHINCONFIG_IDX,
|
||||
F_DEBUGGERCONFIG_IDX,
|
||||
F_LOGGERCONFIG_IDX,
|
||||
F_MAINLOG_IDX,
|
||||
F_RAMDUMP_IDX,
|
||||
F_ARAMDUMP_IDX,
|
||||
F_FAKEVMEMDUMP_IDX,
|
||||
F_GCSRAM_IDX,
|
||||
F_MEMORYWATCHERLOCATIONS_IDX,
|
||||
F_MEMORYWATCHERSOCKET_IDX,
|
||||
NUM_PATH_INDICES
|
||||
enum
|
||||
{
|
||||
D_USER_IDX,
|
||||
D_GCUSER_IDX,
|
||||
D_WIIROOT_IDX, // always points to User/Wii or global user-configured directory
|
||||
D_SESSION_WIIROOT_IDX, // may point to minimal temporary directory for determinism
|
||||
D_CONFIG_IDX, // global settings
|
||||
D_GAMESETTINGS_IDX, // user-specified settings which override both the global and the default
|
||||
// settings (per game)
|
||||
D_MAPS_IDX,
|
||||
D_CACHE_IDX,
|
||||
D_SHADERCACHE_IDX,
|
||||
D_SHADERS_IDX,
|
||||
D_STATESAVES_IDX,
|
||||
D_SCREENSHOTS_IDX,
|
||||
D_HIRESTEXTURES_IDX,
|
||||
D_DUMP_IDX,
|
||||
D_DUMPFRAMES_IDX,
|
||||
D_DUMPAUDIO_IDX,
|
||||
D_DUMPTEXTURES_IDX,
|
||||
D_DUMPDSP_IDX,
|
||||
D_LOAD_IDX,
|
||||
D_LOGS_IDX,
|
||||
D_MAILLOGS_IDX,
|
||||
D_THEMES_IDX,
|
||||
D_PIPES_IDX,
|
||||
D_MEMORYWATCHER_IDX,
|
||||
F_DOLPHINCONFIG_IDX,
|
||||
F_DEBUGGERCONFIG_IDX,
|
||||
F_LOGGERCONFIG_IDX,
|
||||
F_MAINLOG_IDX,
|
||||
F_RAMDUMP_IDX,
|
||||
F_ARAMDUMP_IDX,
|
||||
F_FAKEVMEMDUMP_IDX,
|
||||
F_GCSRAM_IDX,
|
||||
F_MEMORYWATCHERLOCATIONS_IDX,
|
||||
F_MEMORYWATCHERSOCKET_IDX,
|
||||
NUM_PATH_INDICES
|
||||
};
|
||||
|
||||
namespace File
|
||||
{
|
||||
|
||||
// FileSystem tree node/
|
||||
struct FSTEntry
|
||||
{
|
||||
bool isDirectory;
|
||||
u64 size; // File length, or for directories, recursive count of children
|
||||
std::string physicalName; // Name on disk
|
||||
std::string virtualName; // Name in FST names table
|
||||
std::vector<FSTEntry> children;
|
||||
bool isDirectory;
|
||||
u64 size; // File length, or for directories, recursive count of children
|
||||
std::string physicalName; // Name on disk
|
||||
std::string virtualName; // Name in FST names table
|
||||
std::vector<FSTEntry> children;
|
||||
};
|
||||
|
||||
// Returns true if file filename exists
|
||||
|
@ -159,78 +160,80 @@ bool ReadFileToString(const std::string& filename, std::string& str);
|
|||
class IOFile : public NonCopyable
|
||||
{
|
||||
public:
|
||||
IOFile();
|
||||
IOFile(std::FILE* file);
|
||||
IOFile(const std::string& filename, const char openmode[]);
|
||||
IOFile();
|
||||
IOFile(std::FILE* file);
|
||||
IOFile(const std::string& filename, const char openmode[]);
|
||||
|
||||
~IOFile();
|
||||
~IOFile();
|
||||
|
||||
IOFile(IOFile&& other);
|
||||
IOFile& operator=(IOFile&& other);
|
||||
IOFile(IOFile&& other);
|
||||
IOFile& operator=(IOFile&& other);
|
||||
|
||||
void Swap(IOFile& other);
|
||||
void Swap(IOFile& other);
|
||||
|
||||
bool Open(const std::string& filename, const char openmode[]);
|
||||
bool Close();
|
||||
bool Open(const std::string& filename, const char openmode[]);
|
||||
bool Close();
|
||||
|
||||
template <typename T>
|
||||
bool ReadArray(T* data, size_t length, size_t* pReadBytes = nullptr)
|
||||
{
|
||||
size_t read_bytes = 0;
|
||||
if (!IsOpen() || length != (read_bytes = std::fread(data, sizeof(T), length, m_file)))
|
||||
m_good = false;
|
||||
template <typename T>
|
||||
bool ReadArray(T* data, size_t length, size_t* pReadBytes = nullptr)
|
||||
{
|
||||
size_t read_bytes = 0;
|
||||
if (!IsOpen() || length != (read_bytes = std::fread(data, sizeof(T), length, m_file)))
|
||||
m_good = false;
|
||||
|
||||
if (pReadBytes)
|
||||
*pReadBytes = read_bytes;
|
||||
if (pReadBytes)
|
||||
*pReadBytes = read_bytes;
|
||||
|
||||
return m_good;
|
||||
}
|
||||
return m_good;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool WriteArray(const T* data, size_t length)
|
||||
{
|
||||
if (!IsOpen() || length != std::fwrite(data, sizeof(T), length, m_file))
|
||||
m_good = false;
|
||||
template <typename T>
|
||||
bool WriteArray(const T* data, size_t length)
|
||||
{
|
||||
if (!IsOpen() || length != std::fwrite(data, sizeof(T), length, m_file))
|
||||
m_good = false;
|
||||
|
||||
return m_good;
|
||||
}
|
||||
return m_good;
|
||||
}
|
||||
|
||||
bool ReadBytes(void* data, size_t length)
|
||||
{
|
||||
return ReadArray(reinterpret_cast<char*>(data), length);
|
||||
}
|
||||
bool ReadBytes(void* data, size_t length)
|
||||
{
|
||||
return ReadArray(reinterpret_cast<char*>(data), length);
|
||||
}
|
||||
|
||||
bool WriteBytes(const void* data, size_t length)
|
||||
{
|
||||
return WriteArray(reinterpret_cast<const char*>(data), length);
|
||||
}
|
||||
bool WriteBytes(const void* data, size_t 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
|
||||
bool IsGood() const { return m_good; }
|
||||
operator void*() { return m_good ? m_file : nullptr; }
|
||||
std::FILE* GetHandle() { return m_file; }
|
||||
void SetHandle(std::FILE* file);
|
||||
|
||||
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:
|
||||
IOFile(IOFile&);
|
||||
IOFile& operator=(IOFile& other);
|
||||
IOFile(IOFile&);
|
||||
IOFile& operator=(IOFile& other);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -240,8 +243,8 @@ template <typename T>
|
|||
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
fstream.open(UTF8ToTStr(filename).c_str(), openmode);
|
||||
fstream.open(UTF8ToTStr(filename).c_str(), openmode);
|
||||
#else
|
||||
fstream.open(filename.c_str(), openmode);
|
||||
fstream.open(filename.c_str(), openmode);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -16,62 +16,53 @@
|
|||
template <class T, int N>
|
||||
class FixedSizeQueue
|
||||
{
|
||||
T* storage;
|
||||
int head;
|
||||
int tail;
|
||||
int count; // sacrifice 4 bytes for a simpler implementation. may optimize away in the future.
|
||||
|
||||
// Make copy constructor private for now.
|
||||
FixedSizeQueue(FixedSizeQueue& other) {}
|
||||
T* storage;
|
||||
int head;
|
||||
int tail;
|
||||
int count; // sacrifice 4 bytes for a simpler implementation. may optimize away in the future.
|
||||
|
||||
// Make copy constructor private for now.
|
||||
FixedSizeQueue(FixedSizeQueue& other) {}
|
||||
public:
|
||||
FixedSizeQueue()
|
||||
{
|
||||
storage = new T[N];
|
||||
clear();
|
||||
}
|
||||
FixedSizeQueue()
|
||||
{
|
||||
storage = new T[N];
|
||||
clear();
|
||||
}
|
||||
|
||||
~FixedSizeQueue()
|
||||
{
|
||||
delete[] storage;
|
||||
}
|
||||
~FixedSizeQueue() { delete[] storage; }
|
||||
void clear()
|
||||
{
|
||||
head = 0;
|
||||
tail = 0;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
head = 0;
|
||||
tail = 0;
|
||||
count = 0;
|
||||
}
|
||||
void push(T t)
|
||||
{
|
||||
storage[tail] = t;
|
||||
tail++;
|
||||
if (tail == N)
|
||||
tail = 0;
|
||||
count++;
|
||||
}
|
||||
|
||||
void push(T t)
|
||||
{
|
||||
storage[tail] = t;
|
||||
tail++;
|
||||
if (tail == N)
|
||||
tail = 0;
|
||||
count++;
|
||||
}
|
||||
void pop()
|
||||
{
|
||||
head++;
|
||||
if (head == N)
|
||||
head = 0;
|
||||
count--;
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
head++;
|
||||
if (head == N)
|
||||
head = 0;
|
||||
count--;
|
||||
}
|
||||
T pop_front()
|
||||
{
|
||||
const T& temp = storage[head];
|
||||
pop();
|
||||
return temp;
|
||||
}
|
||||
|
||||
T pop_front()
|
||||
{
|
||||
const T& temp = storage[head];
|
||||
pop();
|
||||
return temp;
|
||||
}
|
||||
|
||||
T& front() { return storage[head]; }
|
||||
const T& front() const { return storage[head]; }
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return count;
|
||||
}
|
||||
T& front() { return storage[head]; }
|
||||
const T& front() const { return storage[head]; }
|
||||
size_t size() const { return count; }
|
||||
};
|
||||
|
|
|
@ -19,44 +19,27 @@
|
|||
|
||||
#include <atomic>
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace Common
|
||||
{
|
||||
class Flag final
|
||||
{
|
||||
public:
|
||||
// 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
|
||||
// variable.
|
||||
explicit Flag(bool initial_value = false) : m_val(initial_value) {}
|
||||
|
||||
void Set(bool val = true)
|
||||
{
|
||||
m_val.store(val);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
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);
|
||||
}
|
||||
// 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
|
||||
// variable.
|
||||
explicit Flag(bool initial_value = false) : m_val(initial_value) {}
|
||||
void Set(bool val = true) { m_val.store(val); }
|
||||
void Clear() { 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); }
|
||||
private:
|
||||
std::atomic_bool m_val;
|
||||
std::atomic_bool m_val;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
@ -24,4 +24,3 @@
|
|||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160
|
||||
|
||||
|
|
|
@ -23,11 +23,14 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
typedef void (APIENTRYP PFNDOLRELEASESHADERCOMPILERPROC) (void);
|
||||
typedef void (APIENTRYP PFNDOLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length);
|
||||
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);
|
||||
typedef void(APIENTRYP PFNDOLRELEASESHADERCOMPILERPROC)(void);
|
||||
typedef void(APIENTRYP PFNDOLSHADERBINARYPROC)(GLsizei count, const GLuint* shaders,
|
||||
GLenum binaryformat, const void* binary,
|
||||
GLsizei length);
|
||||
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 PFNDOLDEPTHRANGEFPROC dolDepthRangef;
|
||||
|
|
|
@ -28,8 +28,9 @@
|
|||
#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB
|
||||
#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC
|
||||
|
||||
typedef void (APIENTRYP PFNDOLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name);
|
||||
typedef GLint (APIENTRYP PFNDOLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name);
|
||||
typedef void(APIENTRYP PFNDOLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber,
|
||||
GLuint index, const GLchar* name);
|
||||
typedef GLint(APIENTRYP PFNDOLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar* name);
|
||||
|
||||
extern PFNDOLBINDFRAGDATALOCATIONINDEXEDPROC dolBindFragDataLocationIndexed;
|
||||
extern PFNDOLGETFRAGDATAINDEXPROC dolGetFragDataIndex;
|
||||
|
|
|
@ -33,8 +33,10 @@
|
|||
#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F
|
||||
#define GL_BUFFER_STORAGE_FLAGS 0x8220
|
||||
|
||||
typedef void (APIENTRYP PFNDOLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags);
|
||||
typedef void (APIENTRYP PFNDOLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags);
|
||||
typedef void(APIENTRYP PFNDOLBUFFERSTORAGEPROC)(GLenum target, GLsizeiptr size, const void* data,
|
||||
GLbitfield flags);
|
||||
typedef void(APIENTRYP PFNDOLNAMEDBUFFERSTORAGEEXTPROC)(GLuint buffer, GLsizeiptr size,
|
||||
const void* data, GLbitfield flags);
|
||||
|
||||
extern PFNDOLBUFFERSTORAGEPROC dolBufferStorage;
|
||||
|
||||
|
|
|
@ -23,12 +23,12 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
#define GL_NEGATIVE_ONE_TO_ONE 0x935E
|
||||
#define GL_ZERO_TO_ONE 0x935F
|
||||
#define GL_CLIP_ORIGIN 0x935C
|
||||
#define GL_CLIP_DEPTH_MODE 0x935D
|
||||
#define GL_NEGATIVE_ONE_TO_ONE 0x935E
|
||||
#define GL_ZERO_TO_ONE 0x935F
|
||||
#define GL_CLIP_ORIGIN 0x935C
|
||||
#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;
|
||||
|
||||
|
|
|
@ -23,7 +23,12 @@
|
|||
|
||||
#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;
|
||||
|
||||
|
|
|
@ -23,34 +23,44 @@
|
|||
|
||||
#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);
|
||||
#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242
|
||||
typedef void(APIENTRYP GLDEBUGPROCARB)(GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||
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_CALLBACK_FUNCTION_ARB 0x8244
|
||||
#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245
|
||||
#define GL_DEBUG_SOURCE_API_ARB 0x8246
|
||||
#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244
|
||||
#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245
|
||||
#define GL_DEBUG_SOURCE_API_ARB 0x8246
|
||||
#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247
|
||||
#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248
|
||||
#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249
|
||||
#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A
|
||||
#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B
|
||||
#define GL_DEBUG_TYPE_ERROR_ARB 0x824C
|
||||
#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249
|
||||
#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A
|
||||
#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B
|
||||
#define GL_DEBUG_TYPE_ERROR_ARB 0x824C
|
||||
#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D
|
||||
#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E
|
||||
#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F
|
||||
#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250
|
||||
#define GL_DEBUG_TYPE_OTHER_ARB 0x8251
|
||||
#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143
|
||||
#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144
|
||||
#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145
|
||||
#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146
|
||||
#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147
|
||||
#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148
|
||||
#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F
|
||||
#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250
|
||||
#define GL_DEBUG_TYPE_OTHER_ARB 0x8251
|
||||
#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143
|
||||
#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144
|
||||
#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145
|
||||
#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146
|
||||
#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147
|
||||
#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 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);
|
||||
typedef void(APIENTRYP PFNDOLDEBUGMESSAGECONTROLARBPROC)(GLenum source, GLenum type,
|
||||
GLenum severity, GLsizei count,
|
||||
const GLuint* ids, GLboolean enabled);
|
||||
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 PFNDOLDEBUGMESSAGECONTROLARBPROC dolDebugMessageControlARB;
|
||||
|
|
|
@ -23,10 +23,21 @@
|
|||
|
||||
#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 PFNDOLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, 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);
|
||||
typedef void(APIENTRYP PFNDOLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type,
|
||||
const void* indices, GLint basevertex);
|
||||
typedef void(APIENTRYP PFNDOLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end,
|
||||
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 PFNDOLDRAWELEMENTSINSTANCEDBASEVERTEXPROC dolDrawElementsInstancedBaseVertex;
|
||||
|
|
|
@ -23,26 +23,44 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
typedef void (APIENTRYP PFNDOLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer);
|
||||
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 GLenum (APIENTRYP PFNDOLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target);
|
||||
typedef void (APIENTRYP PFNDOLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers);
|
||||
typedef void (APIENTRYP PFNDOLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers);
|
||||
typedef void (APIENTRYP PFNDOLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
|
||||
typedef void (APIENTRYP PFNDOLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
|
||||
typedef void (APIENTRYP PFNDOLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
|
||||
typedef void (APIENTRYP PFNDOLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, 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);
|
||||
typedef void(APIENTRYP PFNDOLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer);
|
||||
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 GLenum(APIENTRYP PFNDOLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target);
|
||||
typedef void(APIENTRYP PFNDOLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint* framebuffers);
|
||||
typedef void(APIENTRYP PFNDOLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint* renderbuffers);
|
||||
typedef void(APIENTRYP PFNDOLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment,
|
||||
GLenum renderbuffertarget,
|
||||
GLuint renderbuffer);
|
||||
typedef void(APIENTRYP PFNDOLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment,
|
||||
GLenum textarget, GLuint texture,
|
||||
GLint level);
|
||||
typedef void(APIENTRYP PFNDOLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment,
|
||||
GLenum textarget, GLuint texture,
|
||||
GLint level);
|
||||
typedef void(APIENTRYP PFNDOLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment,
|
||||
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 PFNDOLBINDRENDERBUFFERPROC dolBindRenderbuffer;
|
||||
|
|
|
@ -28,9 +28,11 @@
|
|||
#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
|
||||
#define GL_PROGRAM_BINARY_FORMATS 0x87FF
|
||||
|
||||
typedef void (APIENTRYP PFNDOLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary);
|
||||
typedef void (APIENTRYP PFNDOLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length);
|
||||
typedef void (APIENTRYP PFNDOLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value);
|
||||
typedef void(APIENTRYP PFNDOLGETPROGRAMBINARYPROC)(GLuint program, GLsizei bufSize, GLsizei* length,
|
||||
GLenum* binaryFormat, void* binary);
|
||||
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 PFNDOLPROGRAMBINARYPROC dolProgramBinary;
|
||||
|
|
|
@ -23,8 +23,10 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
typedef void *(APIENTRYP PFNDOLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
|
||||
typedef void (APIENTRYP PFNDOLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length);
|
||||
typedef void*(APIENTRYP PFNDOLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length,
|
||||
GLbitfield access);
|
||||
typedef void(APIENTRYP PFNDOLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset,
|
||||
GLsizeiptr length);
|
||||
|
||||
extern PFNDOLFLUSHMAPPEDBUFFERRANGEPROC dolFlushMappedBufferRange;
|
||||
extern PFNDOLMAPBUFFERRANGEPROC dolMapBufferRange;
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
typedef void (APIENTRYP PFNDOLMINSAMPLESHADINGARBPROC) (GLfloat value);
|
||||
typedef void(APIENTRYP PFNDOLMINSAMPLESHADINGARBPROC)(GLfloat value);
|
||||
|
||||
extern PFNDOLMINSAMPLESHADINGARBPROC dolMinSampleShading;
|
||||
|
||||
|
|
|
@ -23,20 +23,28 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
typedef void (APIENTRYP PFNDOLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers);
|
||||
typedef void (APIENTRYP PFNDOLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers);
|
||||
typedef GLboolean (APIENTRYP PFNDOLISSAMPLERPROC) (GLuint sampler);
|
||||
typedef void (APIENTRYP PFNDOLBINDSAMPLERPROC) (GLuint unit, GLuint sampler);
|
||||
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param);
|
||||
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param);
|
||||
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param);
|
||||
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param);
|
||||
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param);
|
||||
typedef void (APIENTRYP PFNDOLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, 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);
|
||||
typedef void(APIENTRYP PFNDOLGENSAMPLERSPROC)(GLsizei count, GLuint* samplers);
|
||||
typedef void(APIENTRYP PFNDOLDELETESAMPLERSPROC)(GLsizei count, const GLuint* samplers);
|
||||
typedef GLboolean(APIENTRYP PFNDOLISSAMPLERPROC)(GLuint sampler);
|
||||
typedef void(APIENTRYP PFNDOLBINDSAMPLERPROC)(GLuint unit, GLuint sampler);
|
||||
typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param);
|
||||
typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname,
|
||||
const GLint* param);
|
||||
typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param);
|
||||
typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname,
|
||||
const GLfloat* param);
|
||||
typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname,
|
||||
const GLint* param);
|
||||
typedef void(APIENTRYP PFNDOLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname,
|
||||
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 PFNDOLDELETESAMPLERSPROC dolDeleteSamplers;
|
||||
|
|
|
@ -40,7 +40,9 @@
|
|||
#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE
|
||||
#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;
|
||||
|
||||
|
|
|
@ -23,14 +23,15 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
typedef GLsync (APIENTRYP PFNDOLFENCESYNCPROC) (GLenum condition, GLbitfield flags);
|
||||
typedef GLboolean (APIENTRYP PFNDOLISSYNCPROC) (GLsync sync);
|
||||
typedef void (APIENTRYP PFNDOLDELETESYNCPROC) (GLsync sync);
|
||||
typedef GLenum (APIENTRYP PFNDOLCLIENTWAITSYNCPROC) (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 PFNDOLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values);
|
||||
typedef void (APIENTRYP PFNDOLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data);
|
||||
typedef GLsync(APIENTRYP PFNDOLFENCESYNCPROC)(GLenum condition, GLbitfield flags);
|
||||
typedef GLboolean(APIENTRYP PFNDOLISSYNCPROC)(GLsync sync);
|
||||
typedef void(APIENTRYP PFNDOLDELETESYNCPROC)(GLsync sync);
|
||||
typedef GLenum(APIENTRYP PFNDOLCLIENTWAITSYNCPROC)(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 PFNDOLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei bufSize,
|
||||
GLsizei* length, GLint* values);
|
||||
typedef void(APIENTRYP PFNDOLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64* data);
|
||||
|
||||
extern PFNDOLCLIENTWAITSYNCPROC dolClientWaitSync;
|
||||
extern PFNDOLDELETESYNCPROC dolDeleteSync;
|
||||
|
|
|
@ -23,10 +23,16 @@
|
|||
|
||||
#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 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);
|
||||
typedef void(APIENTRYP PFNDOLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples,
|
||||
GLenum internalformat, GLsizei width,
|
||||
GLsizei height,
|
||||
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 PFNDOLTEXIMAGE3DMULTISAMPLEPROC dolTexImage3DMultisample;
|
||||
|
|
|
@ -23,8 +23,14 @@
|
|||
|
||||
#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 PFNDOLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);
|
||||
typedef void(APIENTRYP PFNDOLTEXSTORAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples,
|
||||
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 PFNDOLTEXSTORAGE3DMULTISAMPLEPROC dolTexStorage3DMultisample;
|
||||
|
|
|
@ -23,16 +23,29 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
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 PFNDOLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer);
|
||||
typedef void (APIENTRYP PFNDOLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices);
|
||||
typedef void (APIENTRYP PFNDOLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, 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);
|
||||
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 PFNDOLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer);
|
||||
typedef void(APIENTRYP PFNDOLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount,
|
||||
const GLchar* const* uniformNames,
|
||||
GLuint* uniformIndices);
|
||||
typedef void(APIENTRYP PFNDOLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount,
|
||||
const GLuint* uniformIndices, GLenum pname,
|
||||
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 PFNDOLBINDBUFFERRANGEPROC dolBindBufferRange;
|
||||
|
|
|
@ -23,10 +23,10 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
typedef void (APIENTRYP PFNDOLBINDVERTEXARRAYPROC) (GLuint array);
|
||||
typedef void (APIENTRYP PFNDOLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays);
|
||||
typedef void (APIENTRYP PFNDOLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);
|
||||
typedef GLboolean (APIENTRYP PFNDOLISVERTEXARRAYPROC) (GLuint array);
|
||||
typedef void(APIENTRYP PFNDOLBINDVERTEXARRAYPROC)(GLuint array);
|
||||
typedef void(APIENTRYP PFNDOLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint* arrays);
|
||||
typedef void(APIENTRYP PFNDOLGENVERTEXARRAYSPROC)(GLsizei n, GLuint* arrays);
|
||||
typedef GLboolean(APIENTRYP PFNDOLISVERTEXARRAYPROC)(GLuint array);
|
||||
|
||||
extern PFNDOLBINDVERTEXARRAYPROC dolBindVertexArray;
|
||||
extern PFNDOLDELETEVERTEXARRAYSPROC dolDeleteVertexArrays;
|
||||
|
|
|
@ -23,16 +23,18 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
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 PFNDOLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v);
|
||||
typedef void (APIENTRYP PFNDOLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v);
|
||||
typedef void (APIENTRYP PFNDOLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height);
|
||||
typedef void (APIENTRYP PFNDOLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v);
|
||||
typedef void (APIENTRYP PFNDOLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v);
|
||||
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);
|
||||
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 PFNDOLVIEWPORTINDEXEDFVPROC)(GLuint index, const GLfloat* v);
|
||||
typedef void(APIENTRYP PFNDOLSCISSORARRAYVPROC)(GLuint first, GLsizei count, const GLint* v);
|
||||
typedef void(APIENTRYP PFNDOLSCISSORINDEXEDPROC)(GLuint index, GLint left, GLint bottom,
|
||||
GLsizei width, GLsizei height);
|
||||
typedef void(APIENTRYP PFNDOLSCISSORINDEXEDVPROC)(GLuint index, const GLint* v);
|
||||
typedef void(APIENTRYP PFNDOLDEPTHRANGEARRAYVPROC)(GLuint first, GLsizei count, const GLdouble* v);
|
||||
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 PFNDOLDEPTHRANGEINDEXEDPROC dolDepthRangeIndexed;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,14 +7,14 @@
|
|||
#include "Common/CommonTypes.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_buffer_storage.h"
|
||||
#include "Common/GL/GLExtensions/ARB_clip_control.h"
|
||||
#include "Common/GL/GLExtensions/ARB_copy_image.h"
|
||||
#include "Common/GL/GLExtensions/ARB_debug_output.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_get_program_binary.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_viewport_array.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_2.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_4.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
|
||||
{
|
||||
// Initializes the interface
|
||||
bool Init();
|
||||
// Initializes the interface
|
||||
bool Init();
|
||||
|
||||
// Function for checking if the hardware supports an extension
|
||||
// example: if (GLExtensions::Supports("GL_ARB_multi_map"))
|
||||
bool Supports(const std::string& name);
|
||||
// Function for checking if the hardware supports an extension
|
||||
// example: if (GLExtensions::Supports("GL_ARB_multi_map"))
|
||||
bool Supports(const std::string& name);
|
||||
|
||||
// Returns OpenGL version in format 430
|
||||
u32 Version();
|
||||
// Returns OpenGL version in format 430
|
||||
u32 Version();
|
||||
}
|
||||
|
|
|
@ -23,5 +23,5 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
#define GL_OCCLUSION_TEST_HP 0x8165
|
||||
#define GL_OCCLUSION_TEST_RESULT_HP 0x8166
|
||||
#define GL_OCCLUSION_TEST_HP 0x8165
|
||||
#define GL_OCCLUSION_TEST_RESULT_HP 0x8166
|
||||
|
|
|
@ -64,18 +64,31 @@
|
|||
#define GL_DEBUG_SEVERITY_LOW 0x9148
|
||||
#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 PFNDOLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint* ids, GLboolean enabled);
|
||||
typedef void (APIENTRYP PFNDOLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* buf);
|
||||
typedef GLuint (APIENTRYP PFNDOLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufsize, 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);
|
||||
typedef void(APIENTRYP PFNDOLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback,
|
||||
const GLvoid* userParam);
|
||||
typedef void(APIENTRYP PFNDOLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity,
|
||||
GLsizei count, const GLuint* ids,
|
||||
GLboolean enabled);
|
||||
typedef void(APIENTRYP PFNDOLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id,
|
||||
GLenum severity, GLsizei length,
|
||||
const GLchar* buf);
|
||||
typedef GLuint(APIENTRYP PFNDOLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufsize,
|
||||
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 PFNDOLDEBUGMESSAGECONTROLPROC dolDebugMessageControl;
|
||||
|
|
|
@ -23,18 +23,18 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
#define GL_PIXEL_COUNTER_BITS_NV 0x8864
|
||||
#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865
|
||||
#define GL_PIXEL_COUNT_NV 0x8866
|
||||
#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867
|
||||
#define GL_PIXEL_COUNTER_BITS_NV 0x8864
|
||||
#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865
|
||||
#define GL_PIXEL_COUNT_NV 0x8866
|
||||
#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867
|
||||
|
||||
typedef void (APIENTRYP PFNDOLGENOCCLUSIONQUERIESNVPROC) (GLsizei n, GLuint *ids);
|
||||
typedef void (APIENTRYP PFNDOLDELETEOCCLUSIONQUERIESNVPROC) (GLsizei n, const GLuint *ids);
|
||||
typedef GLboolean (APIENTRYP PFNDOLISOCCLUSIONQUERYNVPROC) (GLuint id);
|
||||
typedef void (APIENTRYP PFNDOLBEGINOCCLUSIONQUERYNVPROC) (GLuint id);
|
||||
typedef void (APIENTRYP PFNDOLENDOCCLUSIONQUERYNVPROC) (void);
|
||||
typedef void (APIENTRYP PFNDOLGETOCCLUSIONQUERYIVNVPROC) (GLuint id, GLenum pname, GLint *params);
|
||||
typedef void (APIENTRYP PFNDOLGETOCCLUSIONQUERYUIVNVPROC) (GLuint id, GLenum pname, GLuint *params);
|
||||
typedef void(APIENTRYP PFNDOLGENOCCLUSIONQUERIESNVPROC)(GLsizei n, GLuint* ids);
|
||||
typedef void(APIENTRYP PFNDOLDELETEOCCLUSIONQUERIESNVPROC)(GLsizei n, const GLuint* ids);
|
||||
typedef GLboolean(APIENTRYP PFNDOLISOCCLUSIONQUERYNVPROC)(GLuint id);
|
||||
typedef void(APIENTRYP PFNDOLBEGINOCCLUSIONQUERYNVPROC)(GLuint id);
|
||||
typedef void(APIENTRYP PFNDOLENDOCCLUSIONQUERYNVPROC)(void);
|
||||
typedef void(APIENTRYP PFNDOLGETOCCLUSIONQUERYIVNVPROC)(GLuint id, GLenum pname, GLint* params);
|
||||
typedef void(APIENTRYP PFNDOLGETOCCLUSIONQUERYUIVNVPROC)(GLuint id, GLenum pname, GLuint* params);
|
||||
|
||||
extern PFNDOLGENOCCLUSIONQUERIESNVPROC dolGenOcclusionQueriesNV;
|
||||
extern PFNDOLDELETEOCCLUSIONQUERIESNVPROC dolDeleteOcclusionQueriesNV;
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
#define GL_PRIMITIVE_RESTART_NV 0x8558
|
||||
#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559
|
||||
|
||||
typedef void (APIENTRYP PFNDOLPRIMITIVERESTARTINDEXNVPROC) (GLuint index);
|
||||
typedef void (APIENTRYP PFNDOLPRIMITIVERESTARTNVPROC) (void);
|
||||
typedef void(APIENTRYP PFNDOLPRIMITIVERESTARTINDEXNVPROC)(GLuint index);
|
||||
typedef void(APIENTRYP PFNDOLPRIMITIVERESTARTNVPROC)(void);
|
||||
|
||||
extern PFNDOLPRIMITIVERESTARTINDEXNVPROC dolPrimitiveRestartIndexNV;
|
||||
extern PFNDOLPRIMITIVERESTARTNVPROC dolPrimitiveRestartNV;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,52 +23,62 @@
|
|||
|
||||
#include "Common/GL/GLExtensions/gl_common.h"
|
||||
|
||||
#define GL_RESCALE_NORMAL 0x803A
|
||||
#define GL_CLAMP_TO_EDGE 0x812F
|
||||
#define GL_MAX_ELEMENTS_VERTICES 0x80E8
|
||||
#define GL_MAX_ELEMENTS_INDICES 0x80E9
|
||||
#define GL_BGR 0x80E0
|
||||
#define GL_BGRA 0x80E1
|
||||
#define GL_UNSIGNED_BYTE_3_3_2 0x8032
|
||||
#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
|
||||
#define GL_UNSIGNED_SHORT_5_6_5 0x8363
|
||||
#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_REV 0x8365
|
||||
#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
|
||||
#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_REV 0x8367
|
||||
#define GL_UNSIGNED_INT_10_10_10_2 0x8036
|
||||
#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
|
||||
#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8
|
||||
#define GL_SINGLE_COLOR 0x81F9
|
||||
#define GL_SEPARATE_SPECULAR_COLOR 0x81FA
|
||||
#define GL_TEXTURE_MIN_LOD 0x813A
|
||||
#define GL_TEXTURE_MAX_LOD 0x813B
|
||||
#define GL_TEXTURE_BASE_LEVEL 0x813C
|
||||
#define GL_TEXTURE_MAX_LEVEL 0x813D
|
||||
#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12
|
||||
#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13
|
||||
#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22
|
||||
#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23
|
||||
#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
|
||||
#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
|
||||
#define GL_PACK_SKIP_IMAGES 0x806B
|
||||
#define GL_PACK_IMAGE_HEIGHT 0x806C
|
||||
#define GL_UNPACK_SKIP_IMAGES 0x806D
|
||||
#define GL_UNPACK_IMAGE_HEIGHT 0x806E
|
||||
#define GL_TEXTURE_3D 0x806F
|
||||
#define GL_PROXY_TEXTURE_3D 0x8070
|
||||
#define GL_TEXTURE_DEPTH 0x8071
|
||||
#define GL_TEXTURE_WRAP_R 0x8072
|
||||
#define GL_MAX_3D_TEXTURE_SIZE 0x8073
|
||||
#define GL_TEXTURE_BINDING_3D 0x806A
|
||||
#define GL_RESCALE_NORMAL 0x803A
|
||||
#define GL_CLAMP_TO_EDGE 0x812F
|
||||
#define GL_MAX_ELEMENTS_VERTICES 0x80E8
|
||||
#define GL_MAX_ELEMENTS_INDICES 0x80E9
|
||||
#define GL_BGR 0x80E0
|
||||
#define GL_BGRA 0x80E1
|
||||
#define GL_UNSIGNED_BYTE_3_3_2 0x8032
|
||||
#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
|
||||
#define GL_UNSIGNED_SHORT_5_6_5 0x8363
|
||||
#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_REV 0x8365
|
||||
#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
|
||||
#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_REV 0x8367
|
||||
#define GL_UNSIGNED_INT_10_10_10_2 0x8036
|
||||
#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
|
||||
#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8
|
||||
#define GL_SINGLE_COLOR 0x81F9
|
||||
#define GL_SEPARATE_SPECULAR_COLOR 0x81FA
|
||||
#define GL_TEXTURE_MIN_LOD 0x813A
|
||||
#define GL_TEXTURE_MAX_LOD 0x813B
|
||||
#define GL_TEXTURE_BASE_LEVEL 0x813C
|
||||
#define GL_TEXTURE_MAX_LEVEL 0x813D
|
||||
#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12
|
||||
#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13
|
||||
#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22
|
||||
#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23
|
||||
#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
|
||||
#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
|
||||
#define GL_PACK_SKIP_IMAGES 0x806B
|
||||
#define GL_PACK_IMAGE_HEIGHT 0x806C
|
||||
#define GL_UNPACK_SKIP_IMAGES 0x806D
|
||||
#define GL_UNPACK_IMAGE_HEIGHT 0x806E
|
||||
#define GL_TEXTURE_3D 0x806F
|
||||
#define GL_PROXY_TEXTURE_3D 0x8070
|
||||
#define GL_TEXTURE_DEPTH 0x8071
|
||||
#define GL_TEXTURE_WRAP_R 0x8072
|
||||
#define GL_MAX_3D_TEXTURE_SIZE 0x8073
|
||||
#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 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);
|
||||
typedef void(APIENTRYP PFNDOLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end,
|
||||
GLsizei count, GLenum type,
|
||||
const GLvoid* indices);
|
||||
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 PFNDOLDRAWRANGEELEMENTSPROC dolDrawRangeElements;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue