mirror of
https://github.com/libretro/RetroArch
synced 2025-02-19 03:40:59 +00:00
Add MIDI support
This commit is contained in:
parent
0045e1fff2
commit
b487c3cace
@ -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)
|
||||
|
@ -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
|
||||
|
@ -18,6 +18,7 @@ HAVE_PYTHON = 0
|
||||
DYNAMIC = 1
|
||||
|
||||
HAVE_XINPUT = 1
|
||||
HAVE_WINMM = 1
|
||||
|
||||
HAVE_SDL := 0
|
||||
HAVE_SDL2 := 0
|
||||
|
@ -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));
|
||||
|
@ -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/";
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
13
driver.c
13
driver.c
@ -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)
|
||||
|
12
driver.h
12
driver.h
@ -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
|
||||
|
17
dynamic.c
17
dynamic.c
@ -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);
|
||||
|
@ -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")
|
||||
|
@ -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);
|
||||
|
@ -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 (%).")
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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
98
midi/drivers/null_midi.c
Normal 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
628
midi/drivers/winmm_midi.c
Normal 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
585
midi/midi_driver.c
Normal 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
77
midi/midi_driver.h
Normal 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
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -322,6 +322,7 @@ if [ "$OS" = 'Win32' ]; then
|
||||
|
||||
HAVE_WASAPI=yes
|
||||
HAVE_XAUDIO=yes
|
||||
HAVE_WINMM=yes
|
||||
else
|
||||
HAVE_D3D9=no
|
||||
HAVE_D3D10=no
|
||||
|
Loading…
x
Reference in New Issue
Block a user