Browse Source

Add experimental MIDI controller input support

The game now uses MIDI support for alternative input methods.
feature/data-structs
TheoryOfNekomata 2 years ago
parent
commit
9012772322
15 changed files with 499 additions and 137 deletions
  1. +1
    -0
      .gitignore
  2. +9
    -1
      CMakeLists.txt
  3. +68
    -3
      src/packages/game/IZ_app.c
  4. +4
    -5
      src/packages/game/IZ_app.h
  5. +27
    -7
      src/packages/game/input/IZ_input.c
  6. +12
    -5
      src/packages/game/input/IZ_input.h
  7. +81
    -40
      src/packages/game/input/IZ_joystick.c
  8. +9
    -4
      src/packages/game/input/IZ_joystick.h
  9. +39
    -28
      src/packages/game/input/IZ_keyboard.c
  10. +3
    -3
      src/packages/game/input/IZ_keyboard.h
  11. +181
    -0
      src/packages/game/input/IZ_midi.c
  12. +58
    -0
      src/packages/game/input/IZ_midi.h
  13. +2
    -38
      src/packages/game/main.c
  14. +4
    -2
      src/packages/game/output/IZ_video.c
  15. +1
    -1
      src/packages/game/output/IZ_video.h

+ 1
- 0
.gitignore View File

@@ -60,3 +60,4 @@ $RECYCLE.BIN/
.idea/ .idea/
cmake-build-debug/ cmake-build-debug/
dependencies/ dependencies/
assets/

+ 9
- 1
CMakeLists.txt View File

@@ -43,12 +43,13 @@ add_executable(
src/packages/game/input/IZ_keyboard.h src/packages/game/input/IZ_keyboard.h
src/packages/game/IZ_config.c src/packages/game/IZ_config.c
src/packages/game/IZ_config.h 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( target_link_libraries(
game game
SDL2main SDL2main
SDL2 SDL2
portmidi
) )


add_executable( add_executable(
@@ -109,3 +110,10 @@ if (WIN32)
"${PROJECT_SOURCE_DIR}/dependencies/SDL2/lib/${PROJECT_ARCH}/SDL2.dll" # <--this is in-file "${PROJECT_SOURCE_DIR}/dependencies/SDL2/lib/${PROJECT_ARCH}/SDL2.dll" # <--this is in-file
$<TARGET_FILE_DIR:game>) # <--this is out-file path $<TARGET_FILE_DIR:game>) # <--this is out-file path
endif () 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 ()

+ 68
- 3
src/packages/game/IZ_app.c View File

@@ -17,14 +17,79 @@ IZ_ProcedureResult IZ_InitializeApp(IZ_App* app) {
return 2; 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; return 0;
} }


void IZ_TeardownApp(IZ_App* app) { void IZ_TeardownApp(IZ_App* app) {
IZ_TeardownInput(&app->input_state);
IZ_TeardownVideo(&app->video_state); IZ_TeardownVideo(&app->video_state);
SDL_Quit(); 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;
}

+ 4
- 5
src/packages/game/IZ_app.h View File

@@ -2,20 +2,19 @@
#define IZ_APP_H #define IZ_APP_H


#include <SDL.h> #include <SDL.h>
#include <stdbool.h>
#include "input/IZ_input.h" #include "input/IZ_input.h"
#include "output/IZ_video.h" #include "output/IZ_video.h"
#include "memory/IZ_pool.h" #include "memory/IZ_pool.h"
#include "input/IZ_action.h"


typedef struct { typedef struct {
IZ_InputState input_state[PLAYERS];
IZ_InputState input_state;
IZ_VideoState video_state; IZ_VideoState video_state;


IZ_Pool memory_pool; IZ_Pool memory_pool;
bool quit;
} IZ_App; } IZ_App;


IZ_ProcedureResult IZ_InitializeApp(IZ_App*);

void IZ_TeardownApp(IZ_App*);
IZ_ProcedureResult IZ_RunApp(IZ_App*, uint8_t, char**);


#endif #endif

+ 27
- 7
src/packages/game/input/IZ_input.c View File

@@ -1,18 +1,38 @@
#include "IZ_input.h" #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_HandleJoystickEvents(e, &state->joystick_state, &state->action);
IZ_HandleKeyboardEvents(e, &state->keyboard_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);
} }

+ 12
- 5
src/packages/game/input/IZ_input.h View File

@@ -4,15 +4,22 @@
#include "IZ_action.h" #include "IZ_action.h"
#include "IZ_keyboard.h" #include "IZ_keyboard.h"
#include "IZ_joystick.h" #include "IZ_joystick.h"
#include "IZ_midi.h"


typedef struct { 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; } 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 #endif

