Skip to content

Instantly share code, notes, and snippets.

@kapzyl
Forked from pervognsen/mu.cpp
Created April 21, 2020 20:36
Show Gist options
  • Save kapzyl/400e9339c922f01397c189e3d5ad8731 to your computer and use it in GitHub Desktop.
Save kapzyl/400e9339c922f01397c189e3d5ad8731 to your computer and use it in GitHub Desktop.
Mu as of the second stream
#include "mu.h"
#define _CRT_SECURE_NO_WARNINGS
#include <malloc.h>
#define _USE_MATH_DEFINES
#include <math.h>
#define _NO_CRT_STDIO_INLINE
#include <stdio.h>
#include <stdarg.h>
#define NO_STRICT
#include <windows.h>
#include <wincodec.h>
#include <xinput.h>
#include <xaudio2.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <audiosessiontypes.h>
#include <wincodec.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
#endif
#ifdef _DEBUG
#define Assert(x) if (!(x)) { MessageBoxA(0, x, "Assertion Failed", MB_OK); __debugbreak(); }
#else
#define Assert(x)
#endif
#include "mu.h"
MU_EXTERN_BEGIN
void Mu_ExitWithError(Mu *mu) {
MessageBoxA(0, mu->error, "Mu Error", MB_OK);
ExitProcess(1);
}
void Mu_UpdateDigitalButton(Mu_DigitalButton *button, Mu_Bool down) {
Mu_Bool was_down = button->down;
button->down = down;
button->pressed = !was_down && down;
button->released = was_down && !down;
}
void Mu_UpdateAnalogButton(Mu_AnalogButton *button, float value) {
button->value = value;
Mu_Bool was_down = button->down;
button->down = (value >= button->threshold);
button->pressed = !was_down && button->down;
button->released = was_down && !button->down;
}
void Mu_UpdateStick(Mu_Stick *stick, float x, float y) {
if (fabs(x) <= stick->threshold) {
x = 0.0f;
}
stick->x = x;
if (fabs(y) <= stick->threshold) {
y = 0.0f;
}
stick->y = y;
}
// Mu_Window
static LRESULT CALLBACK Mu_Window_Proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) {
LRESULT result = 0;
Mu *mu = (Mu *)GetWindowLongPtrA(window, GWLP_USERDATA);
switch (message) {
case WM_SIZE:
mu->window.resized = MU_TRUE;
break;
case WM_INPUT: {
UINT size;
GetRawInputData((HRAWINPUT)lparam, RID_INPUT, 0, &size, sizeof(RAWINPUTHEADER));
void *buffer = _alloca(size);
if (GetRawInputData((HRAWINPUT)lparam, RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER)) == size) {
RAWINPUT *raw_input = (RAWINPUT *)buffer;
if (raw_input->header.dwType == RIM_TYPEMOUSE && raw_input->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {
mu->mouse.delta_position.x += raw_input->data.mouse.lLastX;
mu->mouse.delta_position.y += raw_input->data.mouse.lLastY;
USHORT button_flags = raw_input->data.mouse.usButtonFlags;
Mu_Bool left_button_down = mu->mouse.left_button.down;
if (button_flags & RI_MOUSE_LEFT_BUTTON_DOWN) {
left_button_down = MU_TRUE;
}
if (button_flags & RI_MOUSE_LEFT_BUTTON_UP) {
left_button_down = MU_FALSE;
}
Mu_UpdateDigitalButton(&mu->mouse.left_button, left_button_down);
Mu_Bool right_button_down = mu->mouse.right_button.down;
if (button_flags & RI_MOUSE_RIGHT_BUTTON_DOWN) {
right_button_down = MU_TRUE;
}
if (button_flags & RI_MOUSE_RIGHT_BUTTON_UP) {
right_button_down = MU_FALSE;
}
Mu_UpdateDigitalButton(&mu->mouse.right_button, right_button_down);
if (button_flags & RI_MOUSE_WHEEL) {
mu->mouse.delta_wheel += ((SHORT)raw_input->data.mouse.usButtonData) / WHEEL_DELTA;
}
}
}
result = DefWindowProcA(window, message, wparam, lparam);
break;
}
case WM_CHAR: {
WCHAR utf16_character = (WCHAR)wparam;
char ascii_character;
uint32_t ascii_length = WideCharToMultiByte(CP_ACP, 0, &utf16_character, 1, &ascii_character, 1, 0, 0);
if (ascii_length == 1 && mu->text_length + 1 < sizeof(mu->text) - 1) {
mu->text[mu->text_length] = ascii_character;
mu->text[mu->text_length + 1] = 0;
mu->text_length += ascii_length;
}
break;
}
case WM_DESTROY:
mu->quit = MU_TRUE;
break;
case WM_TIMER:
SwitchToFiber(mu->win32.main_fiber);
break;
default:
result = DefWindowProcA(window, message, wparam, lparam);
}
return result;
}
static void CALLBACK Mu_Window_MessageFiberProc(Mu *mu) {
SetTimer(mu->win32.window, 1, 1, 0);
for (;;) {
MSG message;
while (PeekMessage(&message, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
SwitchToFiber(mu->win32.main_fiber);
}
}
Mu_Bool Mu_Window_Initialize(Mu *mu) {
if (!mu->window.title) {
mu->window.title = "Mu";
}
int window_x;
if (mu->window.position.x) {
window_x = mu->window.position.x;
} else {
window_x = CW_USEDEFAULT;
}
int window_y;
if (mu->window.position.y) {
window_y = mu->window.position.y;
} else {
window_y = CW_USEDEFAULT;
}
int window_width;
if (mu->window.size.x) {
window_width = mu->window.size.x;
} else {
window_width = CW_USEDEFAULT;
}
int window_height;
if (mu->window.size.y) {
window_height = mu->window.size.y;
} else {
window_height = CW_USEDEFAULT;
}
mu->win32.main_fiber = ConvertThreadToFiber(0);
Assert(mu->win32.main_fiber);
mu->win32.message_fiber = CreateFiber(0, (PFIBER_START_ROUTINE)Mu_Window_MessageFiberProc, mu);
Assert(mu->win32.message_fiber);
if (window_height != CW_USEDEFAULT && window_width != CW_USEDEFAULT) {
RECT window_rectangle;
window_rectangle.left = 0;
window_rectangle.right = window_width;
window_rectangle.top = 0;
window_rectangle.bottom = window_height;
if (AdjustWindowRect(&window_rectangle, WS_OVERLAPPEDWINDOW, 0)) {
window_width = window_rectangle.right - window_rectangle.left;
window_height = window_rectangle.bottom - window_rectangle.top;
}
}
WNDCLASSA window_class = {0};
window_class.lpfnWndProc = Mu_Window_Proc;
window_class.lpszClassName = "mu";
window_class.style = CS_HREDRAW | CS_VREDRAW;
if (RegisterClassA(&window_class) == 0) {
mu->error = "Failed to initialize window class.";
return MU_FALSE;
}
mu->win32.window = CreateWindowA("mu", mu->window.title, WS_OVERLAPPEDWINDOW, window_x, window_y, window_width, window_height, 0, 0, 0, 0);
if (!mu->win32.window) {
mu->error = "Failed to create window.";
return MU_FALSE;
}
SetWindowLongPtr(mu->win32.window, GWLP_USERDATA, (LONG_PTR)mu);
ShowWindow(mu->win32.window, SW_SHOW);
mu->win32.device_context = GetDC(mu->win32.window);
return MU_TRUE;
}
void Mu_Window_Pull(Mu *mu) {
mu->window.resized = MU_FALSE;
mu->text[0] = 0;
mu->text_length = 0;
mu->mouse.delta_position.x = 0;
mu->mouse.delta_position.y = 0;
mu->mouse.delta_wheel = 0;
mu->mouse.left_button.pressed = MU_FALSE;
mu->mouse.left_button.released = MU_FALSE;
mu->mouse.right_button.pressed = MU_FALSE;
mu->mouse.right_button.released = MU_FALSE;
SwitchToFiber(mu->win32.message_fiber);
mu->mouse.wheel += mu->mouse.delta_wheel;
RECT client_rectangle;
GetClientRect(mu->win32.window, &client_rectangle);
mu->window.size.x = client_rectangle.right - client_rectangle.left;
mu->window.size.y = client_rectangle.bottom - client_rectangle.top;
POINT window_position = {client_rectangle.left, client_rectangle.top};
ClientToScreen(mu->win32.window, &window_position);
mu->window.position.x = window_position.x;
mu->window.position.y = window_position.y;
}
// Mu_Time
Mu_Bool Mu_Time_Initialize(Mu *mu) {
LARGE_INTEGER large_integer;
QueryPerformanceFrequency(&large_integer);
mu->time.ticks_per_second = large_integer.QuadPart;
QueryPerformanceCounter(&large_integer);
mu->time.initial_ticks = large_integer.QuadPart;
return MU_TRUE;
}
void Mu_Time_Pull(Mu *mu) {
LARGE_INTEGER large_integer;
QueryPerformanceCounter(&large_integer);
uint64_t current_ticks = large_integer.QuadPart;
mu->time.delta_ticks = (current_ticks - mu->time.initial_ticks) - mu->time.ticks;
mu->time.ticks = current_ticks - mu->time.initial_ticks;
mu->time.delta_nanoseconds = (1000 * 1000 * 1000 * mu->time.delta_ticks) / mu->time.ticks_per_second;
mu->time.delta_microseconds = mu->time.delta_nanoseconds / 1000;
mu->time.delta_milliseconds = mu->time.delta_microseconds / 1000;
mu->time.delta_seconds = (float)mu->time.delta_ticks / (float)mu->time.ticks_per_second;
mu->time.nanoseconds = (1000 * 1000 * 1000 * mu->time.ticks) / mu->time.ticks_per_second;
mu->time.microseconds = mu->time.nanoseconds / 1000;
mu->time.milliseconds = mu->time.microseconds / 1000;
mu->time.seconds = (float)mu->time.ticks / (float)mu->time.ticks_per_second;
}
// Mu_Keyboard
void Mu_Keyboard_Pull(Mu *mu) {
BYTE keyboard_state[256] = {0};
GetKeyboardState(keyboard_state);
for (int key = 0; key < 256; key++) {
Mu_UpdateDigitalButton(mu->keys + key, keyboard_state[key] >> 7);
}
}
// Mu_Mouse
Mu_Bool Mu_Mouse_Initialize(Mu *mu) {
RAWINPUTDEVICE raw_input_device = {0};
raw_input_device.usUsagePage = 0x01;
raw_input_device.usUsage = 0x02;
raw_input_device.hwndTarget = mu->win32.window;
if (!RegisterRawInputDevices(&raw_input_device, 1, sizeof(raw_input_device))) {
mu->error = "Failed to register mouse as raw input device.";
return MU_FALSE;
}
return MU_TRUE;
}
void Mu_Mouse_Pull(Mu *mu) {
POINT mouse_position;
GetCursorPos(&mouse_position);
mouse_position.x -= mu->window.position.x;
mouse_position.y -= mu->window.position.y;
mu->mouse.position.x = mouse_position.x;
mu->mouse.position.y = mouse_position.y;
}
// Mu_Gamepad
Mu_Bool Mu_Gamepad_Initialize(Mu *mu) {
HMODULE xinput_module = LoadLibraryA("xinput1_3.dll");
if (xinput_module) {
mu->win32.xinput_get_state = (XINPUTGETSTATE)GetProcAddress(xinput_module, "XInputGetState");
}
float trigger_threshold = XINPUT_GAMEPAD_TRIGGER_THRESHOLD / 255.f;
mu->gamepad.left_trigger.threshold = trigger_threshold;
mu->gamepad.right_trigger.threshold = trigger_threshold;
mu->gamepad.left_thumb_stick.threshold = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE / 32767.f;
mu->gamepad.right_thumb_stick.threshold = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE / 32767.f;
return MU_TRUE;
}
void Mu_Gamepad_Pull(Mu *mu) {
XINPUT_STATE xinput_state = {0};
if (!mu->win32.xinput_get_state || mu->win32.xinput_get_state(0, &xinput_state) != ERROR_SUCCESS) {
mu->gamepad.connected = MU_FALSE;
return;
}
mu->gamepad.connected = MU_TRUE;
Mu_UpdateDigitalButton(&mu->gamepad.a_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_A) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.b_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_B) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.x_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_X) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.y_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.left_shoulder_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.right_shoulder_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.up_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.down_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.left_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.right_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.left_thumb_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.right_thumb_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.back_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) != 0);
Mu_UpdateDigitalButton(&mu->gamepad.start_button, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_START) != 0);
Mu_UpdateAnalogButton(&mu->gamepad.left_trigger, xinput_state.Gamepad.bLeftTrigger / 255.f);
Mu_UpdateAnalogButton(&mu->gamepad.right_trigger, xinput_state.Gamepad.bRightTrigger / 255.f);
#define CONVERT(x) (2.0f * (((x + 32768) / 65535.f) - 0.5f))
Mu_UpdateStick(&mu->gamepad.left_thumb_stick, CONVERT(xinput_state.Gamepad.sThumbLX), CONVERT(xinput_state.Gamepad.sThumbLY));
Mu_UpdateStick(&mu->gamepad.right_thumb_stick, CONVERT(xinput_state.Gamepad.sThumbRX), CONVERT(xinput_state.Gamepad.sThumbRY));
#undef CONVERT
}
// Mu_Audio
static void Mu_Audio_DefaultCallback(Mu_AudioBuffer *buffer) {
FillMemory(buffer->samples, sizeof(int16_t) * buffer->samples_count, 0);
}
DWORD Mu_Audio_ThreadProc(void *parameter) {
DWORD result = -1;
Mu *mu = (Mu *)parameter;
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
HANDLE buffer_ready_event = CreateEvent(0, 0, 0, 0);
if (mu->win32.audio_client->SetEventHandle(buffer_ready_event) < 0) {
goto done;
}
uint32_t buffer_frame_count;
if (mu->win32.audio_client->GetBufferSize(&buffer_frame_count) < 0) {
goto done;
}
uint32_t buffer_sample_count = buffer_frame_count * mu->audio.format.channels;
if (mu->win32.audio_client->Start() < 0) {
goto done;
}
for (;;) {
if (WaitForSingleObject(buffer_ready_event, INFINITE) != WAIT_OBJECT_0) {
goto done;
}
uint32_t padding_frame_count;
if (mu->win32.audio_client->GetCurrentPadding(&padding_frame_count) < 0) {
goto done;
}
uint32_t fill_frame_count = buffer_frame_count - padding_frame_count;
Mu_AudioBuffer buffer;
if (mu->win32.audio_render_client->GetBuffer(fill_frame_count, (BYTE **)&buffer.samples) < 0) {
goto done;
}
buffer.format = mu->audio.format;
buffer.samples_count = fill_frame_count * buffer.format.channels;
mu->audio.callback(&buffer);
if (mu->win32.audio_render_client->ReleaseBuffer(fill_frame_count, 0) < 0) {
goto done;
}
}
result = 0;
done:
mu->win32.audio_client->Stop();
return result;
}
static WAVEFORMATEX win32_audio_format = {WAVE_FORMAT_PCM, 1, 44100, 44100 * 2, 2, 16, 0};
static Mu_AudioFormat mu_audio_format = {44100, 1, 2};
Mu_Bool Mu_Audio_Initialize(Mu *mu) {
Mu_Bool result = MU_FALSE;
IMMDeviceEnumerator *device_enumerator = 0;
IMMDevice *audio_device = 0;
if (!mu->audio.callback)
{
mu->audio.callback = Mu_Audio_DefaultCallback;
}
CoInitializeEx(0, COINITBASE_MULTITHREADED);
if (CoCreateInstance(__uuidof(MMDeviceEnumerator), 0, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&device_enumerator)) < 0) {
goto done;
}
if (device_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &audio_device) < 0) {
goto done;
}
IAudioClient *audio_client;
if (audio_device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, 0, (void **)&audio_client) < 0) {
goto done;
}
REFERENCE_TIME audio_buffer_duration = 40 * 1000 * 10; // 40 milliseconds
DWORD audio_client_flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_RATEADJUST | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
if (audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, audio_client_flags, audio_buffer_duration, 0, &win32_audio_format, 0) < 0) {
goto done;
}
IAudioRenderClient *audio_render_client;
if (audio_client->GetService(IID_PPV_ARGS(&audio_render_client)) < 0) {
goto done;
}
mu->audio.format = mu_audio_format;
mu->win32.audio_client = audio_client;
mu->win32.audio_render_client = audio_render_client;
CreateThread(0, 0, Mu_Audio_ThreadProc, mu, 0, 0);
device_enumerator->Release();
result = MU_TRUE;
done:
if (device_enumerator) {
device_enumerator->Release();
}
if (audio_device) {
audio_device->Release();
}
return result;
}
// Mu_OpenGL
void Mu_OpenGL_Push(Mu *mu) {
SwapBuffers(mu->win32.device_context);
}
Mu_Bool Mu_OpenGL_Initialize(Mu *mu) {
PIXELFORMATDESCRIPTOR pixel_format_descriptor;
pixel_format_descriptor.nSize = sizeof(pixel_format_descriptor);
pixel_format_descriptor.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
pixel_format_descriptor.iPixelType = PFD_TYPE_RGBA;
pixel_format_descriptor.cColorBits = 24;
pixel_format_descriptor.cAlphaBits = 0;
pixel_format_descriptor.cDepthBits = 24;
pixel_format_descriptor.cStencilBits = 8;
int pixel_format = ChoosePixelFormat(mu->win32.device_context, &pixel_format_descriptor);
if (!pixel_format) {
return MU_FALSE;
}
if (!DescribePixelFormat(mu->win32.device_context, pixel_format, sizeof(pixel_format_descriptor), &pixel_format_descriptor)) {
return MU_FALSE;
}
if (!SetPixelFormat(mu->win32.device_context, pixel_format, &pixel_format_descriptor)) {
return MU_FALSE;
}
mu->win32.wgl_context = wglCreateContext(mu->win32.device_context);
if (!mu->win32.wgl_context) {
return MU_FALSE;
}
wglMakeCurrent(mu->win32.device_context, mu->win32.wgl_context);
return MU_TRUE;
}
// Mu
Mu_Bool Mu_Pull(Mu *mu) {
if (!mu->initialized) {
if (!mu->error) {
mu->error = "Mu was not initialized.";
}
Mu_ExitWithError(mu);
return MU_FALSE;
}
Mu_Window_Pull(mu);
Mu_Time_Pull(mu);
Mu_Keyboard_Pull(mu);
Mu_Mouse_Pull(mu);
Mu_Gamepad_Pull(mu);
return !mu->quit;
}
void Mu_Push(Mu *mu) {
Mu_OpenGL_Push(mu);
}
Mu_Bool Mu_Initialize(Mu *mu) {
if (!Mu_Window_Initialize(mu)) {
return MU_FALSE;
}
if (!Mu_Time_Initialize(mu)) {
return MU_FALSE;
}
if (!Mu_Mouse_Initialize(mu)) {
return MU_FALSE;
}
if (!Mu_Gamepad_Initialize(mu)) {
return MU_FALSE;
}
if (!Mu_Audio_Initialize(mu)) {
return MU_FALSE;
}
if (!Mu_OpenGL_Initialize(mu)) {
return MU_FALSE;
}
mu->initialized = MU_TRUE;
Mu_Pull(mu);
return MU_TRUE;
}
// Utilities
static IWICImagingFactory *wic_factory;
static SRWLOCK wic_factory_lock = SRWLOCK_INIT;
Mu_Bool Mu_LoadImage(const char *filename, Mu_Image *image) {
Mu_Bool result = MU_FALSE;
IWICBitmapDecoder *image_decoder = 0;
IWICBitmapFrameDecode *image_frame = 0;
IWICBitmapSource *rgba_image_frame = 0;
if (!wic_factory) {
AcquireSRWLockExclusive(&wic_factory_lock);
if (!wic_factory) {
CoInitializeEx(0, COINITBASE_MULTITHREADED);
if (CoCreateInstance(CLSID_WICImagingFactory, 0, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&wic_factory)) < 0) {
ReleaseSRWLockExclusive(&wic_factory_lock);
goto done;
}
}
ReleaseSRWLockExclusive(&wic_factory_lock);
}
int wide_filename_length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, 0, 0);
WCHAR *wide_filename = (WCHAR *)_alloca(wide_filename_length * sizeof(WCHAR));
MultiByteToWideChar(CP_UTF8, 0, filename, -1, wide_filename, wide_filename_length);
if (wic_factory->CreateDecoderFromFilename(wide_filename, 0, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &image_decoder) < 0) {
goto done;
}
if (image_decoder->GetFrame(0, &image_frame) < 0) {
goto done;
}
if (WICConvertBitmapSource(GUID_WICPixelFormat32bppRGBA, image_frame, &rgba_image_frame) < 0) {
goto done;
}
uint32_t width;
uint32_t height;
image_frame->GetSize(&width, &height);
image->width = width;
image->height = height;
image->channels = 4;
uint32_t buffer_size = 4 * width * height;
image->pixels = (uint8_t *)malloc(buffer_size);
uint32_t buffer_stride = 4 * width;
if (rgba_image_frame->CopyPixels(0, buffer_stride, buffer_size, image->pixels) < 0) {
free(image->pixels);
goto done;
}
result = MU_TRUE;
done:
if (image_decoder) {
image_decoder->Release();
}
if (image_frame) {
image_frame->Release();
}
if (rgba_image_frame) {
rgba_image_frame->Release();
}
return result;
}
static bool mf_initialized;
Mu_Bool Mu_LoadAudio(const char *filename, Mu_AudioBuffer *audio) {
Mu_Bool result = MU_FALSE;
IMFSourceReader *source_reader = 0;
IMFMediaType *media_type = 0;
if (!mf_initialized) {
CoInitializeEx(0, COINIT_MULTITHREADED);
if (MFStartup(MF_VERSION, 0) < 0) {
goto done;
}
mf_initialized = true;
}
int wide_filename_length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, 0, 0);
WCHAR *wide_filename = (WCHAR *)_alloca(wide_filename_length * sizeof(WCHAR));
MultiByteToWideChar(CP_UTF8, 0, filename, -1, wide_filename, wide_filename_length);
if (MFCreateSourceReaderFromURL(wide_filename, 0, &source_reader) < 0) {
goto done;
}
if (MFCreateMediaType(&media_type) < 0) {
goto done;
}
media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
media_type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
media_type->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, win32_audio_format.nChannels);
media_type->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, win32_audio_format.nSamplesPerSec);
media_type->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, win32_audio_format.nBlockAlign);
media_type->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, win32_audio_format.nAvgBytesPerSec);
media_type->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, win32_audio_format.wBitsPerSample);
media_type->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
if (source_reader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, media_type) < 0) {
goto done;
}
size_t buffer_capacity = mu_audio_format.samples_per_second * mu_audio_format.bytes_per_sample; // 1 second of audio
size_t buffer_size = 0;
char *buffer = (char *)malloc(buffer_capacity);
for (;;) {
DWORD stream_index;
DWORD flags;
LONGLONG timestamp;
IMFSample *sample;
if (source_reader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &stream_index, &flags, &timestamp, &sample) < 0) {
free(buffer);
goto done;
}
if (flags & MF_SOURCE_READERF_ENDOFSTREAM) {
break;
}
IMFMediaBuffer *sample_buffer;
sample->ConvertToContiguousBuffer(&sample_buffer);
DWORD sample_buffer_size;
sample_buffer->GetCurrentLength(&sample_buffer_size);
size_t new_buffer_size = buffer_size + sample_buffer_size;
if (new_buffer_size > buffer_capacity) {
buffer_capacity *= 2;
if (buffer_capacity < new_buffer_size) {
buffer_capacity = new_buffer_size;
}
buffer = (char *)realloc(buffer, buffer_capacity);
}
BYTE *sample_buffer_pointer;
sample_buffer->Lock(&sample_buffer_pointer, 0, 0);
CopyMemory(buffer + buffer_size, sample_buffer_pointer, sample_buffer_size);
buffer_size += sample_buffer_size;
sample_buffer->Unlock();
sample_buffer->Release();
sample->Release();
}
audio->format = mu_audio_format;
audio->samples = (int16_t *)realloc(buffer, buffer_size);
audio->samples_count = buffer_size / mu_audio_format.bytes_per_sample;
result = MU_TRUE;
done:
if (source_reader) {
source_reader->Release();
}
if (media_type) {
media_type->Release();
}
return result;
}
MU_EXTERN_END
#pragma once
#ifdef __cplusplus
#define MU_EXTERN_BEGIN extern "C" {
#define MU_EXTERN_END }
#else
#define MU_EXTERN_BEGIN
#define MU_EXTERN_END
#endif
#include <stdint.h>
MU_EXTERN_BEGIN
enum {
MU_FALSE = 0,
MU_TRUE = 1,
MU_MAX_KEYS = 256,
MU_MAX_TEXT = 256,
MU_MAX_ERROR = 1024,
MU_CTRL = 0x11, // VK_CONTROL
MU_ALT = 0x12, // VK_MENU
MU_SHIFT = 0x10, // VK_SHIFT
MU_MAX_AUDIO_BUFFER = 2 * 1024
};
typedef uint8_t Mu_Bool;
struct Mu_Int2 {
int x;
int y;
};
struct Mu_DigitalButton {
Mu_Bool down;
Mu_Bool pressed;
Mu_Bool released;
};
struct Mu_AnalogButton {
float threshold;
float value;
Mu_Bool down;
Mu_Bool pressed;
Mu_Bool released;
};
struct Mu_Stick {
float threshold;
float x;
float y;
};
struct Mu_Gamepad {
Mu_Bool connected;
Mu_DigitalButton a_button;
Mu_DigitalButton b_button;
Mu_DigitalButton x_button;
Mu_DigitalButton y_button;
Mu_AnalogButton left_trigger;
Mu_AnalogButton right_trigger;
Mu_DigitalButton left_shoulder_button;
Mu_DigitalButton right_shoulder_button;
Mu_DigitalButton up_button;
Mu_DigitalButton down_button;
Mu_DigitalButton left_button;
Mu_DigitalButton right_button;
Mu_Stick left_thumb_stick;
Mu_Stick right_thumb_stick;
Mu_DigitalButton left_thumb_button;
Mu_DigitalButton right_thumb_button;
Mu_DigitalButton back_button;
Mu_DigitalButton start_button;
};
struct Mu_Mouse {
Mu_DigitalButton left_button;
Mu_DigitalButton right_button;
int wheel;
int delta_wheel;
Mu_Int2 position;
Mu_Int2 delta_position;
};
struct Mu_Window {
char *title;
Mu_Int2 position;
Mu_Int2 size;
Mu_Bool resized;
};
struct Mu_AudioFormat {
uint32_t samples_per_second;
uint32_t channels;
uint32_t bytes_per_sample;
};
struct Mu_AudioBuffer {
int16_t *samples;
size_t samples_count;
Mu_AudioFormat format;
};
typedef void(*Mu_AudioCallback)(Mu_AudioBuffer *buffer);
struct Mu_Audio {
Mu_AudioFormat format;
Mu_AudioCallback callback;
};
struct Mu_Time {
uint64_t delta_ticks;
uint64_t delta_nanoseconds;
uint64_t delta_microseconds;
uint64_t delta_milliseconds;
float delta_seconds;
uint64_t ticks;
uint64_t nanoseconds;
uint64_t microseconds;
uint64_t milliseconds;
float seconds;
uint64_t initial_ticks;
uint64_t ticks_per_second;
};
typedef void *HANDLE;
typedef struct _XINPUT_STATE XINPUT_STATE;
typedef unsigned long (__stdcall *XINPUTGETSTATE)(unsigned long dwUserIndex, XINPUT_STATE* pState);
struct IAudioClient;
struct IAudioRenderClient;
struct Mu_Win32 {
HANDLE window;
HANDLE device_context;
void *main_fiber;
void *message_fiber;
XINPUTGETSTATE xinput_get_state;
IAudioClient *audio_client;
IAudioRenderClient *audio_render_client;
HANDLE wgl_context;
};
struct Mu {
Mu_Bool initialized;
Mu_Bool quit;
char *error;
char error_buffer[MU_MAX_ERROR];
Mu_Window window;
Mu_DigitalButton keys[MU_MAX_KEYS];
Mu_Gamepad gamepad;
Mu_Mouse mouse;
char text[MU_MAX_TEXT];
size_t text_length;
Mu_Time time;
Mu_Audio audio;
Mu_Win32 win32;
};
Mu_Bool Mu_Initialize(Mu *mu);
Mu_Bool Mu_Pull(Mu *mu);
void Mu_Push(Mu *mu);
struct Mu_Image {
uint8_t *pixels;
uint32_t channels;
uint32_t width;
uint32_t height;
};
Mu_Bool Mu_LoadImage(const char *filename, Mu_Image *image);
Mu_Bool Mu_LoadAudio(const char *filename, Mu_AudioBuffer *audio);
MU_EXTERN_END
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment