RetroArch/midi/midi_driver.c

585 lines
16 KiB
C
Raw Normal View History

2018-06-04 05:48:08 +00:00
/* 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 <string/stdstring.h>
2018-06-04 05:48:08 +00:00
#include <lists/string_list.h>
2018-06-17 23:05:52 +00:00
#include <retro_miscellaneous.h>
#include "../configuration.h"
#include "../verbosity.h"
2018-06-04 05:48:08 +00:00
#include "midi_driver.h"
#define MIDI_DRIVER_BUF_SIZE 4096
extern midi_driver_t midi_null;
extern midi_driver_t midi_winmm;
2018-09-28 14:07:51 +00:00
extern midi_driver_t midi_alsa;
2018-06-04 05:48:08 +00:00
static midi_driver_t *midi_drivers[] = {
#if defined(HAVE_ALSA) && !defined(HAVE_HAKCHI)
2018-09-28 14:07:51 +00:00
&midi_alsa,
#endif
2018-06-04 05:48:08 +00:00
#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)
{
2018-06-17 23:05:52 +00:00
unsigned i;
2018-06-04 05:48:08 +00:00
2018-06-17 23:05:52 +00:00
for (i = 0; i < ARRAY_SIZE(midi_drivers); ++i)
2018-06-04 05:48:08 +00:00
{
if (string_is_equal(midi_drivers[i]->ident, ident))
2018-06-04 05:48:08 +00:00
return midi_drivers[i];
}
2019-02-23 16:17:00 +00:00
RARCH_ERR("[MIDI]: Unknown driver \"%s\", falling back to \"null\" driver.\n", ident);
2018-06-04 05:48:08 +00:00
return &midi_null;
}
const void *midi_driver_find_handle(int index)
{
2018-06-17 23:35:38 +00:00
if (index < 0 || index >= ARRAY_SIZE(midi_drivers))
2018-06-04 05:48:08 +00:00
return NULL;
return midi_drivers[index];
}
const char *midi_driver_find_ident(int index)
{
2018-06-17 23:35:38 +00:00
if (index < 0 || index >= ARRAY_SIZE(midi_drivers))
2018-06-04 05:48:08 +00:00
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)
{
midi_event_t event;
uint8_t i;
2018-06-17 23:05:52 +00:00
uint8_t data[3] = { 0xB0, 120, 0 };
bool result = true;
2018-06-04 05:48:08 +00:00
if (!midi_drv_data || !midi_drv_output_enabled)
return false;
2018-06-17 23:05:52 +00:00
event.data = data;
event.data_size = sizeof(data);
2018-06-04 05:48:08 +00:00
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)
{
2018-06-17 23:05:52 +00:00
midi_event_t event;
2018-06-04 05:48:08 +00:00
uint8_t data[8] = { 0xF0, 0x7F, 0x7F, 0x04, 0x01, 0, 0, 0xF7 };
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)
{
2018-06-17 23:05:52 +00:00
midi_drv_input_buffer = (uint8_t*)malloc(MIDI_DRIVER_BUF_SIZE);
2018-06-17 16:50:27 +00:00
midi_drv_output_buffer = (uint8_t*)malloc(MIDI_DRIVER_BUF_SIZE);
2018-06-04 05:48:08 +00:00
2018-06-17 16:50:27 +00:00
if (!midi_drv_input_buffer || !midi_drv_output_buffer)
return false;
2018-06-04 05:48:08 +00:00
2018-06-17 16:50:27 +00:00
midi_drv_input_event.data = midi_drv_input_buffer;
midi_drv_input_event.data_size = 0;
2018-06-04 05:48:08 +00:00
2018-06-17 16:50:27 +00:00
midi_drv_output_event.data = midi_drv_output_buffer;
midi_drv_output_event.data_size = 0;
2018-06-04 05:48:08 +00:00
return true;
}
bool midi_driver_init(void)
{
2018-06-17 23:05:52 +00:00
settings_t *settings = config_get_ptr();
2018-06-04 05:48:08 +00:00
union string_list_elem_attr attr = {0};
const char *err_str = NULL;
2018-06-18 03:03:10 +00:00
midi_drv_inputs = string_list_new();
midi_drv_outputs = string_list_new();
2018-06-04 05:48:08 +00:00
RARCH_LOG("[MIDI]: Initializing ...\n");
2018-06-17 23:05:52 +00:00
if (!settings)
2018-06-04 05:48:08 +00:00
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;
2018-06-17 23:05:52 +00:00
midi_drv = midi_driver_find_driver(settings->arrays.midi_driver);
if (strcmp(midi_drv->ident, settings->arrays.midi_driver))
strlcpy(settings->arrays.midi_driver, midi_drv->ident,
sizeof(settings->arrays.midi_driver));
2018-06-04 05:48:08 +00:00
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 (string_is_not_equal(settings->arrays.midi_input, "Off"))
2018-06-04 05:48:08 +00:00
{
2018-06-17 23:05:52 +00:00
if (string_list_find_elem(midi_drv_inputs, settings->arrays.midi_input))
input = settings->arrays.midi_input;
2018-06-04 05:48:08 +00:00
else
{
RARCH_WARN("[MIDI]: Input device \"%s\" unavailable.\n",
2018-06-17 23:05:52 +00:00
settings->arrays.midi_input);
strlcpy(settings->arrays.midi_input, "Off",
sizeof(settings->arrays.midi_input));
2018-06-04 05:48:08 +00:00
}
}
if (string_is_not_equal(settings->arrays.midi_output, "Off"))
2018-06-04 05:48:08 +00:00
{
2018-06-17 23:05:52 +00:00
if (string_list_find_elem(midi_drv_outputs, settings->arrays.midi_output))
output = settings->arrays.midi_output;
2018-06-04 05:48:08 +00:00
else
{
RARCH_WARN("[MIDI]: Output device \"%s\" unavailable.\n",
2018-06-17 23:05:52 +00:00
settings->arrays.midi_output);
strlcpy(settings->arrays.midi_output, "Off",
sizeof(settings->arrays.midi_output));
2018-06-04 05:48:08 +00:00
}
}
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);
2018-06-17 23:05:52 +00:00
midi_driver_set_volume(settings->uints.midi_volume);
2018-06-04 05:48:08 +00:00
}
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);
2018-06-17 23:05:52 +00:00
return err_str == NULL;
2018-06-04 05:48:08 +00:00
}
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;
}
2018-06-17 23:05:52 +00:00
midi_drv_input_enabled = false;
2018-06-04 05:48:08 +00:00
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 (string_is_equal(input, "Off"))
2018-06-04 05:48:08 +00:00
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 (string_is_equal(output, "Off"))
2018-06-04 05:48:08 +00:00
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)
{
2018-06-17 23:05:52 +00:00
settings_t *settings = config_get_ptr();
2018-06-04 05:48:08 +00:00
midi_drv_output_enabled = true;
RARCH_LOG("[MIDI]: Output device changed to \"%s\".\n", output);
2018-06-17 23:05:52 +00:00
if (settings)
midi_driver_set_volume(settings->uints.midi_volume);
2018-06-04 05:48:08 +00:00
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)
2018-06-21 05:34:27 +00:00
event_size = (int)midi_drv_output_event.data_size + 1;
2018-06-04 05:48:08 +00:00
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
2018-06-21 05:34:27 +00:00
midi_drv_output_pending = true;
event_size = (int)midi_driver_get_event_size(byte);
midi_drv_output_event.data_size = 0;
2018-06-04 05:48:08 +00:00
midi_drv_output_event.delta_time = 0;
}
}
else
{
2018-06-21 05:34:27 +00:00
event_size = (int)midi_driver_get_event_size(byte);
midi_drv_output_event.data_size = 0;
midi_drv_output_event.delta_time = 0;
2018-06-04 05:48:08 +00:00
}
}
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];
}