The game now uses MIDI support for alternative input methods.feature/data-structs
@@ -60,3 +60,4 @@ $RECYCLE.BIN/ | |||
.idea/ | |||
cmake-build-debug/ | |||
dependencies/ | |||
assets/ |
@@ -43,12 +43,13 @@ add_executable( | |||
src/packages/game/input/IZ_keyboard.h | |||
src/packages/game/IZ_config.c | |||
src/packages/game/IZ_config.h | |||
src/packages/game/geometry/IZ_point2d.c src/packages/game/geometry/IZ_point2d.h src/packages/game/geometry/IZ_vector2d.c src/packages/game/geometry/IZ_vector2d.h src/packages/game/geometry/IZ_rect.c src/packages/game/geometry/IZ_rect.h src/packages/game/core/IZ_object.c src/packages/game/core/IZ_object.h src/packages/game/core/IZ_creature.c src/packages/game/core/IZ_creature.h src/packages/game/core/IZ_entity.c src/packages/game/core/IZ_entity.h src/packages/game/memory/IZ_pool.c src/packages/game/memory/IZ_pool.h src/packages/game/input/IZ_input.c src/packages/game/input/IZ_input.h) | |||
src/packages/game/geometry/IZ_point2d.c src/packages/game/geometry/IZ_point2d.h src/packages/game/geometry/IZ_vector2d.c src/packages/game/geometry/IZ_vector2d.h src/packages/game/geometry/IZ_rect.c src/packages/game/geometry/IZ_rect.h src/packages/game/core/IZ_object.c src/packages/game/core/IZ_object.h src/packages/game/core/IZ_creature.c src/packages/game/core/IZ_creature.h src/packages/game/core/IZ_entity.c src/packages/game/core/IZ_entity.h src/packages/game/memory/IZ_pool.c src/packages/game/memory/IZ_pool.h src/packages/game/input/IZ_input.c src/packages/game/input/IZ_input.h src/packages/game/input/IZ_midi.c src/packages/game/input/IZ_midi.h) | |||
target_link_libraries( | |||
game | |||
SDL2main | |||
SDL2 | |||
portmidi | |||
) | |||
add_executable( | |||
@@ -109,3 +110,10 @@ if (WIN32) | |||
"${PROJECT_SOURCE_DIR}/dependencies/SDL2/lib/${PROJECT_ARCH}/SDL2.dll" # <--this is in-file | |||
$<TARGET_FILE_DIR:game>) # <--this is out-file path | |||
endif () | |||
if (WIN32) | |||
add_custom_command(TARGET game POST_BUILD | |||
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake - E copy_if_different..." | |||
"${PROJECT_SOURCE_DIR}/dependencies/portmidi/Release/portmidi.dll" # <--this is in-file | |||
$<TARGET_FILE_DIR:game>) # <--this is out-file path | |||
endif () |
@@ -17,14 +17,79 @@ IZ_ProcedureResult IZ_InitializeApp(IZ_App* app) { | |||
return 2; | |||
} | |||
for (uint8_t p = 0; p < PLAYERS; p += 1) { | |||
IZ_InitializeInput(config_path, &app->input_state[p], p); | |||
} | |||
IZ_InitializeInput(config_path, &app->input_state); | |||
app->quit = false; | |||
return 0; | |||
} | |||
void IZ_TeardownApp(IZ_App* app) { | |||
IZ_TeardownInput(&app->input_state); | |||
IZ_TeardownVideo(&app->video_state); | |||
SDL_Quit(); | |||
} | |||
void IZ_HandleSDLEvents(IZ_App* app) { | |||
while (SDL_PollEvent(&app->input_state.sdl_event) != 0) { | |||
if (app->input_state.sdl_event.type == SDL_QUIT) { | |||
app->quit = true; | |||
break; | |||
} | |||
IZ_HandleSDLInputEvents(app->input_state.sdl_event, &app->input_state); | |||
} | |||
} | |||
void IZ_HandlePortMIDIEvents(IZ_App* app) { | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
int32_t midi_events_count = Pm_Read( | |||
app->input_state.midi_input_state[player_index].stream, | |||
app->input_state.midi_input_state[player_index].event_buffer, | |||
1024 | |||
); | |||
if (midi_events_count > 0) { | |||
for (int32_t midi_event_index = 0; midi_event_index < midi_events_count; midi_event_index += 1) { | |||
IZ_HandlePortMIDIInputEvents( | |||
app->input_state.midi_input_state->event_buffer[midi_event_index], | |||
&app->input_state | |||
); | |||
} | |||
} | |||
} | |||
} | |||
void IZ_HandleEvents(IZ_App* app) { | |||
IZ_HandleSDLEvents(app); | |||
IZ_HandlePortMIDIEvents(app); | |||
} | |||
IZ_ProcedureResult IZ_RunApp(IZ_App* app, uint8_t argc, char* argv[]) { | |||
printf_s("Args (%u):\n", argc); | |||
for (uint8_t i = 0; i < argc; i += 1) { | |||
printf_s(" %s", argv[i]); | |||
} | |||
IZ_ProcedureResult init_result = IZ_InitializeApp(app); | |||
if (init_result) { | |||
return init_result; | |||
} | |||
while (true) { | |||
uint64_t ticks = SDL_GetTicks64(); | |||
// TODO do audio processing | |||
// TODO do networking? | |||
// process events | |||
IZ_HandleEvents(app); | |||
if (app->quit) { | |||
break; | |||
} | |||
IZ_UpdateVideo(&app->video_state, &app->input_state, ticks); | |||
} | |||
IZ_TeardownApp(app); | |||
return 0; | |||
} |
@@ -2,20 +2,19 @@ | |||
#define IZ_APP_H | |||
#include <SDL.h> | |||
#include <stdbool.h> | |||
#include "input/IZ_input.h" | |||
#include "output/IZ_video.h" | |||
#include "memory/IZ_pool.h" | |||
#include "input/IZ_action.h" | |||
typedef struct { | |||
IZ_InputState input_state[PLAYERS]; | |||
IZ_InputState input_state; | |||
IZ_VideoState video_state; | |||
IZ_Pool memory_pool; | |||
bool quit; | |||
} IZ_App; | |||
IZ_ProcedureResult IZ_InitializeApp(IZ_App*); | |||
void IZ_TeardownApp(IZ_App*); | |||
IZ_ProcedureResult IZ_RunApp(IZ_App*, uint8_t, char**); | |||
#endif |
@@ -1,18 +1,38 @@ | |||
#include "IZ_input.h" | |||
void IZ_HandleInputEvents(SDL_Event e, IZ_InputState* state) { | |||
void IZ_HandleSDLInputEvents(SDL_Event e, IZ_InputState* state) { | |||
IZ_HandleJoystickEvents(e, &state->joystick_state, &state->action); | |||
IZ_HandleKeyboardEvents(e, &state->keyboard_state, &state->action); | |||
} | |||
void IZ_InitializeInput(const char* config_path, IZ_InputState* state, uint8_t p) { | |||
if (IZ_InitializeKeyboardConfig(config_path, &state->keyboard_state.config, p)) { | |||
fprintf_s(stderr, "Error committing keyboard config for player %d.\n", p); | |||
void IZ_HandlePortMIDIInputEvents(PmEvent e, IZ_InputState* state) { | |||
IZ_HandleMIDIInputEvents(e, &state->midi_input_state, &state->action); | |||
} | |||
void IZ_InitializeInput(const char* config_path, IZ_InputState* state) { | |||
*state = (IZ_InputState) {}; | |||
IZ_ProcedureResult joystick_result = IZ_InitializeJoystickState(config_path, &state->joystick_state); | |||
if (joystick_result) { | |||
fprintf_s(stderr, "Error committing joystick config. Code: %u.\n", joystick_result); | |||
} | |||
IZ_ProcedureResult keyboard_result = IZ_InitializeKeyboardState(config_path, &state->keyboard_state); | |||
if (keyboard_result) { | |||
fprintf_s(stderr, "Error committing keyboard config. Code: %u.\n", keyboard_result); | |||
} | |||
IZ_ProcedureResult midi_input_result = IZ_InitializeMIDIInput(config_path, &state->midi_input_state); | |||
if (midi_input_result) { | |||
fprintf_s(stderr, "Error committing MIDI input config. Code: %u.\n", midi_input_result); | |||
} | |||
if (IZ_InitializeJoystickConfig(config_path, &state->joystick_state.config, p)) { | |||
fprintf_s(stderr, "Error committing joystick config for player %d.\n", p); | |||
for (uint8_t p = 0; p < PLAYERS; p += 1) { | |||
state->action[p] = 0; | |||
} | |||
} | |||
state->action = 0; | |||
void IZ_TeardownInput(IZ_InputState* state) { | |||
IZ_TeardownJoystickState(&state->joystick_state); | |||
IZ_TeardownMIDIInput(&state->midi_input_state); | |||
} |
@@ -4,15 +4,22 @@ | |||
#include "IZ_action.h" | |||
#include "IZ_keyboard.h" | |||
#include "IZ_joystick.h" | |||
#include "IZ_midi.h" | |||
typedef struct { | |||
IZ_Action action; | |||
IZ_KeyboardState keyboard_state; | |||
IZ_JoystickState joystick_state; | |||
IZ_Action action[PLAYERS]; | |||
SDL_Event sdl_event; | |||
IZ_KeyboardState keyboard_state[PLAYERS]; | |||
IZ_JoystickState joystick_state[PLAYERS]; | |||
IZ_MIDIInputState midi_input_state[PLAYERS]; | |||
} IZ_InputState; | |||
void IZ_HandleInputEvents(SDL_Event, IZ_InputState*); | |||
void IZ_HandleSDLInputEvents(SDL_Event, IZ_InputState*); | |||
void IZ_InitializeInput(const char*, IZ_InputState*, uint8_t); | |||
void IZ_HandlePortMIDIInputEvents(PmEvent, IZ_InputState*); | |||
void IZ_InitializeInput(const char*, IZ_InputState*); | |||
void IZ_TeardownInput(IZ_InputState*); | |||
#endif |
@@ -2,18 +2,18 @@ | |||
void IZ_HandleJoystickDeviceEvents(SDL_Event e, IZ_JoystickState* state) { | |||
if (e.type == SDL_JOYDEVICEADDED) { | |||
if (SDL_NumJoysticks() <= PLAYERS && !state->joystick_instance) { | |||
state->joystick_instance = SDL_JoystickOpen(e.jdevice.which); | |||
if (SDL_NumJoysticks() <= PLAYERS && !state->device) { | |||
state->device = SDL_JoystickOpen(e.jdevice.which); | |||
} | |||
return; | |||
} | |||
if (e.type == SDL_JOYDEVICEREMOVED) { | |||
if ( | |||
state->joystick_instance | |||
&& SDL_JoystickInstanceID(state->joystick_instance) == e.jdevice.which | |||
state->device | |||
&& SDL_JoystickInstanceID(state->device) == e.jdevice.which | |||
) { | |||
state->joystick_instance = NULL; | |||
state->device = NULL; | |||
} | |||
} | |||
} | |||
@@ -82,56 +82,97 @@ void IZ_HandleJoystickButtonEvents(SDL_Event e, IZ_JoystickState* state, IZ_Acti | |||
} | |||
} | |||
void IZ_HandleJoystickEvents(SDL_Event e, IZ_JoystickState* state, IZ_Action* action) { | |||
IZ_HandleJoystickDeviceEvents(e, state); | |||
IZ_HandleJoystickAxisEvents(e, state, action); | |||
IZ_HandleJoystickHatEvents(e, action); | |||
IZ_HandleJoystickButtonEvents(e, state, action); | |||
void IZ_HandleJoystickEvents(SDL_Event e, IZ_JoystickState(* state)[PLAYERS], IZ_Action(* action)[PLAYERS]) { | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
IZ_HandleJoystickDeviceEvents(e, state[player_index]); | |||
IZ_HandleJoystickAxisEvents(e, state[player_index], action[player_index]); | |||
IZ_HandleJoystickHatEvents(e, action[player_index]); | |||
IZ_HandleJoystickButtonEvents(e, state[player_index], action[player_index]); | |||
} | |||
} | |||
void IZ_LoadJoystickConfig(const char* config_path, IZ_JoystickConfig* config, uint8_t player_index) { | |||
char joystick_control_mapping_section_name[26]; | |||
sprintf_s(joystick_control_mapping_section_name, 26, "Joystick.%d.ControlMapping", player_index); | |||
void IZ_LoadJoystickConfig(const char* config_path, IZ_JoystickState(* state)[PLAYERS]) { | |||
char control_mapping_section_name[26]; | |||
char main_section_name[11]; | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
sprintf_s(control_mapping_section_name, 26, "Joystick.%d.ControlMapping", player_index); | |||
for (uint8_t i = 4; i < CONTROLS; i += 1) { | |||
state[player_index]->config.control_mapping[i] = ini_getl( | |||
control_mapping_section_name, | |||
ACTION_NAMES[i], | |||
IZ_DEFAULT_JOYSTICK_CONTROLS[player_index][i], | |||
config_path | |||
); | |||
} | |||
for (uint8_t i = 4; i < CONTROLS; i += 1) { | |||
config->control_mapping[i] = ini_getl(joystick_control_mapping_section_name, ACTION_NAMES[i], IZ_DEFAULT_JOYSTICK_CONTROLS[player_index][i], config_path); | |||
sprintf_s(main_section_name, 11, "Joystick.%d", player_index); | |||
state[player_index]->config.axis_threshold = ini_getl(main_section_name, "AxisThreshold", IZ_DEFAULT_AXIS_THRESHOLD, config_path); | |||
state[player_index]->config.device_id = ini_getl(main_section_name, "DeviceID", player_index, config_path); | |||
} | |||
char joystick_section_name[] = "Joystick.0"; | |||
joystick_section_name[9] = (char) (48 + player_index); | |||
config->axis_threshold = ini_getl(joystick_section_name, "AxisThreshold", 8000, config_path); | |||
} | |||
IZ_ProcedureResult IZ_SaveJoystickConfig(const char* config_path, IZ_JoystickConfig* config, uint8_t player_index) { | |||
char joystick_control_mapping_section_name[26]; | |||
sprintf_s(joystick_control_mapping_section_name, 26, "Joystick.%d.ControlMapping", player_index); | |||
IZ_ProcedureResult IZ_SaveJoystickConfig(const char* config_path, IZ_JoystickState(* state)[PLAYERS]) { | |||
uint8_t problem = 0; | |||
char control_mapping_section_name[26]; | |||
char main_section_name[11]; | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
sprintf_s(control_mapping_section_name, 26, "Joystick.%d.ControlMapping", player_index); | |||
for (uint8_t i = 4; i < CONTROLS; i += 1) { | |||
if (!ini_putl( | |||
control_mapping_section_name, | |||
ACTION_NAMES[i], | |||
state[player_index]->config.control_mapping[i], | |||
config_path | |||
)) { | |||
return 1; | |||
} | |||
} | |||
for (uint8_t i = 4; i < CONTROLS; i += 1) { | |||
sprintf_s(main_section_name, 11, "Joystick.%d", player_index); | |||
if (!ini_putl( | |||
joystick_control_mapping_section_name, | |||
ACTION_NAMES[i], | |||
config->control_mapping[i], | |||
config_path) | |||
) { | |||
return 1; | |||
main_section_name, | |||
"DeviceID", | |||
state[player_index]->config.device_id, | |||
config_path | |||
)) { | |||
problem |= (1 << player_index); | |||
} | |||
if (!ini_putl( | |||
main_section_name, | |||
"AxisThreshold", | |||
state[player_index]->config.axis_threshold, | |||
config_path | |||
)) { | |||
problem |= (1 << player_index); | |||
} | |||
} | |||
char joystick_section_name[] = "Joystick.0"; | |||
joystick_section_name[9] = (char) (48 + player_index); | |||
if (!ini_putl( | |||
joystick_section_name, | |||
"AxisThreshold", | |||
config->axis_threshold, | |||
config_path) | |||
) { | |||
return problem; | |||
} | |||
IZ_ProcedureResult IZ_InitializeJoystickState(const char* config_path, IZ_JoystickState(* state)[PLAYERS]) { | |||
IZ_LoadJoystickConfig(config_path, state); | |||
if (IZ_SaveJoystickConfig(config_path, state)) { | |||
return 1; | |||
} | |||
uint8_t joysticks_count = SDL_NumJoysticks(); | |||
for (uint8_t player_index = 0; player_index < joysticks_count; player_index += 1) { | |||
state[player_index]->device = SDL_JoystickOpen(state[player_index]->config.device_id); | |||
} | |||
return 0; | |||
} | |||
IZ_ProcedureResult IZ_InitializeJoystickConfig(const char* config_path, IZ_JoystickConfig* config, uint8_t p) { | |||
IZ_LoadJoystickConfig(config_path, config, p); | |||
return IZ_SaveJoystickConfig(config_path, config, p); | |||
void IZ_TeardownJoystickState(IZ_JoystickState(* state)[PLAYERS]) { | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
if (!state[player_index]->device) { | |||
continue; | |||
} | |||
SDL_JoystickClose(state[player_index]->device); | |||
} | |||
} |
@@ -8,6 +8,8 @@ | |||
typedef uint8_t IZ_PadButton; | |||
static const uint16_t IZ_DEFAULT_AXIS_THRESHOLD = 8000; | |||
typedef enum { | |||
IZ_JOY_AXIS_DIRECTION_HORIZONTAL1 = 0, | |||
IZ_JOY_AXIS_DIRECTION_VERTICAL1 = 1, | |||
@@ -17,11 +19,12 @@ typedef enum { | |||
typedef struct { | |||
uint16_t axis_threshold; | |||
SDL_JoystickID device_id; | |||
IZ_PadButton control_mapping[CONTROLS]; | |||
} IZ_JoystickConfig; | |||
typedef struct { | |||
SDL_Joystick* joystick_instance; | |||
SDL_Joystick* device; | |||
IZ_JoystickConfig config; | |||
} IZ_JoystickState; | |||
@@ -46,10 +49,12 @@ static IZ_PadButton IZ_DEFAULT_JOYSTICK_CONTROLS[PLAYERS][CONTROLS] = { | |||
}, | |||
}; | |||
IZ_ProcedureResult IZ_SaveJoystickConfig(const char*, IZ_JoystickConfig*, uint8_t); | |||
IZ_ProcedureResult IZ_SaveJoystickConfig(const char*, IZ_JoystickState(*)[PLAYERS]); | |||
void IZ_HandleJoystickEvents(SDL_Event, IZ_JoystickState(*)[PLAYERS], IZ_Action(*)[PLAYERS]); | |||
void IZ_HandleJoystickEvents(SDL_Event, IZ_JoystickState*, IZ_Action*); | |||
IZ_ProcedureResult IZ_InitializeJoystickState(const char*, IZ_JoystickState(*)[PLAYERS]); | |||
IZ_ProcedureResult IZ_InitializeJoystickConfig(const char*, IZ_JoystickConfig*, uint8_t); | |||
void IZ_TeardownJoystickState(IZ_JoystickState(*)[PLAYERS]); | |||
#endif |
@@ -1,6 +1,6 @@ | |||
#include "IZ_keyboard.h" | |||
void IZ_HandleKeyboardEvents(SDL_Event e, IZ_KeyboardState* state, IZ_Action* action) { | |||
void IZ_HandleKewyboardKeyUpDownEvents(SDL_Event e, IZ_KeyboardState* state, IZ_Action* action) { | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
if (e.key.keysym.sym == state->config.control_mapping[i]) { | |||
const uint16_t bitflag = (0x1 << i); | |||
@@ -16,42 +16,53 @@ void IZ_HandleKeyboardEvents(SDL_Event e, IZ_KeyboardState* state, IZ_Action* ac | |||
} | |||
} | |||
IZ_ProcedureResult IZ_SaveKeyboardConfig(const char* config_path, IZ_KeyboardConfig* config, uint8_t player_index) { | |||
char keyboard_section_name[26]; | |||
sprintf_s(keyboard_section_name, 26, "Keyboard.%d.ControlMapping", player_index); | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
if (!ini_puts( | |||
keyboard_section_name, | |||
ACTION_NAMES[i], | |||
SDL_GetKeyName(config->control_mapping[i]), | |||
config_path) | |||
) { | |||
return 1; | |||
void IZ_HandleKeyboardEvents(SDL_Event e, IZ_KeyboardState(* state)[PLAYERS], IZ_Action(* action)[PLAYERS]) { | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
IZ_HandleKewyboardKeyUpDownEvents(e, state[player_index], action[player_index]); | |||
} | |||
} | |||
IZ_ProcedureResult IZ_SaveKeyboardConfig(const char* config_path, IZ_KeyboardState(* state)[PLAYERS]) { | |||
uint8_t problem = 0; | |||
char control_mapping_section_name[26]; | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
sprintf_s(control_mapping_section_name, 26, "Keyboard.%d.ControlMapping", player_index); | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
if (!ini_puts( | |||
control_mapping_section_name, | |||
ACTION_NAMES[i], | |||
SDL_GetKeyName(state[player_index]->config.control_mapping[i]), | |||
config_path | |||
)) { | |||
problem |= (1 << player_index); | |||
} | |||
} | |||
} | |||
return 0; | |||
return problem; | |||
} | |||
void IZ_LoadKeyboardConfig(const char* config_path, IZ_KeyboardConfig* config, uint8_t player_index) { | |||
void IZ_LoadKeyboardConfig(const char* config_path, IZ_KeyboardState(* state)[PLAYERS]) { | |||
char buffer[128]; | |||
char keyboard_section_name[26]; | |||
sprintf_s(keyboard_section_name, 26, "Keyboard.%d.ControlMapping", player_index); | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
ini_gets( | |||
keyboard_section_name, | |||
ACTION_NAMES[i], | |||
SDL_GetKeyName(IZ_DEFAULT_KEYBOARD_CONTROLS[player_index][i]), | |||
buffer, | |||
128, | |||
config_path | |||
); | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
sprintf_s(keyboard_section_name, 26, "Keyboard.%d.ControlMapping", player_index); | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
ini_gets( | |||
keyboard_section_name, | |||
ACTION_NAMES[i], | |||
SDL_GetKeyName(IZ_DEFAULT_KEYBOARD_CONTROLS[player_index][i]), | |||
buffer, | |||
128, | |||
config_path | |||
); | |||
config->control_mapping[i] = SDL_GetKeyFromName(buffer); | |||
state[player_index]->config.control_mapping[i] = SDL_GetKeyFromName(buffer); | |||
} | |||
} | |||
} | |||
IZ_ProcedureResult IZ_InitializeKeyboardConfig(const char* config_path, IZ_KeyboardConfig* config, uint8_t p) { | |||
IZ_LoadKeyboardConfig(config_path, config, p); | |||
return IZ_SaveKeyboardConfig(config_path, config, p); | |||
IZ_ProcedureResult IZ_InitializeKeyboardState(const char* config_path, IZ_KeyboardState(* state)[PLAYERS]) { | |||
IZ_LoadKeyboardConfig(config_path, state); | |||
return IZ_SaveKeyboardConfig(config_path, state); | |||
} |
@@ -35,10 +35,10 @@ static const SDL_KeyCode IZ_DEFAULT_KEYBOARD_CONTROLS[PLAYERS][CONTROLS] = { | |||
}, | |||
}; | |||
IZ_ProcedureResult IZ_SaveKeyboardConfig(const char*, IZ_KeyboardConfig*, uint8_t); | |||
IZ_ProcedureResult IZ_SaveKeyboardConfig(const char*, IZ_KeyboardState(*)[PLAYERS]); | |||
void IZ_HandleKeyboardEvents(SDL_Event, IZ_KeyboardState*, IZ_Action*); | |||
void IZ_HandleKeyboardEvents(SDL_Event, IZ_KeyboardState(*)[PLAYERS], IZ_Action(*)[PLAYERS]); | |||
IZ_ProcedureResult IZ_InitializeKeyboardConfig(const char*, IZ_KeyboardConfig*, uint8_t); | |||
IZ_ProcedureResult IZ_InitializeKeyboardState(const char*, IZ_KeyboardState(*)[PLAYERS]); | |||
#endif |
@@ -0,0 +1,181 @@ | |||
#include "IZ_midi.h" | |||
char* IZ_GetMIDINoteName(uint8_t midi_note) { | |||
static const char* pitch_names[] = { | |||
"C", | |||
"C#", | |||
"D", | |||
"D#", | |||
"E", | |||
"F", | |||
"F#", | |||
"G", | |||
"G#", | |||
"A", | |||
"A#", | |||
"B" | |||
}; | |||
const uint8_t pitch_class = midi_note % 12; | |||
const uint8_t octave = midi_note / 12; | |||
static char note_name[4]; | |||
sprintf_s(note_name, 4, "%s%u", pitch_names[pitch_class], octave); | |||
return note_name; | |||
} | |||
uint8_t IZ_GetMIDINoteFromName(char* name) { | |||
char name_copy[4]; | |||
memcpy_s(name_copy, 4, name, 4); | |||
_strlwr_s(name_copy, 4); | |||
uint8_t octave; | |||
const char base_pitch_name[] = "c d ef g a b"; | |||
if (strlen(name_copy) == 2) { | |||
octave = name_copy[1] - '0'; | |||
for (uint8_t i = 0; i < 12; i += 1) { | |||
if (base_pitch_name[i] == name_copy[0]) { | |||
return (octave * 12) + i; | |||
} | |||
} | |||
return 255u; | |||
} | |||
uint8_t pitch_class; | |||
octave = name_copy[2] - '0'; | |||
if (strstr(name_copy, "c#") || strstr(name_copy, "db")) { | |||
pitch_class = 1; | |||
} else if (strstr(name_copy, "d#") || strstr(name_copy, "eb")) { | |||
pitch_class = 3; | |||
} else if (strstr(name_copy, "f#") || strstr(name_copy, "gb")) { | |||
pitch_class = 6; | |||
} else if (strstr(name_copy, "g#") || strstr(name_copy, "ab")) { | |||
pitch_class = 8; | |||
} else if (strstr(name_copy, "a#") || strstr(name_copy, "bb")) { | |||
pitch_class = 10; | |||
} else { | |||
return 255u; | |||
} | |||
return (octave * 12) + pitch_class; | |||
} | |||
void IZ_HandleMIDINoteOnOffEvents(PmEvent e, IZ_MIDIInputState* state, IZ_Action* action) { | |||
uint32_t message = e.message; | |||
uint8_t status = message & 0xFFu; | |||
uint8_t data1 = (message >> 8) & 0xFFu; | |||
// uint8_t data2 = (message >> 16) & 0xFFu; | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
if (data1 == state->config.control_mapping[i]) { | |||
const uint16_t bitflag = (0x1 << i); | |||
if (status == IZ_MIDI_NOTE_ON) { | |||
*action |= bitflag; | |||
return; | |||
} | |||
if (status == IZ_MIDI_NOTE_OFF) { | |||
*action &= ~bitflag; | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
void IZ_HandleMIDIInputEvents(PmEvent e, IZ_MIDIInputState(* state)[PLAYERS], IZ_Action(* action)[PLAYERS]) { | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
IZ_HandleMIDINoteOnOffEvents(e, state[player_index], action[player_index]); | |||
} | |||
} | |||
IZ_ProcedureResult IZ_SaveMIDIInputConfig(const char* config_path, IZ_MIDIInputState(* state)[PLAYERS]) { | |||
uint8_t problem = 0; | |||
char control_mapping_section_name[27]; | |||
char main_section_name[12]; | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
sprintf_s(control_mapping_section_name, 27, "MIDIInput.%d.ControlMapping", player_index); | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
if (!ini_puts( | |||
control_mapping_section_name, | |||
ACTION_NAMES[i], | |||
IZ_GetMIDINoteName(state[player_index]->config.control_mapping[i]), | |||
config_path | |||
)) { | |||
problem |= (1 << player_index); | |||
} | |||
} | |||
sprintf_s(main_section_name, 12, "MIDIInput.%d", player_index); | |||
if (!ini_putl( | |||
main_section_name, | |||
"DeviceID", | |||
state[player_index]->config.device_id, | |||
config_path | |||
)) { | |||
problem |= (1 << player_index); | |||
} | |||
} | |||
return problem; | |||
} | |||
void IZ_LoadMIDIInputConfig(const char* config_path, IZ_MIDIInputState(* state)[PLAYERS]) { | |||
char buffer[128]; | |||
char control_mapping_section_name[27]; | |||
char main_section_name[12]; | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
sprintf_s(control_mapping_section_name, 27, "MIDIInput.%d.ControlMapping", player_index); | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
ini_gets( | |||
control_mapping_section_name, | |||
ACTION_NAMES[i], | |||
IZ_GetMIDINoteName(IZ_DEFAULT_MIDI_INPUT_CONTROLS[player_index][i]), | |||
buffer, | |||
128, | |||
config_path | |||
); | |||
state[player_index]->config.control_mapping[i] = IZ_GetMIDINoteFromName(buffer); | |||
} | |||
sprintf_s(main_section_name, 12, "MIDIInput.%d", player_index); | |||
state[player_index]->config.device_id = ini_getl(main_section_name, "DeviceID", player_index, config_path); | |||
} | |||
} | |||
IZ_ProcedureResult IZ_InitializeMIDIInput(const char* config_path, IZ_MIDIInputState(* state)[PLAYERS]) { | |||
if (Pm_Initialize()) { | |||
return 1; | |||
} | |||
IZ_LoadMIDIInputConfig(config_path, state); | |||
if (IZ_SaveMIDIInputConfig(config_path, state)) { | |||
return 2; | |||
} | |||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||
state[player_index]->device_info = Pm_GetDeviceInfo(state[player_index]->config.device_id); | |||
state[player_index]->stream = NULL; | |||
Pm_OpenInput( | |||
&state[player_index]->stream, | |||
state[player_index]->config.device_id, | |||
NULL, | |||
MIDI_EVENT_BUFFER_SIZE, | |||
NULL, | |||
NULL | |||
); | |||
} | |||
return 0; | |||
} | |||
void IZ_TeardownMIDIInput(IZ_MIDIInputState(* state)[PLAYERS]) { | |||
for (uint8_t i = 0; i < PLAYERS; i += 1) { | |||
if (!state[i]->stream) { | |||
continue; | |||
} | |||
Pm_Close(state[i]->stream); | |||
} | |||
} |
@@ -0,0 +1,58 @@ | |||
#ifndef IZ_MIDI_H | |||
#define IZ_MIDI_H | |||
#include <string.h> | |||
#include <portmidi.h> | |||
#include <minIni.h> | |||
#include "IZ_action.h" | |||
#define MIDI_EVENT_BUFFER_SIZE 1024 | |||
typedef uint8_t IZ_MIDINote; | |||
static const uint8_t IZ_MIDI_NOTE_ON = 0x90u; | |||
static const uint8_t IZ_MIDI_NOTE_OFF = 0x80u; | |||
typedef struct { | |||
PmDeviceID device_id; | |||
IZ_MIDINote control_mapping[CONTROLS]; | |||
} IZ_MIDIInputConfig; | |||
typedef struct { | |||
IZ_MIDIInputConfig config; | |||
const PmDeviceInfo* device_info; | |||
PmStream* stream; | |||
PmEvent event_buffer[MIDI_EVENT_BUFFER_SIZE]; | |||
} IZ_MIDIInputState; | |||
static const IZ_MIDINote IZ_DEFAULT_MIDI_INPUT_CONTROLS[PLAYERS][CONTROLS] = { | |||
{ | |||
70, | |||
72, | |||
71, | |||
69, | |||
77, | |||
76, | |||
48, | |||
49, | |||
50, | |||
51, | |||
52, | |||
53, | |||
54, | |||
55, | |||
56, | |||
57, | |||
} | |||
}; | |||
IZ_ProcedureResult IZ_SaveMIDIInputConfig(const char*, IZ_MIDIInputState(*)[PLAYERS]); | |||
void IZ_HandleMIDIInputEvents(PmEvent, IZ_MIDIInputState(*)[PLAYERS], IZ_Action(*)[PLAYERS]); | |||
IZ_ProcedureResult IZ_InitializeMIDIInput(const char*, IZ_MIDIInputState(*)[PLAYERS]); | |||
void IZ_TeardownMIDIInput(IZ_MIDIInputState(*)[PLAYERS]); | |||
#endif |
@@ -1,43 +1,7 @@ | |||
#include <SDL.h> | |||
#include <stdbool.h> | |||
#include "IZ_app.h" | |||
int main(int argc, char* args[]) { | |||
int main(int argc, char* argv[]) { | |||
IZ_App app; | |||
IZ_ProcedureResult result = IZ_InitializeApp(&app); | |||
if (result) { | |||
return result; | |||
} | |||
bool quit = false; | |||
SDL_Event e; | |||
while (true) { | |||
uint64_t ticks = SDL_GetTicks64(); | |||
// TODO do audio processing | |||
// TODO do networking? | |||
// process events | |||
while (SDL_PollEvent(&e) != 0) { | |||
if (e.type == SDL_QUIT) { | |||
quit = true; | |||
break; | |||
} | |||
for (uint8_t p = 0; p < PLAYERS; p += 1) { | |||
IZ_HandleInputEvents(e, &(app.input_state[p])); | |||
} | |||
} | |||
if (quit) { | |||
break; | |||
} | |||
IZ_UpdateVideo(&app.video_state, &app.input_state, ticks); | |||
} | |||
IZ_TeardownApp(&app); | |||
return 0; | |||
return IZ_RunApp(&app, argc, argv); | |||
} |
@@ -21,6 +21,8 @@ void IZ_LoadVideoConfig(const char* config_path, IZ_VideoConfig* config) { | |||
} | |||
IZ_ProcedureResult IZ_InitializeVideo(const char* config_path, IZ_VideoState* state) { | |||
*state = (IZ_VideoState) {}; | |||
IZ_LoadVideoConfig(config_path, &state->config); | |||
if (IZ_SaveVideoConfig(config_path, &state->config)) { | |||
fprintf_s(stderr, "Error committing video config.\n"); | |||
@@ -45,7 +47,7 @@ IZ_ProcedureResult IZ_InitializeVideo(const char* config_path, IZ_VideoState* st | |||
return 0; | |||
} | |||
void IZ_UpdateVideo(IZ_VideoState* video_state, IZ_InputState(* input_states)[PLAYERS], uint64_t ticks) { | |||
void IZ_UpdateVideo(IZ_VideoState* video_state, IZ_InputState* input_states, uint64_t ticks) { | |||
if (ticks - video_state->last_update_at > 1000 / video_state->config.max_fps) { | |||
// Update window | |||
SDL_FillRect(video_state->surface, NULL, SDL_MapRGB(video_state->surface->format, 0x00, 0x00, 0x00)); | |||
@@ -71,7 +73,7 @@ void IZ_UpdateVideo(IZ_VideoState* video_state, IZ_InputState(* input_states)[PL | |||
const uint8_t row = i / 4; | |||
const IZ_Action bitflag = (0x1 << i); | |||
const uint8_t size = 4; | |||
if (input_states[p]->action & bitflag) { | |||
if (input_states->action[p] & bitflag) { | |||
SDL_FillRect(video_state->surface, &(SDL_Rect) { | |||
column * size, | |||
row * size, | |||
@@ -27,7 +27,7 @@ IZ_ProcedureResult IZ_InitializeVideo(const char*, IZ_VideoState*); | |||
IZ_ProcedureResult IZ_SaveVideoConfig(const char*, IZ_VideoConfig*); | |||
void IZ_UpdateVideo(IZ_VideoState*, IZ_InputState(*)[PLAYERS], uint64_t); | |||
void IZ_UpdateVideo(IZ_VideoState*, IZ_InputState*, uint64_t); | |||
void IZ_TeardownVideo(IZ_VideoState*); | |||