+ 81
- 40
src/packages/game/input/IZ_joystick.c View File

@@ -2,18 +2,18 @@


void IZ_HandleJoystickDeviceEvents(SDL_Event e, IZ_JoystickState* state) { void IZ_HandleJoystickDeviceEvents(SDL_Event e, IZ_JoystickState* state) {
if (e.type == SDL_JOYDEVICEADDED) { 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; return;
} }


if (e.type == SDL_JOYDEVICEREMOVED) { if (e.type == SDL_JOYDEVICEREMOVED) {
if ( 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( 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; 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; 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);
}
} }

+ 9
- 4
src/packages/game/input/IZ_joystick.h View File

@@ -8,6 +8,8 @@


typedef uint8_t IZ_PadButton; typedef uint8_t IZ_PadButton;


static const uint16_t IZ_DEFAULT_AXIS_THRESHOLD = 8000;

typedef enum { typedef enum {
IZ_JOY_AXIS_DIRECTION_HORIZONTAL1 = 0, IZ_JOY_AXIS_DIRECTION_HORIZONTAL1 = 0,
IZ_JOY_AXIS_DIRECTION_VERTICAL1 = 1, IZ_JOY_AXIS_DIRECTION_VERTICAL1 = 1,
@@ -17,11 +19,12 @@ typedef enum {


typedef struct { typedef struct {
uint16_t axis_threshold; uint16_t axis_threshold;
SDL_JoystickID device_id;
IZ_PadButton control_mapping[CONTROLS]; IZ_PadButton control_mapping[CONTROLS];
} IZ_JoystickConfig; } IZ_JoystickConfig;


typedef struct { typedef struct {
SDL_Joystick* joystick_instance;
SDL_Joystick* device;
IZ_JoystickConfig config; IZ_JoystickConfig config;
} IZ_JoystickState; } 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 #endif

+ 39
- 28
src/packages/game/input/IZ_keyboard.c View File

@@ -1,6 +1,6 @@
#include "IZ_keyboard.h" #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) { for (uint8_t i = 0; i < CONTROLS; i += 1) {
if (e.key.keysym.sym == state->config.control_mapping[i]) { if (e.key.keysym.sym == state->config.control_mapping[i]) {
const uint16_t bitflag = (0x1 << 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 buffer[128];
char keyboard_section_name[26]; 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);
} }

+ 3
- 3
src/packages/game/input/IZ_keyboard.h View File

@@ -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 #endif

+ 181
- 0
src/packages/game/input/IZ_midi.c View File

@@ -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);
}
}

+ 58
- 0
src/packages/game/input/IZ_midi.h View File

@@ -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

+ 2
- 38
src/packages/game/main.c View File

@@ -1,43 +1,7 @@
#include <SDL.h> #include <SDL.h>
#include <stdbool.h>

#include "IZ_app.h" #include "IZ_app.h"


int main(int argc, char* args[]) {
int main(int argc, char* argv[]) {
IZ_App app; 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);
} }

+ 4
- 2
src/packages/game/output/IZ_video.c View File

@@ -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) { IZ_ProcedureResult IZ_InitializeVideo(const char* config_path, IZ_VideoState* state) {
*state = (IZ_VideoState) {};

IZ_LoadVideoConfig(config_path, &state->config); IZ_LoadVideoConfig(config_path, &state->config);
if (IZ_SaveVideoConfig(config_path, &state->config)) { if (IZ_SaveVideoConfig(config_path, &state->config)) {
fprintf_s(stderr, "Error committing video config.\n"); 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; 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) { if (ticks - video_state->last_update_at > 1000 / video_state->config.max_fps) {
// Update window // Update window
SDL_FillRect(video_state->surface, NULL, SDL_MapRGB(video_state->surface->format, 0x00, 0x00, 0x00)); 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 uint8_t row = i / 4;
const IZ_Action bitflag = (0x1 << i); const IZ_Action bitflag = (0x1 << i);
const uint8_t size = 4; const uint8_t size = 4;
if (input_states[p]->action & bitflag) {
if (input_states->action[p] & bitflag) {
SDL_FillRect(video_state->surface, &(SDL_Rect) { SDL_FillRect(video_state->surface, &(SDL_Rect) {
column * size, column * size,
row * size, row * size,


+ 1
- 1
src/packages/game/output/IZ_video.h View File

@@ -27,7 +27,7 @@ IZ_ProcedureResult IZ_InitializeVideo(const char*, IZ_VideoState*);


IZ_ProcedureResult IZ_SaveVideoConfig(const char*, IZ_VideoConfig*); 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*); void IZ_TeardownVideo(IZ_VideoState*);




Loading…
Cancel
Save