1
0
mirror of https://github.com/libretro/RetroArch synced 2024-07-03 00:38:44 +00:00

Add MIDI support

This commit is contained in:
Zoran Vuckovic 2018-06-04 07:48:08 +02:00
parent 0045e1fff2
commit b487c3cace
32 changed files with 1832 additions and 7 deletions

View File

@ -264,6 +264,8 @@ OBJ += frontend/frontend.o \
$(LIBRETRO_COMM_DIR)/features/features_cpu.o \
performance_counters.o \
verbosity.o \
midi/midi_driver.o \
midi/drivers/null_midi.o
ifeq ($(HAVE_RUNAHEAD), 1)
DEFINES += -DHAVE_RUNAHEAD
@ -686,6 +688,12 @@ ifeq ($(HAVE_XAUDIO), 1)
LIBS += -lole32
endif
ifeq ($(HAVE_WINMM), 1)
OBJ += midi/drivers/winmm_midi.o
DEFINES += -DHAVE_WINMM
LIBS += -lwinmm
endif
# Audio Resamplers
ifeq ($(HAVE_NEON),1)

View File

@ -25,6 +25,7 @@ HAVE_XAUDIO := 1
HAVE_XINPUT := 1
HAVE_WASAPI := 0
HAVE_THREAD_STORAGE := 1
HAVE_WINMM := 1
HAVE_RPNG := 1
HAVE_ZLIB := 1

View File

@ -18,6 +18,7 @@ HAVE_PYTHON = 0
DYNAMIC = 1
HAVE_XINPUT = 1
HAVE_WINMM = 1
HAVE_SDL := 0
HAVE_SDL2 := 0

View File

@ -26,6 +26,7 @@
#include <string/stdstring.h>
#include <streams/file_stream.h>
#include <streams/stdin_stream.h>
#include <midi/midi_driver.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
@ -2040,6 +2041,7 @@ TODO: Add a setting for these tweaks */
command_event_save_auto_state();
break;
case CMD_EVENT_AUDIO_STOP:
midi_driver_set_all_sounds_off();
return audio_driver_stop();
case CMD_EVENT_AUDIO_START:
return audio_driver_start(rarch_ctl(RARCH_CTL_IS_SHUTDOWN, NULL));

View File

@ -689,6 +689,11 @@ static enum resampler_quality audio_resampler_quality_level = RESAMPLER_QUALITY_
static enum resampler_quality audio_resampler_quality_level = RESAMPLER_QUALITY_NORMAL;
#endif
/* MIDI */
static const char *midi_input = "Off";
static const char *midi_output = "Off";
static const unsigned midi_volume = 100;
#if defined(ANDROID)
#if defined(ANDROID_ARM)
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/android/latest/armeabi-v7a/";

View File

@ -52,6 +52,8 @@
#include "tasks/tasks_internal.h"
#include "../list_special.h"
static const char* invalid_filename_chars[] = {
/* https://support.microsoft.com/en-us/help/905231/information-about-the-characters-that-you-cannot-use-in-site-names--fo */
"~", "#", "%", "&", "*", "{", "}", "\\", ":", "[", "]", "?", "/", "|", "\'", "\"",
@ -281,6 +283,11 @@ enum record_driver_enum
RECORD_NULL
};
enum midi_driver_enum
{
MIDI_WINMM = RECORD_NULL + 1,
MIDI_NULL
};
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) || defined(__CELLOS_LV2__)
static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_GL;
@ -386,6 +393,12 @@ static enum record_driver_enum RECORD_DEFAULT_DRIVER = RECORD_FFMPEG;
static enum record_driver_enum RECORD_DEFAULT_DRIVER = RECORD_NULL;
#endif
#ifdef HAVE_WINMM
static enum midi_driver_enum MIDI_DEFAULT_DRIVER = MIDI_WINMM;
#else
static enum midi_driver_enum MIDI_DEFAULT_DRIVER = MIDI_NULL;
#endif
#if defined(XENON)
static enum input_driver_enum INPUT_DEFAULT_DRIVER = INPUT_XENON360;
#elif defined(_XBOX360) || defined(_XBOX) || defined(HAVE_XINPUT2) || defined(HAVE_XINPUT_XBOX1)
@ -1005,6 +1018,26 @@ const char *config_get_default_menu(void)
return "null";
}
const char *config_get_default_midi(void)
{
enum midi_driver_enum default_driver = MIDI_DEFAULT_DRIVER;
switch (default_driver)
{
case MIDI_WINMM:
return "winmm";
case MIDI_NULL:
break;
}
return "null";
}
const char *config_get_midi_driver_options(void)
{
return char_list_new_special(STRING_LIST_MIDI_DRIVERS, NULL);
}
bool config_overlay_enable_default(void)
{
if (g_defaults.overlay.set)
@ -1046,6 +1079,9 @@ static struct config_array_setting *populate_settings_array(settings_t *settings
SETTING_ARRAY("bundle_assets_dst_path_subdir", settings->arrays.bundle_assets_dst_subdir, false, NULL, true);
SETTING_ARRAY("led_driver", settings->arrays.led_driver, false, NULL, true);
SETTING_ARRAY("netplay_mitm_server", settings->arrays.netplay_mitm_server, false, NULL, true);
SETTING_ARRAY("midi_driver", settings->arrays.midi_driver, false, NULL, true);
SETTING_ARRAY("midi_input", settings->arrays.midi_input, true, midi_input, true);
SETTING_ARRAY("midi_output", settings->arrays.midi_output, true, midi_output, true);
*size = count;
return tmp;
@ -1532,6 +1568,8 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings,
SETTING_UINT("run_ahead_frames", &settings->uints.run_ahead_frames, true, 1, false);
SETTING_UINT("midi_volume", &settings->uints.midi_volume, true, midi_volume, false);
*size = count;
return tmp;
@ -1585,6 +1623,7 @@ static void config_set_defaults(void)
const char *def_led = config_get_default_led();
const char *def_location = config_get_default_location();
const char *def_record = config_get_default_record();
const char *def_midi = config_get_default_midi();
const char *def_mitm = netplay_mitm_server;
struct config_float_setting *float_settings = populate_settings_float (settings, &float_settings_size);
struct config_bool_setting *bool_settings = populate_settings_bool (settings, &bool_settings_size);
@ -1665,6 +1704,9 @@ static void config_set_defaults(void)
if (def_record)
strlcpy(settings->arrays.record_driver,
def_record, sizeof(settings->arrays.record_driver));
if (def_midi)
strlcpy(settings->arrays.midi_driver,
def_midi, sizeof(settings->arrays.midi_driver));
if (def_mitm)
strlcpy(settings->arrays.netplay_mitm_server,
def_mitm, sizeof(settings->arrays.netplay_mitm_server));
@ -1982,6 +2024,13 @@ static void config_set_defaults(void)
free(temp_str);
}
if (midi_input)
strlcpy(settings->arrays.midi_input,
midi_input, sizeof(settings->arrays.midi_input));
if (midi_output)
strlcpy(settings->arrays.midi_output,
midi_output, sizeof(settings->arrays.midi_output));
/* Avoid reloading config on every content load */
if (default_block_config_read)
rarch_ctl(RARCH_CTL_SET_BLOCK_CONFIG_READ, NULL);

View File

@ -403,6 +403,8 @@ typedef struct settings
unsigned led_map[MAX_LEDS];
unsigned run_ahead_frames;
unsigned midi_volume;
} uints;
struct
@ -424,6 +426,7 @@ typedef struct settings
char audio_resampler[32];
char input_driver[32];
char input_joypad_driver[32];
char midi_driver[32];
char input_keyboard_layout[64];
@ -437,6 +440,9 @@ typedef struct settings
char bundle_assets_dst_subdir[PATH_MAX_LENGTH];
char netplay_mitm_server[255];
char midi_input[32];
char midi_output[32];
} arrays;
struct
@ -583,6 +589,9 @@ const char *config_get_default_joypad(void);
**/
const char *config_get_default_menu(void);
const char *config_get_default_midi(void);
const char *config_get_midi_driver_options(void);
const char *config_get_default_record(void);
/**

View File

@ -38,6 +38,7 @@
#include "location/location_driver.h"
#include "wifi/wifi_driver.h"
#include "led/led_driver.h"
#include "midi/midi_driver.h"
#include "configuration.h"
#include "core.h"
#include "core_info.h"
@ -113,6 +114,12 @@ static const void *find_driver_nonempty(const char *label, int i,
if (drv)
strlcpy(s, record_driver_find_ident(i), len);
}
else if (string_is_equal(label, "midi_driver"))
{
drv = midi_driver_find_handle(i);
if (drv)
strlcpy(s, midi_driver_find_ident(i), len);
}
else if (string_is_equal(label, "audio_resampler_driver"))
{
drv = audio_resampler_driver_find_handle(i);
@ -396,6 +403,9 @@ void drivers_init(int flags)
{
led_driver_init();
}
if (flags & DRIVER_MIDI_MASK)
midi_driver_init();
}
@ -459,6 +469,9 @@ void driver_uninit(int flags)
if ((flags & DRIVER_AUDIO_MASK) && !audio_driver_owns_driver())
audio_driver_destroy_data();
if (flags & DRIVER_MIDI_MASK)
midi_driver_free();
}
bool driver_ctl(enum driver_ctl_state state, void *data)

View File

@ -35,7 +35,8 @@ RETRO_BEGIN_DECLS
| DRIVER_MENU_MASK \
| DRIVERS_VIDEO_INPUT_MASK \
| DRIVER_WIFI_MASK \
| DRIVER_LED_MASK )
| DRIVER_LED_MASK \
| DRIVER_MIDI_MASK )
#define DRIVERS_CMD_ALL_BUT_MENU \
( DRIVER_AUDIO_MASK \
@ -45,7 +46,8 @@ RETRO_BEGIN_DECLS
| DRIVER_LOCATION_MASK \
| DRIVERS_VIDEO_INPUT_MASK \
| DRIVER_WIFI_MASK \
| DRIVER_LED_MASK )
| DRIVER_LED_MASK \
| DRIVER_MIDI_MASK )
enum
{
@ -57,7 +59,8 @@ enum
DRIVER_MENU,
DRIVERS_VIDEO_INPUT,
DRIVER_WIFI,
DRIVER_LED
DRIVER_LED,
DRIVER_MIDI
};
enum
@ -70,7 +73,8 @@ enum
DRIVER_MENU_MASK = 1 << DRIVER_MENU,
DRIVERS_VIDEO_INPUT_MASK = 1 << DRIVERS_VIDEO_INPUT,
DRIVER_WIFI_MASK = 1 << DRIVER_WIFI,
DRIVER_LED_MASK = 1 << DRIVER_LED
DRIVER_LED_MASK = 1 << DRIVER_LED,
DRIVER_MIDI_MASK = 1 << DRIVER_MIDI
};
enum driver_ctl_state

View File

@ -55,6 +55,7 @@
#include "performance_counters.h"
#include "gfx/video_driver.h"
#include "led/led_driver.h"
#include "midi/midi_driver.h"
#include "cores/internal_cores.h"
#include "frontend/frontend_driver.h"
@ -1778,6 +1779,22 @@ bool rarch_environment_cb(unsigned cmd, void *data)
}
}
break;
case RETRO_ENVIRONMENT_GET_MIDI_INTERFACE:
{
struct retro_midi_interface *midi_interface =
(struct retro_midi_interface *)data;
if (midi_interface)
{
midi_interface->input_enabled = midi_driver_input_enabled;
midi_interface->output_enabled = midi_driver_output_enabled;
midi_interface->read = midi_driver_read;
midi_interface->write = midi_driver_write;
midi_interface->flush = midi_driver_flush;
}
}
break;
default:
RARCH_LOG("Environ UNSUPPORTED (#%u).\n", cmd);

View File

@ -333,6 +333,8 @@ MSG_HASH(MENU_ENUM_LABEL_DEFERRED_PLAYLIST_SETTINGS_LIST,
"deferred_playlist_settings")
MSG_HASH(MENU_ENUM_LABEL_DEFERRED_PRIVACY_SETTINGS_LIST,
"deferred_privacy_settings_list")
MSG_HASH(MENU_ENUM_LABEL_DEFERRED_MIDI_SETTINGS_LIST,
"deferred_midi_settings_list")
MSG_HASH(MENU_ENUM_LABEL_DEFERRED_RDB_ENTRY_DETAIL,
"deferred_rdb_entry_detail")
MSG_HASH(MENU_ENUM_LABEL_DEFERRED_RECORDING_SETTINGS_LIST,
@ -789,6 +791,8 @@ MSG_HASH(MENU_ENUM_LABEL_POINTER_ENABLE,
"menu_pointer_enable")
MSG_HASH(MENU_ENUM_LABEL_PRIVACY_SETTINGS,
"privacy_settings")
MSG_HASH(MENU_ENUM_LABEL_MIDI_SETTINGS,
"midi_settings")
MSG_HASH(MENU_ENUM_LABEL_QUIT_RETROARCH,
"quit_retroarch")
MSG_HASH(MENU_ENUM_LABEL_RDB_ENTRY,
@ -889,6 +893,8 @@ MSG_HASH(MENU_ENUM_LABEL_RECORD_CONFIG,
"record_config")
MSG_HASH(MENU_ENUM_LABEL_RECORD_DRIVER,
"record_driver")
MSG_HASH(MENU_ENUM_LABEL_MIDI_DRIVER,
"midi_driver")
MSG_HASH(MENU_ENUM_LABEL_RECORD_ENABLE,
"record_enable")
MSG_HASH(MENU_ENUM_LABEL_RECORD_PATH,
@ -1517,3 +1523,9 @@ MSG_HASH(MENU_ENUM_LABEL_DEFERRED_QUICK_MENU_OVERRIDE_OPTIONS,
"deferred_quick_menu_override_options")
MSG_HASH(MENU_ENUM_LABEL_DISCORD_IN_MENU,
"discord_in_menu")
MSG_HASH(MENU_ENUM_LABEL_MIDI_INPUT,
"midi_input")
MSG_HASH(MENU_ENUM_LABEL_MIDI_OUTPUT,
"midi_output")
MSG_HASH(MENU_ENUM_LABEL_MIDI_VOLUME,
"midi_volume")

View File

@ -2037,6 +2037,30 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len)
snprintf(s, len,
"Sets the blue value of the OSD text color. Valid values are between 0 and 255.");
break;
case MENU_ENUM_LABEL_MIDI_DRIVER:
snprintf(s, len,
"MIDI driver to use.");
break;
case MENU_ENUM_LABEL_MIDI_INPUT:
snprintf(s, len,
"Sets the input device (driver specific).\n"
"When set to \"Off\", MIDI input will be disabled.\n"
"Device name can also be typed in.");
break;
case MENU_ENUM_LABEL_MIDI_OUTPUT:
snprintf(s, len,
"Sets the output device (driver specific).\n"
"When set to \"Off\", MIDI output will be disabled.\n"
"Device name can also be typed in.\n"
" \n"
"When MIDI output is enabled and core and game/app support MIDI output,\n"
"some or all sounds (depends on game/app) will be generated by MIDI device.\n"
"In case of \"null\" MIDI driver this means that those sounds won't be audible.");
break;
case MENU_ENUM_LABEL_MIDI_VOLUME:
snprintf(s, len,
"Sets the master volume of the output device.");
break;
default:
if (string_is_empty(s))
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE), len);

View File

@ -1261,6 +1261,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_PRESENT,
"Present")
MSG_HASH(MENU_ENUM_LABEL_VALUE_PRIVACY_SETTINGS,
"Privacy")
MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_SETTINGS,
"MIDI")
MSG_HASH(MENU_ENUM_LABEL_VALUE_QUIT_RETROARCH,
"Quit RetroArch")
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ANALOG,
@ -1331,6 +1333,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_CONFIG,
"Load Recording Config...")
MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_DRIVER,
"Record Driver")
MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_DRIVER,
"MIDI Driver")
MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_ENABLE,
"Enable Recording")
MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_PATH,
@ -1977,6 +1981,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_USER_SETTINGS,
"Change account, username, and language settings.")
MSG_HASH(MENU_ENUM_SUBLABEL_PRIVACY_SETTINGS,
"Change your privacy settings.")
MSG_HASH(MENU_ENUM_SUBLABEL_MIDI_SETTINGS,
"Change MIDI settings.")
MSG_HASH(MENU_ENUM_SUBLABEL_DIRECTORY_SETTINGS,
"Change default directories where files are located.")
MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_SETTINGS,
@ -2864,6 +2870,10 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_RECORD_DRIVER,
"Record driver to use."
)
MSG_HASH(
MENU_ENUM_SUBLABEL_MIDI_DRIVER,
"MIDI driver to use."
)
MSG_HASH(
MENU_ENUM_SUBLABEL_WIFI_DRIVER,
"WiFi driver to use."
@ -3726,3 +3736,15 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_DISCORD_ALLOW,
"Enable or disable Discord support. Will not work with the browser version, only native desktop client."
)
MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_INPUT,
"Input")
MSG_HASH(MENU_ENUM_SUBLABEL_MIDI_INPUT,
"Select input device.")
MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_OUTPUT,
"Output")
MSG_HASH(MENU_ENUM_SUBLABEL_MIDI_OUTPUT,
"Select output device.")
MSG_HASH(MENU_ENUM_LABEL_VALUE_MIDI_VOLUME,
"Volume")
MSG_HASH(MENU_ENUM_SUBLABEL_MIDI_VOLUME,
"Set output volume (%).")

View File

@ -1163,6 +1163,23 @@ struct retro_led_interface
* * State will never be saved when using Hard Disable Audio.
*/
#define RETRO_ENVIRONMENT_GET_MIDI_INTERFACE (48 | RETRO_ENVIRONMENT_EXPERIMENTAL)
typedef bool (RETRO_CALLCONV *retro_midi_input_enabled_t)(void);
typedef bool (RETRO_CALLCONV *retro_midi_output_enabled_t)(void);
typedef bool (RETRO_CALLCONV *retro_midi_read_t)(uint8_t *byte);
typedef bool (RETRO_CALLCONV *retro_midi_write_t)(uint8_t byte, uint32_t delta_time);
typedef bool (RETRO_CALLCONV *retro_midi_flush_t)(void);
struct retro_midi_interface
{
retro_midi_input_enabled_t input_enabled;
retro_midi_output_enabled_t output_enabled;
retro_midi_read_t read;
retro_midi_write_t write;
retro_midi_flush_t flush;
};
#define RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE (41 | RETRO_ENVIRONMENT_EXPERIMENTAL)
/* const struct retro_hw_render_interface ** --
* Returns an API specific rendering interface for accessing API specific data.

View File

@ -50,6 +50,7 @@
#include "input/input_driver.h"
#include "audio/audio_driver.h"
#include "record/record_driver.h"
#include "midi/midi_driver.h"
#include "configuration.h"
struct string_list *dir_list_new_special(const char *input_dir,
@ -274,6 +275,15 @@ struct string_list *string_list_new_special(enum string_list_type type,
string_list_append(s, opt, attr);
}
break;
case STRING_LIST_MIDI_DRIVERS:
for (i = 0; midi_driver_find_handle(i); i++)
{
const char *opt = midi_driver_find_ident(i);
*len += strlen(opt) + 1;
string_list_append(s, opt, attr);
}
break;
case STRING_LIST_SUPPORTED_CORES_PATHS:
core_info_get_list(&core_info_list);

View File

@ -53,6 +53,7 @@ enum string_list_type
STRING_LIST_INPUT_JOYPAD_DRIVERS,
STRING_LIST_INPUT_HID_DRIVERS,
STRING_LIST_RECORD_DRIVERS,
STRING_LIST_MIDI_DRIVERS,
STRING_LIST_SUPPORTED_CORES_PATHS,
STRING_LIST_SUPPORTED_CORES_NAMES
};

View File

@ -155,6 +155,7 @@ generic_deferred_push(deferred_push_lakka_services_list, DISPLAYLIST_
generic_deferred_push(deferred_push_user_settings_list, DISPLAYLIST_USER_SETTINGS_LIST)
generic_deferred_push(deferred_push_directory_settings_list, DISPLAYLIST_DIRECTORY_SETTINGS_LIST)
generic_deferred_push(deferred_push_privacy_settings_list, DISPLAYLIST_PRIVACY_SETTINGS_LIST)
generic_deferred_push(deferred_push_midi_settings_list, DISPLAYLIST_MIDI_SETTINGS_LIST)
generic_deferred_push(deferred_push_audio_settings_list, DISPLAYLIST_AUDIO_SETTINGS_LIST)
generic_deferred_push(deferred_push_audio_mixer_settings_list, DISPLAYLIST_AUDIO_MIXER_SETTINGS_LIST)
generic_deferred_push(deferred_push_input_settings_list, DISPLAYLIST_INPUT_SETTINGS_LIST)
@ -731,6 +732,12 @@ static int menu_cbs_init_bind_deferred_push_compare_label(
return 0;
}
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_MIDI_SETTINGS_LIST)))
{
BIND_ACTION_DEFERRED_PUSH(cbs, deferred_push_midi_settings_list);
return 0;
}
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_CORE_CONTENT_DIRS_LIST)))
{
#ifdef HAVE_NETWORKING

View File

@ -1971,6 +1971,7 @@ static int menu_cbs_init_bind_get_string_representation_compare_label(
case MENU_ENUM_LABEL_JOYPAD_DRIVER:
case MENU_ENUM_LABEL_AUDIO_RESAMPLER_DRIVER:
case MENU_ENUM_LABEL_RECORD_DRIVER:
case MENU_ENUM_LABEL_MIDI_DRIVER:
case MENU_ENUM_LABEL_LOCATION_DRIVER:
case MENU_ENUM_LABEL_CAMERA_DRIVER:
case MENU_ENUM_LABEL_WIFI_DRIVER:

View File

@ -316,6 +316,8 @@ static enum msg_hash_enums action_ok_dl_to_enum(unsigned lbl)
return MENU_ENUM_LABEL_DEFERRED_DIRECTORY_SETTINGS_LIST;
case ACTION_OK_DL_PRIVACY_SETTINGS_LIST:
return MENU_ENUM_LABEL_DEFERRED_PRIVACY_SETTINGS_LIST;
case ACTION_OK_DL_MIDI_SETTINGS_LIST:
return MENU_ENUM_LABEL_DEFERRED_MIDI_SETTINGS_LIST;
case ACTION_OK_DL_AUDIO_SETTINGS_LIST:
return MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST;
case ACTION_OK_DL_AUDIO_MIXER_SETTINGS_LIST:
@ -842,6 +844,7 @@ int generic_action_ok_displaylist_push(const char *path,
case ACTION_OK_DL_USER_SETTINGS_LIST:
case ACTION_OK_DL_DIRECTORY_SETTINGS_LIST:
case ACTION_OK_DL_PRIVACY_SETTINGS_LIST:
case ACTION_OK_DL_MIDI_SETTINGS_LIST:
case ACTION_OK_DL_AUDIO_SETTINGS_LIST:
case ACTION_OK_DL_AUDIO_MIXER_SETTINGS_LIST:
case ACTION_OK_DL_INPUT_HOTKEY_BINDS_LIST:
@ -3420,6 +3423,7 @@ default_action_ok_func(action_ok_user_list, ACTION_OK_DL_USER_SETTINGS_LIST)
default_action_ok_func(action_ok_netplay_sublist, ACTION_OK_DL_NETPLAY)
default_action_ok_func(action_ok_directory_list, ACTION_OK_DL_DIRECTORY_SETTINGS_LIST)
default_action_ok_func(action_ok_privacy_list, ACTION_OK_DL_PRIVACY_SETTINGS_LIST)
default_action_ok_func(action_ok_midi_list, ACTION_OK_DL_MIDI_SETTINGS_LIST)
default_action_ok_func(action_ok_rdb_entry, ACTION_OK_DL_RDB_ENTRY)
default_action_ok_func(action_ok_mixer_stream_actions, ACTION_OK_DL_MIXER_STREAM_SETTINGS_LIST)
default_action_ok_func(action_ok_browse_url_list, ACTION_OK_DL_BROWSE_URL_LIST)
@ -4662,6 +4666,9 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_PRIVACY_SETTINGS:
BIND_ACTION_OK(cbs, action_ok_privacy_list);
break;
case MENU_ENUM_LABEL_MIDI_SETTINGS:
BIND_ACTION_OK(cbs, action_ok_midi_list);
break;
case MENU_ENUM_LABEL_SCREEN_RESOLUTION:
BIND_ACTION_OK(cbs, action_ok_video_resolution);
break;

View File

@ -75,6 +75,7 @@ default_sublabel_macro(action_bind_sublabel_saving_settings_list, MENU_
default_sublabel_macro(action_bind_sublabel_logging_settings_list, MENU_ENUM_SUBLABEL_LOGGING_SETTINGS)
default_sublabel_macro(action_bind_sublabel_user_interface_settings_list, MENU_ENUM_SUBLABEL_USER_INTERFACE_SETTINGS)
default_sublabel_macro(action_bind_sublabel_privacy_settings_list, MENU_ENUM_SUBLABEL_PRIVACY_SETTINGS)
default_sublabel_macro(action_bind_sublabel_midi_settings_list, MENU_ENUM_SUBLABEL_MIDI_SETTINGS)
default_sublabel_macro(action_bind_sublabel_directory_settings_list, MENU_ENUM_SUBLABEL_DIRECTORY_SETTINGS)
default_sublabel_macro(action_bind_sublabel_playlist_settings_list, MENU_ENUM_SUBLABEL_PLAYLIST_SETTINGS)
default_sublabel_macro(action_bind_sublabel_network_settings_list, MENU_ENUM_SUBLABEL_NETWORK_SETTINGS)
@ -222,6 +223,7 @@ default_sublabel_macro(action_bind_sublabel_camera_driver, MENU_
default_sublabel_macro(action_bind_sublabel_location_driver, MENU_ENUM_SUBLABEL_LOCATION_DRIVER)
default_sublabel_macro(action_bind_sublabel_menu_driver, MENU_ENUM_SUBLABEL_MENU_DRIVER)
default_sublabel_macro(action_bind_sublabel_record_driver, MENU_ENUM_SUBLABEL_RECORD_DRIVER)
default_sublabel_macro(action_bind_sublabel_midi_driver, MENU_ENUM_SUBLABEL_MIDI_DRIVER)
default_sublabel_macro(action_bind_sublabel_wifi_driver, MENU_ENUM_SUBLABEL_WIFI_DRIVER)
default_sublabel_macro(action_bind_sublabel_filter_supported_extensions, MENU_ENUM_SUBLABEL_NAVIGATION_BROWSER_FILTER_SUPPORTED_EXTENSIONS_ENABLE)
default_sublabel_macro(action_bind_sublabel_wallpaper, MENU_ENUM_SUBLABEL_MENU_WALLPAPER)
@ -421,6 +423,9 @@ default_sublabel_macro(action_bind_sublabel_netplay_mitm_server,
default_sublabel_macro(action_bind_sublabel_core_delete, MENU_ENUM_SUBLABEL_CORE_DELETE)
default_sublabel_macro(action_bind_sublabel_pause_hardcode_mode, MENU_ENUM_SUBLABEL_ACHIEVEMENT_PAUSE)
default_sublabel_macro(action_bind_sublabel_resume_hardcode_mode, MENU_ENUM_SUBLABEL_ACHIEVEMENT_RESUME)
default_sublabel_macro(action_bind_sublabel_midi_input, MENU_ENUM_SUBLABEL_MIDI_INPUT)
default_sublabel_macro(action_bind_sublabel_midi_output, MENU_ENUM_SUBLABEL_MIDI_OUTPUT)
default_sublabel_macro(action_bind_sublabel_midi_volume, MENU_ENUM_SUBLABEL_MIDI_VOLUME)
static int action_bind_sublabel_cheevos_entry(
file_list_t *list,
@ -1256,6 +1261,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_RECORD_DRIVER:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_record_driver);
break;
case MENU_ENUM_LABEL_MIDI_DRIVER:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_midi_driver);
break;
case MENU_ENUM_LABEL_MENU_DRIVER:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_driver);
break;
@ -1680,6 +1688,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_PRIVACY_SETTINGS:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_privacy_settings_list);
break;
case MENU_ENUM_LABEL_MIDI_SETTINGS:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_midi_settings_list);
break;
case MENU_ENUM_LABEL_DIRECTORY_SETTINGS:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_directory_settings_list);
break;
@ -1758,6 +1769,15 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_ACHIEVEMENT_RESUME:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_resume_hardcode_mode);
break;
case MENU_ENUM_LABEL_MIDI_INPUT:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_midi_input);
break;
case MENU_ENUM_LABEL_MIDI_OUTPUT:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_midi_output);
break;
case MENU_ENUM_LABEL_MIDI_VOLUME:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_midi_volume);
break;
default:
case MSG_UNKNOWN:
return -1;

View File

@ -133,6 +133,7 @@ default_title_macro(action_get_lakka_services_list, MENU_ENUM_LABEL_
default_title_macro(action_get_user_settings_list, MENU_ENUM_LABEL_VALUE_USER_SETTINGS)
default_title_macro(action_get_directory_settings_list, MENU_ENUM_LABEL_VALUE_DIRECTORY_SETTINGS)
default_title_macro(action_get_privacy_settings_list, MENU_ENUM_LABEL_VALUE_PRIVACY_SETTINGS)
default_title_macro(action_get_midi_settings_list, MENU_ENUM_LABEL_VALUE_MIDI_SETTINGS)
default_title_macro(action_get_updater_settings_list, MENU_ENUM_LABEL_VALUE_UPDATER_SETTINGS)
default_title_macro(action_get_audio_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_SETTINGS)
default_title_macro(action_get_audio_mixer_settings_list, MENU_ENUM_LABEL_VALUE_AUDIO_MIXER_SETTINGS)
@ -448,6 +449,11 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs,
BIND_ACTION_GET_TITLE(cbs, action_get_privacy_settings_list);
return 0;
}
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_MIDI_SETTINGS_LIST)))
{
BIND_ACTION_GET_TITLE(cbs, action_get_midi_settings_list);
return 0;
}
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_CORE_CONTENT_DIRS_LIST)))
{
BIND_ACTION_GET_TITLE(cbs, action_get_download_core_content_list);

View File

@ -2679,6 +2679,8 @@ static void materialui_list_insert(void *userdata,
||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_PRIVACY_SETTINGS))
||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_MIDI_SETTINGS))
||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_MENU_VIEWS_SETTINGS))
||
string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_QUICK_MENU_VIEWS_SETTINGS))

View File

@ -127,6 +127,7 @@ enum
ACTION_OK_DL_USER_SETTINGS_LIST,
ACTION_OK_DL_DIRECTORY_SETTINGS_LIST,
ACTION_OK_DL_PRIVACY_SETTINGS_LIST,
ACTION_OK_DL_MIDI_SETTINGS_LIST,
ACTION_OK_DL_BROWSE_URL_START,
ACTION_OK_DL_CONTENT_SETTINGS
};

View File

@ -4966,6 +4966,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
ret = menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_RECORD_DRIVER,
PARSE_ONLY_STRING_OPTIONS, false);
ret = menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_MIDI_DRIVER,
PARSE_ONLY_STRING_OPTIONS, false);
#ifdef HAVE_LAKKA
ret = menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_WIFI_DRIVER,
@ -5942,6 +5945,22 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
MENU_ENUM_LABEL_NO_SETTINGS_FOUND,
0, 0, 0);
info->need_refresh = true;
info->need_push = true;
break;
case DISPLAYLIST_MIDI_SETTINGS_LIST:
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_MIDI_INPUT,
PARSE_ONLY_STRING, false);
menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_MIDI_OUTPUT,
PARSE_ONLY_STRING, false);
menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_MIDI_VOLUME,
PARSE_ONLY_UINT, false);
info->need_refresh = true;
info->need_push = true;
break;
@ -6365,6 +6384,8 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
MENU_ENUM_LABEL_DIRECTORY_SETTINGS, PARSE_ACTION, false);
ret = menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_PRIVACY_SETTINGS, PARSE_ACTION, false);
ret = menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_MIDI_SETTINGS, PARSE_ACTION, false);
info->need_push = true;
break;
case DISPLAYLIST_HORIZONTAL:

View File

@ -141,6 +141,7 @@ enum menu_displaylist_ctl_state
DISPLAYLIST_USER_SETTINGS_LIST,
DISPLAYLIST_DIRECTORY_SETTINGS_LIST,
DISPLAYLIST_PRIVACY_SETTINGS_LIST,
DISPLAYLIST_MIDI_SETTINGS_LIST,
DISPLAYLIST_RECORDING_SETTINGS_LIST,
DISPLAYLIST_PLAYLIST_SETTINGS_LIST,
DISPLAYLIST_ACCOUNTS_CHEEVOS_LIST,

View File

@ -76,6 +76,7 @@
#include "../record/record_driver.h"
#include "../audio/audio_driver.h"
#include "../input/input_driver.h"
#include "../midi/midi_driver.h"
#include "../tasks/tasks_internal.h"
#include "../config.def.h"
#include "../ui/ui_companion_driver.h"
@ -122,7 +123,8 @@ enum settings_list_type
SETTINGS_LIST_USER_ACCOUNTS,
SETTINGS_LIST_USER_ACCOUNTS_CHEEVOS,
SETTINGS_LIST_DIRECTORY,
SETTINGS_LIST_PRIVACY
SETTINGS_LIST_PRIVACY,
SETTINGS_LIST_MIDI
};
struct bool_entry
@ -347,6 +349,90 @@ static int setting_string_action_right_driver(void *data,
return 0;
}
int setting_string_action_left_midi_input(void *data, bool wraparound)
{
struct string_list *list = midi_driver_get_avail_inputs();
if (list && list->size > 1)
{
rarch_setting_t *setting = (rarch_setting_t*)data;
int i = string_list_find_elem(list, setting->value.target.string) - 2;
if (wraparound && i == -1)
i = (int)list->size - 1;
if (i >= 0)
{
strlcpy(setting->value.target.string, list->elems[i].data, setting->size);
return 0;
}
}
return -1;
}
int setting_string_action_right_midi_input(void *data, bool wraparound)
{
struct string_list *list = midi_driver_get_avail_inputs();
if (list && list->size > 1)
{
rarch_setting_t *setting = (rarch_setting_t*)data;
int i = string_list_find_elem(list, setting->value.target.string);
if (wraparound && i == (int)list->size)
i = 0;
if (i >= 0 && i < (int)list->size)
{
strlcpy(setting->value.target.string, list->elems[i].data, setting->size);
return 0;
}
}
return -1;
}
int setting_string_action_left_midi_output(void *data, bool wraparound)
{
struct string_list *list = midi_driver_get_avail_outputs();
if (list && list->size > 1)
{
rarch_setting_t *setting = (rarch_setting_t*)data;
int i = string_list_find_elem(list, setting->value.target.string) - 2;
if (wraparound && i == -1)
i = (int)list->size - 1;
if (i >= 0)
{
strlcpy(setting->value.target.string, list->elems[i].data, setting->size);
return 0;
}
}
return -1;
}
int setting_string_action_right_midi_output(void *data, bool wraparound)
{
struct string_list *list = midi_driver_get_avail_outputs();
if (list && list->size > 1)
{
rarch_setting_t *setting = (rarch_setting_t*)data;
int i = string_list_find_elem(list, setting->value.target.string);
if (wraparound && i == (int)list->size)
i = 0;
if (i >= 0 && i < (int)list->size)
{
strlcpy(setting->value.target.string, list->elems[i].data, setting->size);
return 0;
}
}
return -1;
}
static void setting_get_string_representation_uint_video_rotation(void *data,
char *s, size_t len)
{
@ -1537,6 +1623,15 @@ void general_write_handler(void *data)
case MENU_ENUM_LABEL_VIDEO_WINDOW_SHOW_DECORATIONS:
video_display_server_set_window_decorations(settings->bools.video_window_show_decorations);
break;
case MENU_ENUM_LABEL_MIDI_INPUT:
midi_driver_set_input(settings->arrays.midi_input);
break;
case MENU_ENUM_LABEL_MIDI_OUTPUT:
midi_driver_set_output(settings->arrays.midi_output);
break;
case MENU_ENUM_LABEL_MIDI_VOLUME:
midi_driver_set_volume(settings->uints.midi_volume);
break;
default:
break;
}
@ -2495,6 +2590,14 @@ static bool setting_append_list(
&subgroup_info,
parent_group);
CONFIG_ACTION(
list, list_info,
MENU_ENUM_LABEL_MIDI_SETTINGS,
MENU_ENUM_LABEL_VALUE_MIDI_SETTINGS,
&group_info,
&subgroup_info,
parent_group);
for (user = 0; user < MAX_USERS; user++)
setting_append_list_input_player_options(list, list_info, parent_group, user);
@ -2504,7 +2607,7 @@ static bool setting_append_list(
case SETTINGS_LIST_DRIVERS:
{
unsigned i;
struct string_options_entry string_options_entries[10];
struct string_options_entry string_options_entries[11];
START_GROUP(list, list_info, &group_info, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DRIVER_SETTINGS), parent_group);
menu_settings_list_current_add_enum_idx(list, list_info, MENU_ENUM_LABEL_DRIVER_SETTINGS);
@ -2584,6 +2687,13 @@ static bool setting_append_list(
string_options_entries[9].default_value = config_get_default_record();
string_options_entries[9].values = config_get_record_driver_options();
string_options_entries[10].target = settings->arrays.midi_driver;
string_options_entries[10].len = sizeof(settings->arrays.midi_driver);
string_options_entries[10].name_enum_idx = MENU_ENUM_LABEL_MIDI_DRIVER;
string_options_entries[10].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_MIDI_DRIVER;
string_options_entries[10].default_value = config_get_default_midi();
string_options_entries[10].values = config_get_midi_driver_options();
for (i = 0; i < ARRAY_SIZE(string_options_entries); i++)
{
CONFIG_STRING_OPTIONS(
@ -7907,6 +8017,63 @@ static bool setting_append_list(
SD_FLAG_NONE);
}
END_SUB_GROUP(list, list_info, parent_group);
END_GROUP(list, list_info, parent_group);
break;
case SETTINGS_LIST_MIDI:
START_GROUP(list, list_info, &group_info,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MIDI_SETTINGS), parent_group);
parent_group = msg_hash_to_str(MENU_ENUM_LABEL_MIDI_SETTINGS);
START_SUB_GROUP(list, list_info, "State",
&group_info, &subgroup_info, parent_group);
CONFIG_STRING(
list, list_info,
settings->arrays.midi_input,
sizeof(settings->arrays.midi_input),
MENU_ENUM_LABEL_MIDI_INPUT,
MENU_ENUM_LABEL_VALUE_MIDI_INPUT,
midi_input,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler);
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ALLOW_INPUT);
(*list)[list_info->index - 1].action_left = setting_string_action_left_midi_input;
(*list)[list_info->index - 1].action_right = setting_string_action_right_midi_input;
CONFIG_STRING(
list, list_info,
settings->arrays.midi_output,
sizeof(settings->arrays.midi_output),
MENU_ENUM_LABEL_MIDI_OUTPUT,
MENU_ENUM_LABEL_VALUE_MIDI_OUTPUT,
midi_output,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler);
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ALLOW_INPUT);
(*list)[list_info->index - 1].action_left = setting_string_action_left_midi_output;
(*list)[list_info->index - 1].action_right = setting_string_action_right_midi_output;
CONFIG_UINT(
list, list_info,
&settings->uints.midi_volume,
MENU_ENUM_LABEL_MIDI_VOLUME,
MENU_ENUM_LABEL_VALUE_MIDI_VOLUME,
midi_volume,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler);
menu_settings_list_current_add_range(list, list_info, 0.0f, 100.0f, 1.0f, true, true);
END_SUB_GROUP(list, list_info, parent_group);
END_GROUP(list, list_info, parent_group);
break;
@ -8028,7 +8195,8 @@ static rarch_setting_t *menu_setting_new_internal(rarch_setting_info_t *list_inf
SETTINGS_LIST_USER_ACCOUNTS,
SETTINGS_LIST_USER_ACCOUNTS_CHEEVOS,
SETTINGS_LIST_DIRECTORY,
SETTINGS_LIST_PRIVACY
SETTINGS_LIST_PRIVACY,
SETTINGS_LIST_MIDI
};
const char *root = msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU);
rarch_setting_t *list = (rarch_setting_t*)calloc(

98
midi/drivers/null_midi.c Normal file
View File

@ -0,0 +1,98 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2018 The RetroArch team
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <libretro.h>
#include <lists/string_list.h>
#include "../midi_driver.h"
static bool null_midi_get_avail_inputs(struct string_list *inputs)
{
union string_list_elem_attr attr = {0};
return string_list_append(inputs, "Null", attr);
}
static bool null_midi_get_avail_outputs(struct string_list *outputs)
{
union string_list_elem_attr attr = {0};
return string_list_append(outputs, "Null", attr);
}
static void *null_midi_init(const char *input, const char *output)
{
(void)input;
(void)output;
return (void*)-1;
}
static void null_midi_free(void *p)
{
(void)p;
}
static bool null_midi_set_input(void *p, const char *input)
{
(void)p;
return input == NULL || !strcmp(input, "Null");
}
static bool null_midi_set_output(void *p, const char *output)
{
(void)p;
return output == NULL || !strcmp(output, "Null");
}
static bool null_midi_read(void *p, midi_event_t *event)
{
(void)p;
(void)event;
return false;
}
static bool null_midi_write(void *p, const midi_event_t *event)
{
(void)p;
(void)event;
return true;
}
static bool null_midi_flush(void *p)
{
(void)p;
return true;
}
midi_driver_t midi_null = {
"null",
null_midi_get_avail_inputs,
null_midi_get_avail_outputs,
null_midi_init,
null_midi_free,
null_midi_set_input,
null_midi_set_output,
null_midi_read,
null_midi_write,
null_midi_flush
};

628
midi/drivers/winmm_midi.c Normal file
View File

@ -0,0 +1,628 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2018 The RetroArch team
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <windows.h>
#include <libretro.h>
#include <lists/string_list.h>
#include <verbosity.h>
#include "../midi_driver.h"
#define WINMM_MIDI_BUF_CNT 3
#define WINMM_MIDI_BUF_LEN 1024
typedef struct
{
MIDIHDR header;
DWORD data[WINMM_MIDI_BUF_LEN];
} winmm_midi_buffer_t;
typedef struct
{
uint8_t data[WINMM_MIDI_BUF_LEN * 4];
midi_event_t events[WINMM_MIDI_BUF_LEN];
int rd_idx;
int wr_idx;
} winmm_midi_queue_t;
typedef struct
{
HMIDIIN in_dev;
HMIDISTRM out_dev;
winmm_midi_queue_t in_queue;
winmm_midi_buffer_t out_bufs[WINMM_MIDI_BUF_CNT];
int out_buf_idx;
double tick_dur;
} winmm_midi_t;
static void winmm_midi_free(void *p);
static bool winmm_midi_queue_read(winmm_midi_queue_t *q, midi_event_t *ev)
{
int i;
midi_event_t *src_ev;
if (q->rd_idx == q->wr_idx)
return false;
if (ev->data_size < q->events[q->rd_idx].data_size)
{
#ifdef DEBUG
RARCH_ERR("[MIDI]: Input queue read failed (event data too small).\n");
#endif
return false;
}
src_ev = &q->events[q->rd_idx];
for (i = 0; i < src_ev->data_size; ++i)
ev->data[i] = src_ev->data[i];
ev->data_size = src_ev->data_size;
ev->delta_time = src_ev->delta_time;
if (q->rd_idx + 1 == WINMM_MIDI_BUF_LEN)
q->rd_idx = 0;
else
++q->rd_idx;
return true;
}
static bool winmm_midi_queue_write(winmm_midi_queue_t *q, const midi_event_t *ev)
{
int rd_idx = q->rd_idx;
midi_event_t *dest_ev;
int wr_avail;
int i;
if (q->wr_idx >= rd_idx)
wr_avail = WINMM_MIDI_BUF_LEN - q->wr_idx + rd_idx;
else
wr_avail = rd_idx - q->wr_idx - 1;
if (wr_avail < 1)
{
#ifdef DEBUG
RARCH_ERR("[MIDI]: Input queue overflow.\n");
#endif
return false;
}
dest_ev = &q->events[q->wr_idx];
if (ev->data_size > 4)
{
#ifdef DEBUG
RARCH_ERR("[MIDI]: Input queue write failed (event too big).\n");
#endif
return false;
}
for (i = 0; i < ev->data_size; ++i)
dest_ev->data[i] = ev->data[i];
dest_ev->data_size = ev->data_size;
dest_ev->delta_time = ev->delta_time;
if (q->wr_idx + 1 == WINMM_MIDI_BUF_LEN)
q->wr_idx = 0;
else
++q->wr_idx;
return true;
}
static void winmm_midi_queue_init(winmm_midi_queue_t *q)
{
int i, j;
for (i = j = 0; i < WINMM_MIDI_BUF_LEN; ++i, j += 4)
{
q->events[i].data = &q->data[j];
q->events[i].delta_time = 0;
}
q->rd_idx = 0;
q->wr_idx = 0;
}
static void CALLBACK winmm_midi_input_callback(HMIDIIN dev, UINT msg,
DWORD_PTR q, DWORD_PTR par1, DWORD_PTR par2)
{
winmm_midi_queue_t *queue = (winmm_midi_queue_t*)q;
midi_event_t event;
uint8_t data[3];
(void)dev;
if (msg == MIM_OPEN)
winmm_midi_queue_init(queue);
else if (msg == MIM_DATA)
{
data[0] = (uint8_t)(par1 & 0xFF);
data[1] = (uint8_t)((par1 >> 8) & 0xFF);
data[2] = (uint8_t)((par1 >> 16) & 0xFF);
event.data = data;
event.data_size = midi_driver_get_event_size(data[0]);
event.delta_time = 0;
if (!winmm_midi_queue_write(queue, &event))
{
#ifdef DEBUG
RARCH_ERR("[MIDI]: Input event dropped.\n");
#endif
}
}
else if(msg == MIM_LONGDATA)
{
#ifdef DEBUG
RARCH_WARN("[MIDI]: SysEx input not implemented, event dropped.\n");
#endif
}
}
static HMIDIIN winmm_midi_open_input_device(const char *dev_name,
winmm_midi_queue_t *queue)
{
UINT dev_count = midiInGetNumDevs();
HMIDIIN dev = NULL;
MIDIINCAPSA caps;
MMRESULT mmr;
UINT i;
for (i = 0; i < dev_count; ++i)
{
mmr = midiInGetDevCapsA(i, &caps, sizeof(caps));
if (mmr == MMSYSERR_NOERROR)
{
if (!strcmp(caps.szPname, dev_name))
{
mmr = midiInOpen(&dev, i, (DWORD_PTR)winmm_midi_input_callback,
(DWORD_PTR)queue, CALLBACK_FUNCTION);
if (mmr != MMSYSERR_NOERROR)
RARCH_ERR("[MIDI]: midiInOpen failed with error %d.\n", mmr);
break;
}
}
else
RARCH_WARN("[MIDI]: midiInGetDevCapsA failed with error %d.\n", mmr);
}
return dev;
}
static HMIDISTRM winmm_midi_open_output_device(const char *dev_name)
{
UINT dev_count = midiOutGetNumDevs();
HMIDISTRM dev = NULL;
MIDIOUTCAPSA caps;
MMRESULT mmr;
UINT i;
for (i = 0; i < dev_count; ++i)
{
mmr = midiOutGetDevCapsA(i, &caps, sizeof(caps));
if (mmr == MMSYSERR_NOERROR)
{
if (!strcmp(caps.szPname, dev_name))
{
mmr = midiStreamOpen(&dev, &i, 1, 0, 0, CALLBACK_NULL);
if (mmr != MMSYSERR_NOERROR)
RARCH_ERR("[MIDI]: midiStreamOpen failed with error %d.\n", mmr);
break;
}
}
else
RARCH_WARN("[MIDI]: midiOutGetDevCapsA failed with error %d.\n", mmr);
}
return dev;
}
static bool winmm_midi_init_clock(HMIDISTRM out_dev, double *tick_dur)
{
MIDIPROPTIMEDIV division;
MIDIPROPTEMPO tempo;
MMRESULT mmr;
tempo.cbStruct = sizeof(tempo);
mmr = midiStreamProperty(out_dev, (LPBYTE)&tempo,
MIDIPROP_GET | MIDIPROP_TEMPO);
if (mmr != MMSYSERR_NOERROR)
{
RARCH_ERR("[MIDI]: Current tempo unavailable (error %d).\n", mmr);
return false;
}
division.dwTimeDiv = 3;
while (tempo.dwTempo / division.dwTimeDiv > 320)
division.dwTimeDiv *= 2;
division.cbStruct = sizeof(division);
mmr = midiStreamProperty(out_dev, (LPBYTE)&division,
MIDIPROP_SET | MIDIPROP_TIMEDIV);
if (mmr != MMSYSERR_NOERROR)
{
RARCH_ERR("[MIDI]: Time division change failed (error %d).\n", mmr);
return false;
}
*tick_dur = (double)tempo.dwTempo / (double)division.dwTimeDiv;
#ifdef DEBUG
RARCH_LOG("[MIDI]: Tick duration %f us.\n", *tick_dur);
#endif
return true;
}
static bool winmm_midi_init_output_buffers(HMIDISTRM dev, winmm_midi_buffer_t *bufs)
{
MMRESULT mmr;
int i;
for (i = 0; i < WINMM_MIDI_BUF_CNT; ++i)
{
bufs[i].header.dwBufferLength = sizeof(DWORD) * WINMM_MIDI_BUF_LEN;
bufs[i].header.dwBytesRecorded = 0;
bufs[i].header.dwFlags = 0;
bufs[i].header.lpData = (LPSTR)bufs[i].data;
mmr = midiOutPrepareHeader((HMIDIOUT)dev, &bufs[i].header, sizeof(MIDIHDR));
if (mmr != MMSYSERR_NOERROR)
{
RARCH_ERR("[MIDI]: midiOutPrepareHeader failed with error %d.\n", mmr);
while (--i <= 0)
midiOutUnprepareHeader((HMIDIOUT)dev, &bufs[i].header, sizeof(MIDIHDR));
return false;
}
}
return true;
}
static void winmm_midi_free_output_buffers(HMIDISTRM dev, winmm_midi_buffer_t *bufs)
{
MMRESULT mmr;
int i;
for (i = 0; i < WINMM_MIDI_BUF_CNT; ++i)
{
mmr = midiOutUnprepareHeader((HMIDIOUT)dev, &bufs[i].header, sizeof(MIDIHDR));
if (mmr != MMSYSERR_NOERROR)
RARCH_ERR("[MIDI]: midiOutUnprepareHeader failed with error %d.\n", mmr);
}
}
static bool winmm_midi_write_short_event(winmm_midi_buffer_t *buf,
const uint8_t *data, size_t data_size, DWORD delta_time)
{
DWORD i = buf->header.dwBytesRecorded / sizeof(DWORD);
if (buf->header.dwBytesRecorded + sizeof(DWORD) * 3 >
sizeof(DWORD) * WINMM_MIDI_BUF_LEN)
return false;
buf->data[i++] = delta_time;
buf->data[i++] = 0;
buf->data[i] = MEVT_F_SHORT << 24;
if (data_size == 0)
buf->data[i] |= MEVT_NOP;
else
{
buf->data[i] |= MEVT_SHORTMSG << 24 | data[0];
if (data_size > 1)
buf->data[i] |= data[1] << 8;
if (data_size > 2)
buf->data[i] |= data[2] << 16;
}
buf->header.dwBytesRecorded += sizeof(DWORD) * 3;
return true;
}
static bool winmm_midi_write_long_event(winmm_midi_buffer_t *buf,
const uint8_t *data, size_t data_size, DWORD delta_time)
{
DWORD i = buf->header.dwBytesRecorded / sizeof(DWORD);
if (buf->header.dwBytesRecorded + sizeof(DWORD) * 3 + data_size >
sizeof(DWORD) * WINMM_MIDI_BUF_LEN)
return false;
buf->data[i++] = delta_time;
buf->data[i++] = 0;
buf->data[i++] = MEVT_F_LONG << 24 | MEVT_LONGMSG << 24 | data_size;
memcpy(&buf->data[i], data, data_size);
buf->header.dwBytesRecorded += sizeof(DWORD) * 3 + data_size;
return true;
}
static bool winmm_midi_get_avail_inputs(struct string_list *inputs)
{
union string_list_elem_attr attr = {0};
UINT dev_count = midiInGetNumDevs();
MIDIINCAPSA caps;
MMRESULT mmr;
UINT i;
for (i = 0; i < dev_count; ++i)
{
mmr = midiInGetDevCapsA(i, &caps, sizeof(caps));
if (mmr != MMSYSERR_NOERROR)
{
RARCH_ERR("[MIDI]: midiInGetDevCapsA failed with error %d.\n", mmr);
return false;
}
if (!string_list_append(inputs, caps.szPname, attr))
{
RARCH_ERR("[MIDI]: string_list_append failed.\n");
return false;
}
}
return true;
}
static bool winmm_midi_get_avail_outputs(struct string_list *outputs)
{
union string_list_elem_attr attr = {0};
UINT dev_count = midiOutGetNumDevs();
MIDIOUTCAPSA caps;
MMRESULT mmr;
UINT i;
for (i = 0; i < dev_count; ++i)
{
mmr = midiOutGetDevCapsA(i, &caps, sizeof(caps));
if (mmr != MMSYSERR_NOERROR)
{
RARCH_ERR("[MIDI]: midiOutGetDevCapsA failed with error %d.\n", mmr);
return false;
}
if (!string_list_append(outputs, caps.szPname, attr))
{
RARCH_ERR("[MIDI]: string_list_append failed.\n");
return false;
}
}
return true;
}
static void *winmm_midi_init(const char *input, const char *output)
{
winmm_midi_t *d = (winmm_midi_t*)calloc(sizeof(winmm_midi_t), 1);
bool err = false;
MMRESULT mmr;
if (!d)
{
RARCH_ERR("[MIDI]: Out of memory.\n");
return NULL;
}
if (input)
{
d->in_dev = winmm_midi_open_input_device(input, &d->in_queue);
if (!d->in_dev)
err = true;
else
{
mmr = midiInStart(d->in_dev);
if (mmr != MMSYSERR_NOERROR)
{
RARCH_ERR("[MIDI]: midiInStart failed with error %d.\n", mmr);
err = true;
}
}
}
if (output)
{
d->out_dev = winmm_midi_open_output_device(output);
if (!d->out_dev)
err = true;
else if (!winmm_midi_init_clock(d->out_dev, &d->tick_dur))
err = true;
else if (!winmm_midi_init_output_buffers(d->out_dev, d->out_bufs))
err = true;
else
{
mmr = midiStreamRestart(d->out_dev);
if (mmr != MMSYSERR_NOERROR)
{
RARCH_ERR("[MIDI]: midiStreamRestart failed with error %d.\n", mmr);
err = true;
}
}
}
if (err)
{
winmm_midi_free(d);
return NULL;
}
return d;
}
static void winmm_midi_free(void *p)
{
winmm_midi_t *d = (winmm_midi_t*)p;
if (d->in_dev)
{
midiInStop(d->in_dev);
midiInClose(d->in_dev);
}
if (d->out_dev)
{
midiStreamStop(d->out_dev);
winmm_midi_free_output_buffers(d->out_dev, d->out_bufs);
midiStreamClose(d->out_dev);
}
free(d);
}
static bool winmm_midi_set_input(void *p, const char *input)
{
winmm_midi_t *d = (winmm_midi_t*)p;
HMIDIIN new_dev = NULL;
MMRESULT mmr;
if (input)
{
new_dev = winmm_midi_open_input_device(input, &d->in_queue);
if (!new_dev)
return false;
}
if (d->in_dev)
{
midiInStop(d->in_dev);
midiInClose(d->in_dev);
}
d->in_dev = new_dev;
if (d->in_dev)
{
mmr = midiInStart(d->in_dev);
if (mmr != MMSYSERR_NOERROR)
{
RARCH_ERR("[MIDI]: midiInStart failed with error %d.\n", mmr);
return false;
}
}
return true;
}
static bool winmm_midi_set_output(void *p, const char *output)
{
winmm_midi_t *d = (winmm_midi_t*)p;
HMIDISTRM new_dev = NULL;
MMRESULT mmr;
int i;
if (output)
{
new_dev = winmm_midi_open_output_device(output);
if (!new_dev)
return false;
}
if (d->out_dev)
{
midiStreamStop(d->out_dev);
winmm_midi_free_output_buffers(d->out_dev, d->out_bufs);
midiStreamClose(d->out_dev);
}
d->out_dev = new_dev;
if (d->out_dev)
{
if (!winmm_midi_init_output_buffers(d->out_dev, d->out_bufs))
return false;
d->out_buf_idx = 0;
mmr = midiStreamRestart(d->out_dev);
if (mmr != MMSYSERR_NOERROR)
{
RARCH_ERR("[MIDI]: midiStreamRestart failed with error %d.\n", mmr);
return false;
}
}
return true;
}
static bool winmm_midi_read(void *p, midi_event_t *event)
{
winmm_midi_t *d = (winmm_midi_t*)p;
return winmm_midi_queue_read(&d->in_queue, event);
}
static bool winmm_midi_write(void *p, const midi_event_t *event)
{
winmm_midi_t *d = (winmm_midi_t*)p;
winmm_midi_buffer_t *buf = &d->out_bufs[d->out_buf_idx];
DWORD delta_time;
if (buf->header.dwFlags & MHDR_INQUEUE)
return false;
if (buf->header.dwFlags & MHDR_DONE)
{
buf->header.dwBytesRecorded = 0;
buf->header.dwFlags ^= MHDR_DONE;
}
delta_time = (DWORD)((double)event->delta_time / d->tick_dur);
if (event->data_size < 4)
return winmm_midi_write_short_event(buf, event->data,
event->data_size, delta_time);
return winmm_midi_write_long_event(buf, event->data,
event->data_size, delta_time);
}
static bool winmm_midi_flush(void *p)
{
winmm_midi_t *d = (winmm_midi_t*)p;
winmm_midi_buffer_t *buf = &d->out_bufs[d->out_buf_idx];
MMRESULT mmr;
if (buf->header.dwBytesRecorded)
{
mmr = midiStreamOut(d->out_dev, &buf->header, sizeof(buf->header));
if (mmr != MMSYSERR_NOERROR)
{
#ifdef DEBUG
RARCH_ERR("[MIDI]: midiStreamOut failed with error %d.\n", mmr);
#endif
return false;
}
if (++d->out_buf_idx == WINMM_MIDI_BUF_CNT)
d->out_buf_idx = 0;
}
return true;
}
midi_driver_t midi_winmm = {
"winmm",
winmm_midi_get_avail_inputs,
winmm_midi_get_avail_outputs,
winmm_midi_init,
winmm_midi_free,
winmm_midi_set_input,
winmm_midi_set_output,
winmm_midi_read,
winmm_midi_write,
winmm_midi_flush
};

585
midi/midi_driver.c Normal file
View File

@ -0,0 +1,585 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2018 The RetroArch team
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <configuration.h>
#include <verbosity.h>
#include <lists/string_list.h>
#include "midi_driver.h"
#define MIDI_DRIVER_BUF_SIZE 4096
#define MIDI_DRIVER_COUNT (int)(sizeof(midi_drivers) / sizeof(midi_driver_t*))
extern midi_driver_t midi_null;
extern midi_driver_t midi_winmm;
static midi_driver_t *midi_drivers[] = {
#ifdef HAVE_WINMM
&midi_winmm,
#endif
&midi_null
};
static midi_driver_t *midi_drv = &midi_null;
static void *midi_drv_data;
static struct string_list *midi_drv_inputs;
static struct string_list *midi_drv_outputs;
static bool midi_drv_input_enabled;
static bool midi_drv_output_enabled;
static uint8_t *midi_drv_input_buffer;
static uint8_t *midi_drv_output_buffer;
static midi_event_t midi_drv_input_event;
static midi_event_t midi_drv_output_event;
static bool midi_drv_output_pending;
static const uint8_t midi_drv_ev_sizes[128] =
{
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
0, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
static midi_driver_t *midi_driver_find_driver(const char *ident)
{
int i;
for (i = 0; i < MIDI_DRIVER_COUNT; ++i)
{
if (!strcmp(midi_drivers[i]->ident, ident))
return midi_drivers[i];
}
RARCH_ERR("[MIDI]: Unknown driver \"%s\", falling back to \"null\" driver.\n");
return &midi_null;
}
const void *midi_driver_find_handle(int index)
{
if (index < 0 || index >= MIDI_DRIVER_COUNT)
return NULL;
return midi_drivers[index];
}
const char *midi_driver_find_ident(int index)
{
if (index < 0 || index >= MIDI_DRIVER_COUNT)
return NULL;
return midi_drivers[index]->ident;
}
struct string_list *midi_driver_get_avail_inputs(void)
{
return midi_drv_inputs;
}
struct string_list *midi_driver_get_avail_outputs(void)
{
return midi_drv_outputs;
}
bool midi_driver_set_all_sounds_off(void)
{
uint8_t data[3] = { 0xB0, 120, 0 };
bool result = true;
midi_event_t event;
uint8_t i;
if (!midi_drv_data || !midi_drv_output_enabled)
return false;
event.data = data;
event.data_size = sizeof(data);
event.delta_time = 0;
for (i = 0; i < 16; ++i)
{
data[0] = 0xB0 | i;
if (!midi_drv->write(midi_drv_data, &event))
result = false;
}
if (!midi_drv->flush(midi_drv_data))
result = false;
if (!result)
RARCH_ERR("[MIDI]: All sounds off failed.\n");
return result;
}
bool midi_driver_set_volume(unsigned volume)
{
uint8_t data[8] = { 0xF0, 0x7F, 0x7F, 0x04, 0x01, 0, 0, 0xF7 };
const char *err_str = NULL;
midi_event_t event;
if (!midi_drv_data || !midi_drv_output_enabled)
return false;
volume = (unsigned)(163.83 * volume + 0.5);
if (volume > 16383)
volume = 16383;
data[5] = (uint8_t)(volume & 0x7F);
data[6] = (uint8_t)(volume >> 7);
event.data = data;
event.data_size = sizeof(data);
event.delta_time = 0;
if (!midi_drv->write(midi_drv_data, &event))
{
RARCH_ERR("[MIDI]: Volume change failed.\n");
return false;
}
return true;
}
bool midi_driver_init_io_buffers(void)
{
if (midi_drv_input_enabled)
{
midi_drv_input_buffer = (uint8_t*)malloc(MIDI_DRIVER_BUF_SIZE);
if (!midi_drv_input_buffer)
return false;
midi_drv_input_event.data = midi_drv_input_buffer;
midi_drv_input_event.data_size = 0;
}
if (midi_drv_output_enabled)
{
midi_drv_output_buffer = (uint8_t*)malloc(MIDI_DRIVER_BUF_SIZE);
if (!midi_drv_output_buffer)
return false;
midi_drv_output_event.data = midi_drv_output_buffer;
midi_drv_output_event.data_size = 0;
}
return true;
}
bool midi_driver_init(void)
{
settings_t *sett = config_get_ptr();
midi_drv_inputs = string_list_new();
midi_drv_outputs = string_list_new();
union string_list_elem_attr attr = {0};
const char *err_str = NULL;
RARCH_LOG("[MIDI]: Initializing ...\n");
if (!sett)
err_str = "settings unavailable";
else if (!midi_drv_inputs || !midi_drv_outputs)
err_str = "string_list_new failed";
else if (!string_list_append(midi_drv_inputs, "Off", attr) ||
!string_list_append(midi_drv_outputs, "Off", attr))
err_str = "string_list_append failed";
else
{
char * input = NULL;
char * output = NULL;
midi_drv = midi_driver_find_driver(sett->arrays.midi_driver);
if (strcmp(midi_drv->ident, sett->arrays.midi_driver))
strcpy(sett->arrays.midi_driver, midi_drv->ident);
if (!midi_drv->get_avail_inputs(midi_drv_inputs))
err_str = "list of input devices unavailable";
else if (!midi_drv->get_avail_outputs(midi_drv_outputs))
err_str = "list of output devices unavailable";
else
{
if (strcmp(sett->arrays.midi_input, "Off"))
{
if (string_list_find_elem(midi_drv_inputs, sett->arrays.midi_input))
input = sett->arrays.midi_input;
else
{
RARCH_WARN("[MIDI]: Input device \"%s\" unavailable.\n",
sett->arrays.midi_input);
strcpy(sett->arrays.midi_input, "Off");
}
}
if (strcmp(sett->arrays.midi_output, "Off"))
{
if (string_list_find_elem(midi_drv_outputs, sett->arrays.midi_output))
output = sett->arrays.midi_output;
else
{
RARCH_WARN("[MIDI]: Output device \"%s\" unavailable.\n",
sett->arrays.midi_output);
strcpy(sett->arrays.midi_output, "Off");
}
}
midi_drv_data = midi_drv->init(input, output);
if (!midi_drv_data)
err_str = "driver init failed";
else
{
midi_drv_input_enabled = input != NULL;
midi_drv_output_enabled = output != NULL;
if (!midi_driver_init_io_buffers())
err_str = "out of memory";
else
{
if (input)
RARCH_LOG("[MIDI]: Input device \"%s\".\n", input);
else
RARCH_LOG("[MIDI]: Input disabled.\n");
if (output)
{
RARCH_LOG("[MIDI]: Output device \"%s\".\n", output);
midi_driver_set_volume(sett->uints.midi_volume);
}
else
RARCH_LOG("[MIDI]: Output disabled.\n");
}
}
}
}
if (err_str)
{
midi_driver_free();
RARCH_ERR("[MIDI]: Initialization failed (%s).\n", err_str);
}
else
RARCH_LOG("[MIDI]: Initialized \"%s\" driver.\n", midi_drv->ident);
return err_str == NULL;;
}
void midi_driver_free(void)
{
if (midi_drv_data)
{
midi_drv->free(midi_drv_data);
midi_drv_data = NULL;
}
if (midi_drv_inputs)
{
string_list_free(midi_drv_inputs);
midi_drv_inputs = NULL;
}
if (midi_drv_outputs)
{
string_list_free(midi_drv_outputs);
midi_drv_outputs = NULL;
}
if (midi_drv_input_buffer)
{
free(midi_drv_input_buffer);
midi_drv_input_buffer = NULL;
}
if (midi_drv_output_buffer)
{
free(midi_drv_output_buffer);
midi_drv_output_buffer = NULL;
}
midi_drv_input_enabled = false;
midi_drv_output_enabled = false;
}
bool midi_driver_set_input(const char *input)
{
if (!midi_drv_data)
{
#ifdef DEBUG
RARCH_ERR("[MIDI]: midi_driver_set_input called on uninitialized driver.\n");
#endif
return false;
}
if (!strcmp(input, "Off"))
input = NULL;
if (!midi_drv->set_input(midi_drv_data, input))
{
if (input)
RARCH_ERR("[MIDI]: Failed to change input device to \"%s\".\n", input);
else
RARCH_ERR("[MIDI]: Failed to disable input.\n");
return false;
}
if (input)
RARCH_LOG("[MIDI]: Input device changed to \"%s\".\n", input);
else
RARCH_LOG("[MIDI]: Input disabled.\n");
midi_drv_input_enabled = input != NULL;
return true;
}
bool midi_driver_set_output(const char *output)
{
if (!midi_drv_data)
{
#ifdef DEBUG
RARCH_ERR("[MIDI]: midi_driver_set_output called on uninitialized driver.\n");
#endif
return false;
}
if (!strcmp(output, "Off"))
output = NULL;
if (!midi_drv->set_output(midi_drv_data, output))
{
if (output)
RARCH_ERR("[MIDI]: Failed to change output device to \"%s\".\n", output);
else
RARCH_ERR("[MIDI]: Failed to disable output.\n");
return false;
}
if (output)
{
settings_t *sett = config_get_ptr();
midi_drv_output_enabled = true;
RARCH_LOG("[MIDI]: Output device changed to \"%s\".\n", output);
if (sett)
midi_driver_set_volume(sett->uints.midi_volume);
else
RARCH_ERR("[MIDI]: Volume change failed (settings unavailable).\n");
}
else
{
midi_drv_output_enabled = false;
RARCH_LOG("[MIDI]: Output disabled.\n");
}
return true;
}
bool midi_driver_input_enabled(void)
{
return midi_drv_input_enabled;
}
bool midi_driver_output_enabled(void)
{
return midi_drv_output_enabled;
}
bool midi_driver_read(uint8_t *byte)
{
static int i;
if (!midi_drv_data || !midi_drv_input_enabled || !byte)
{
#ifdef DEBUG
if (!midi_drv_data)
RARCH_ERR("[MIDI]: midi_driver_read called on uninitialized driver.\n");
else if (!midi_drv_input_enabled)
RARCH_ERR("[MIDI]: midi_driver_read called when input is disabled.\n");
else
RARCH_ERR("[MIDI]: midi_driver_read called with null pointer.\n");
#endif
return false;
}
if (i == midi_drv_input_event.data_size)
{
midi_drv_input_event.data_size = MIDI_DRIVER_BUF_SIZE;
if (!midi_drv->read(midi_drv_data, &midi_drv_input_event))
{
midi_drv_input_event.data_size = i;
return false;
}
i = 0;
#ifdef DEBUG
if (midi_drv_input_event.data_size == 1)
RARCH_LOG("[MIDI]: In [0x%02X].\n",
midi_drv_input_event.data[0]);
else if (midi_drv_input_event.data_size == 2)
RARCH_LOG("[MIDI]: In [0x%02X, 0x%02X].\n",
midi_drv_input_event.data[0],
midi_drv_input_event.data[1]);
else if (midi_drv_input_event.data_size == 3)
RARCH_LOG("[MIDI]: In [0x%02X, 0x%02X, 0x%02X].\n",
midi_drv_input_event.data[0],
midi_drv_input_event.data[1],
midi_drv_input_event.data[2]);
else
RARCH_LOG("[MIDI]: In [0x%02X, ...], size %u.\n",
midi_drv_input_event.data[0],
midi_drv_input_event.data_size);
#endif
}
*byte = midi_drv_input_event.data[i++];
return true;
}
bool midi_driver_write(uint8_t byte, uint32_t delta_time)
{
static int event_size;
if (!midi_drv_data || !midi_drv_output_enabled)
{
#ifdef DEBUG
if (!midi_drv_data)
RARCH_ERR("[MIDI]: midi_driver_write called on uninitialized driver.\n");
else
RARCH_ERR("[MIDI]: midi_driver_write called when output is disabled.\n");
#endif
return false;
}
if (byte >= 0x80)
{
if (midi_drv_output_event.data_size &&
midi_drv_output_event.data[0] == 0xF0)
{
if (byte == 0xF7)
event_size = midi_drv_output_event.data_size + 1;
else
{
if (!midi_drv->write(midi_drv_data, &midi_drv_output_event))
return false;
#ifdef DEBUG
if (midi_drv_output_event.data_size == 1)
RARCH_LOG("[MIDI]: Out [0x%02X].\n",
midi_drv_output_event.data[0]);
else if (midi_drv_output_event.data_size == 2)
RARCH_LOG("[MIDI]: Out [0x%02X, 0x%02X].\n",
midi_drv_output_event.data[0],
midi_drv_output_event.data[1]);
else if (midi_drv_output_event.data_size == 3)
RARCH_LOG("[MIDI]: Out [0x%02X, 0x%02X, 0x%02X].\n",
midi_drv_output_event.data[0],
midi_drv_output_event.data[1],
midi_drv_output_event.data[2]);
else
RARCH_LOG("[MIDI]: Out [0x%02X, ...], size %u.\n",
midi_drv_output_event.data[0],
midi_drv_output_event.data_size);
#endif
midi_drv_output_pending = true;
event_size = midi_driver_get_event_size(byte);
midi_drv_output_event.data_size = 0;
midi_drv_output_event.delta_time = 0;
}
}
else
{
event_size = midi_driver_get_event_size(byte);
midi_drv_output_event.data_size = 0;
midi_drv_output_event.delta_time = 0;
}
}
if (midi_drv_output_event.data_size < MIDI_DRIVER_BUF_SIZE)
{
midi_drv_output_event.data[midi_drv_output_event.data_size] = byte;
++midi_drv_output_event.data_size;
midi_drv_output_event.delta_time += delta_time;
}
else
{
#ifdef DEBUG
RARCH_ERR("[MIDI]: Output event dropped.\n");
#endif
return false;
}
if (midi_drv_output_event.data_size == event_size)
{
if (!midi_drv->write(midi_drv_data, &midi_drv_output_event))
return false;
#ifdef DEBUG
if (midi_drv_output_event.data_size == 1)
RARCH_LOG("[MIDI]: Out [0x%02X].\n",
midi_drv_output_event.data[0]);
else if (midi_drv_output_event.data_size == 2)
RARCH_LOG("[MIDI]: Out [0x%02X, 0x%02X].\n",
midi_drv_output_event.data[0],
midi_drv_output_event.data[1]);
else if (midi_drv_output_event.data_size == 3)
RARCH_LOG("[MIDI]: Out [0x%02X, 0x%02X, 0x%02X].\n",
midi_drv_output_event.data[0],
midi_drv_output_event.data[1],
midi_drv_output_event.data[2]);
else
RARCH_LOG("[MIDI]: Out [0x%02X, ...], size %u.\n",
midi_drv_output_event.data[0],
midi_drv_output_event.data_size);
#endif
midi_drv_output_pending = true;
}
return true;
}
bool midi_driver_flush(void)
{
if (!midi_drv_data)
{
#ifdef DEBUG
RARCH_ERR("[MIDI]: midi_driver_flush called on uninitialized driver.\n");
#endif
return false;
}
if (midi_drv_output_pending)
midi_drv_output_pending = !midi_drv->flush(midi_drv_data);
return !midi_drv_output_pending;
}
size_t midi_driver_get_event_size(uint8_t status)
{
if (status < 0x80)
{
#ifdef DEBUG
RARCH_ERR("[MIDI]: midi_driver_get_event_size called with invalid status.\n");
#endif
return 0;
}
return midi_drv_ev_sizes[status - 0x80];
}

77
midi/midi_driver.h Normal file
View File

@ -0,0 +1,77 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2018 The RetroArch team
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MIDI_DRIVER__H
#define __MIDI_DRIVER__H
#include <boolean.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
struct string_list;
typedef struct
{
uint8_t *data;
size_t data_size;
uint32_t delta_time;
} midi_event_t;
typedef struct midi_driver
{
const char *ident;
bool (*get_avail_inputs)(struct string_list *inputs);
bool (*get_avail_outputs)(struct string_list *outputs);
void *(*init)(const char *input, const char *output);
void (*free)(void *p);
bool (*set_input)(void *p, const char *input);
bool (*set_output)(void *p, const char *output);
bool (*read)(void *p, midi_event_t *event);
bool (*write)(void *p, const midi_event_t *event);
bool (*flush)(void *p);
} midi_driver_t;
const void *midi_driver_find_handle(int index);
const char *midi_driver_find_ident(int index);
struct string_list *midi_driver_get_avail_inputs(void);
struct string_list *midi_driver_get_avail_outputs(void);
bool midi_driver_set_all_sounds_off(void);
bool midi_driver_set_volume(unsigned volume);
bool midi_driver_init(void);
void midi_driver_free(void);
bool midi_driver_set_input(const char *input);
bool midi_driver_set_output(const char *output);
bool midi_driver_input_enabled(void);
bool midi_driver_output_enabled(void);
bool midi_driver_read(uint8_t *byte);
bool midi_driver_write(uint8_t byte, uint32_t delta_time);
bool midi_driver_flush(void);
size_t midi_driver_get_event_size(uint8_t status);
RETRO_END_DECLS
#endif

View File

@ -903,6 +903,7 @@ enum msg_hash_enums
MENU_LABEL(USER_SETTINGS),
MENU_LABEL(DIRECTORY_SETTINGS),
MENU_LABEL(PRIVACY_SETTINGS),
MENU_LABEL(MIDI_SETTINGS),
MENU_LABEL(NETWORK_SETTINGS),
MENU_LABEL(NETPLAY_LAN_SCAN_SETTINGS),
@ -1030,6 +1031,7 @@ enum msg_hash_enums
MENU_ENUM_LABEL_DEFERRED_USER_SETTINGS_LIST,
MENU_ENUM_LABEL_DEFERRED_DIRECTORY_SETTINGS_LIST,
MENU_ENUM_LABEL_DEFERRED_PRIVACY_SETTINGS_LIST,
MENU_ENUM_LABEL_DEFERRED_MIDI_SETTINGS_LIST,
MENU_ENUM_LABEL_DEFERRED_LOGGING_SETTINGS_LIST,
MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST,
MENU_ENUM_LABEL_DEFERRED_AUDIO_MIXER_SETTINGS_LIST,
@ -1280,6 +1282,7 @@ enum msg_hash_enums
MENU_LABEL(RECORD_DRIVER),
MENU_LABEL(VIDEO_DRIVER),
MENU_LABEL(INPUT_DRIVER),
MENU_LABEL(MIDI_DRIVER),
MENU_ENUM_LABEL_VIDEO_DRIVER_GL,
MENU_ENUM_LABEL_VIDEO_DRIVER_SDL2,
@ -1861,6 +1864,10 @@ enum msg_hash_enums
MENU_ENUM_LABEL_VALUE_QT_FILE_READ_OPEN_FAILED,
MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST,
MENU_LABEL(MIDI_INPUT),
MENU_LABEL(MIDI_OUTPUT),
MENU_LABEL(MIDI_VOLUME),
MSG_LAST
};

View File

@ -322,6 +322,7 @@ if [ "$OS" = 'Win32' ]; then
HAVE_WASAPI=yes
HAVE_XAUDIO=yes
HAVE_WINMM=yes
else
HAVE_D3D9=no
HAVE_D3D10=no