diff --git a/.gitignore b/.gitignore index def08d5..78920f7 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ $RECYCLE.BIN/ .idea/ cmake-build-debug/ dependencies/ +assets/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d9a4003..f66c5f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 $) # <--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 + $) # <--this is out-file path +endif () diff --git a/src/packages/game/IZ_app.c b/src/packages/game/IZ_app.c index 5fee81e..6470cb2 100644 --- a/src/packages/game/IZ_app.c +++ b/src/packages/game/IZ_app.c @@ -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; +} diff --git a/src/packages/game/IZ_app.h b/src/packages/game/IZ_app.h index bc1b13f..a069141 100644 --- a/src/packages/game/IZ_app.h +++ b/src/packages/game/IZ_app.h @@ -2,20 +2,19 @@ #define IZ_APP_H #include +#include #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 diff --git a/src/packages/game/input/IZ_input.c b/src/packages/game/input/IZ_input.c index f0f9b0d..f47ef3d 100644 --- a/src/packages/game/input/IZ_input.c +++ b/src/packages/game/input/IZ_input.c @@ -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); } diff --git a/src/packages/game/input/IZ_input.h b/src/packages/game/input/IZ_input.h index ce09a1e..50a44b5 100644 --- a/src/packages/game/input/IZ_input.h +++ b/src/packages/game/input/IZ_input.h @@ -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 diff --git a/src/packages/game/input/IZ_joystick.c b/src/packages/game/input/IZ_joystick.c index ba2b481..ea07515 100644 --- a/src/packages/game/input/IZ_joystick.c +++ b/src/packages/game/input/IZ_joystick.c @@ -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); + } } diff --git a/src/packages/game/input/IZ_joystick.h b/src/packages/game/input/IZ_joystick.h index e88089d..a257958 100644 --- a/src/packages/game/input/IZ_joystick.h +++ b/src/packages/game/input/IZ_joystick.h @@ -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 diff --git a/src/packages/game/input/IZ_keyboard.c b/src/packages/game/input/IZ_keyboard.c index 7cd1207..e39bd99 100644 --- a/src/packages/game/input/IZ_keyboard.c +++ b/src/packages/game/input/IZ_keyboard.c @@ -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); } diff --git a/src/packages/game/input/IZ_keyboard.h b/src/packages/game/input/IZ_keyboard.h index 3e31993..16228ec 100644 --- a/src/packages/game/input/IZ_keyboard.h +++ b/src/packages/game/input/IZ_keyboard.h @@ -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 diff --git a/src/packages/game/input/IZ_midi.c b/src/packages/game/input/IZ_midi.c new file mode 100644 index 0000000..d1517f0 --- /dev/null +++ b/src/packages/game/input/IZ_midi.c @@ -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); + } +} diff --git a/src/packages/game/input/IZ_midi.h b/src/packages/game/input/IZ_midi.h new file mode 100644 index 0000000..f006cb9 --- /dev/null +++ b/src/packages/game/input/IZ_midi.h @@ -0,0 +1,58 @@ +#ifndef IZ_MIDI_H +#define IZ_MIDI_H + +#include +#include +#include +#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 diff --git a/src/packages/game/main.c b/src/packages/game/main.c index 3d0f15b..606a34d 100644 --- a/src/packages/game/main.c +++ b/src/packages/game/main.c @@ -1,43 +1,7 @@ #include -#include - #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); } diff --git a/src/packages/game/output/IZ_video.c b/src/packages/game/output/IZ_video.c index 4f198b1..27fb142 100644 --- a/src/packages/game/output/IZ_video.c +++ b/src/packages/game/output/IZ_video.c @@ -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, diff --git a/src/packages/game/output/IZ_video.h b/src/packages/game/output/IZ_video.h index d63a588..01c7e82 100644 --- a/src/packages/game/output/IZ_video.h +++ b/src/packages/game/output/IZ_video.h @@ -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*);