mame/src/osd/osdcore.c

591 lines
13 KiB
C

#include "emucore.h"
#include "osdcore.h"
#include "portmidi/portmidi.h"
bool g_print_verbose = false;
/*-------------------------------------------------
osd_file_output_callback - default callback
for file output
-------------------------------------------------*/
void osd_file_output_callback(FILE *param, const char *format, va_list argptr)
{
vfprintf(param, format, argptr);
}
/*-------------------------------------------------
osd_null_output_callback - default callback
for no output
-------------------------------------------------*/
void osd_null_output_callback(FILE *param, const char *format, va_list argptr)
{
}
/* output channels */
static output_delegate output_cb[OSD_OUTPUT_CHANNEL_COUNT] =
{
output_delegate(FUNC(osd_file_output_callback), stderr), // OSD_OUTPUT_CHANNEL_ERROR
output_delegate(FUNC(osd_file_output_callback), stderr), // OSD_OUTPUT_CHANNEL_WARNING
output_delegate(FUNC(osd_file_output_callback), stdout), // OSD_OUTPUT_CHANNEL_INFO
#ifdef MAME_DEBUG
output_delegate(FUNC(osd_file_output_callback), stdout), // OSD_OUTPUT_CHANNEL_DEBUG
#else
output_delegate(FUNC(osd_null_output_callback), stdout), // OSD_OUTPUT_CHANNEL_DEBUG
#endif
output_delegate(FUNC(osd_file_output_callback), stdout), // OSD_OUTPUT_CHANNEL_VERBOSE
output_delegate(FUNC(osd_file_output_callback), stdout) // OSD_OUTPUT_CHANNEL_LOG
};
/***************************************************************************
OUTPUT MANAGEMENT
***************************************************************************/
/*-------------------------------------------------
osd_set_output_channel - configure an output
channel
-------------------------------------------------*/
output_delegate osd_set_output_channel(output_channel channel, output_delegate callback)
{
assert(channel < OSD_OUTPUT_CHANNEL_COUNT);
assert(!callback.isnull());
/* return the originals if requested */
output_delegate prevcb = output_cb[channel];
/* set the new ones */
output_cb[channel] = callback;
return prevcb;
}
/*-------------------------------------------------
osd_printf_error - output an error to the
appropriate callback
-------------------------------------------------*/
void CLIB_DECL osd_printf_error(const char *format, ...)
{
va_list argptr;
/* do the output */
va_start(argptr, format);
output_cb[OSD_OUTPUT_CHANNEL_ERROR](format, argptr);
va_end(argptr);
}
/*-------------------------------------------------
osd_printf_warning - output a warning to the
appropriate callback
-------------------------------------------------*/
void CLIB_DECL osd_printf_warning(const char *format, ...)
{
va_list argptr;
/* do the output */
va_start(argptr, format);
output_cb[OSD_OUTPUT_CHANNEL_WARNING](format, argptr);
va_end(argptr);
}
/*-------------------------------------------------
osd_printf_info - output info text to the
appropriate callback
-------------------------------------------------*/
void CLIB_DECL osd_printf_info(const char *format, ...)
{
va_list argptr;
/* do the output */
va_start(argptr, format);
output_cb[OSD_OUTPUT_CHANNEL_INFO](format, argptr);
va_end(argptr);
}
/*-------------------------------------------------
osd_printf_verbose - output verbose text to
the appropriate callback
-------------------------------------------------*/
void CLIB_DECL osd_printf_verbose(const char *format, ...)
{
va_list argptr;
/* if we're not verbose, skip it */
if (!g_print_verbose)
return;
/* do the output */
va_start(argptr, format);
output_cb[OSD_OUTPUT_CHANNEL_VERBOSE](format, argptr);
va_end(argptr);
}
/*-------------------------------------------------
osd_printf_debug - output debug text to the
appropriate callback
-------------------------------------------------*/
void CLIB_DECL osd_printf_debug(const char *format, ...)
{
va_list argptr;
/* do the output */
va_start(argptr, format);
output_cb[OSD_OUTPUT_CHANNEL_DEBUG](format, argptr);
va_end(argptr);
}
/*-------------------------------------------------
osd_printf_log - output log text to the
appropriate callback
-------------------------------------------------*/
#ifdef UNUSED_FUNCTION
void CLIB_DECL osd_printf_log(const char *format, ...)
{
va_list argptr;
/* do the output */
va_start(argptr, format);
output_cb[OSD_OUTPUT_CHANNEL_LOG])(format, argptr);
va_end(argptr);
}
#endif
static const int RX_EVENT_BUF_SIZE = 512;
#define MIDI_SYSEX 0xf0
#define MIDI_EOX 0xf7
struct osd_midi_device
{
#ifndef DISABLE_MIDI
PortMidiStream *pmStream;
PmEvent rx_evBuf[RX_EVENT_BUF_SIZE];
#endif
UINT8 xmit_in[4]; // Pm_Messages mean we can at most have 3 residue bytes
int xmit_cnt;
UINT8 last_status;
bool rx_sysex;
};
void osd_list_midi_devices(void)
{
#ifndef DISABLE_MIDI
int num_devs = Pm_CountDevices();
const PmDeviceInfo *pmInfo;
printf("\n");
if (num_devs == 0)
{
printf("No MIDI ports were found\n");
return;
}
printf("MIDI input ports:\n");
for (int i = 0; i < num_devs; i++)
{
pmInfo = Pm_GetDeviceInfo(i);
if (pmInfo->input)
{
printf("%s %s\n", pmInfo->name, (i == Pm_GetDefaultInputDeviceID()) ? "(default)" : "");
}
}
printf("\nMIDI output ports:\n");
for (int i = 0; i < num_devs; i++)
{
pmInfo = Pm_GetDeviceInfo(i);
if (pmInfo->output)
{
printf("%s %s\n", pmInfo->name, (i == Pm_GetDefaultOutputDeviceID()) ? "(default)" : "");
}
}
#else
printf("\nMIDI is not supported in this build\n");
#endif
}
osd_midi_device *osd_open_midi_input(const char *devname)
{
#ifndef DISABLE_MIDI
int num_devs = Pm_CountDevices();
int found_dev = -1;
const PmDeviceInfo *pmInfo;
PortMidiStream *stm;
osd_midi_device *ret;
if (!strcmp("default", devname))
{
found_dev = Pm_GetDefaultInputDeviceID();
}
else
{
for (int i = 0; i < num_devs; i++)
{
pmInfo = Pm_GetDeviceInfo(i);
if (pmInfo->input)
{
if (!strcmp(devname, pmInfo->name))
{
found_dev = i;
break;
}
}
}
}
if (found_dev >= 0)
{
if (Pm_OpenInput(&stm, found_dev, NULL, RX_EVENT_BUF_SIZE, NULL, NULL) == pmNoError)
{
ret = (osd_midi_device *)osd_malloc(sizeof(osd_midi_device));
memset(ret, 0, sizeof(osd_midi_device));
ret->pmStream = stm;
return ret;
}
else
{
printf("Couldn't open PM device\n");
return NULL;
}
}
else
{
return NULL;
}
#else
return NULL;
#endif
}
osd_midi_device *osd_open_midi_output(const char *devname)
{
#ifndef DISABLE_MIDI
int num_devs = Pm_CountDevices();
int found_dev = -1;
const PmDeviceInfo *pmInfo;
PortMidiStream *stm;
osd_midi_device *ret;
if (!strcmp("default", devname))
{
found_dev = Pm_GetDefaultOutputDeviceID();
}
else
{
for (int i = 0; i < num_devs; i++)
{
pmInfo = Pm_GetDeviceInfo(i);
if (pmInfo->output)
{
if (!strcmp(devname, pmInfo->name))
{
found_dev = i;
break;
}
}
}
}
if (found_dev >= 0)
{
if (Pm_OpenOutput(&stm, found_dev, NULL, 100, NULL, NULL, 0) == pmNoError)
{
ret = (osd_midi_device *)osd_malloc(sizeof(osd_midi_device));
memset(ret, 0, sizeof(osd_midi_device));
ret->pmStream = stm;
return ret;
}
else
{
printf("Couldn't open PM device\n");
return NULL;
}
}
else
{
return NULL;
}
#endif
return NULL;
}
void osd_close_midi_channel(osd_midi_device *dev)
{
#ifndef DISABLE_MIDI
Pm_Close(dev->pmStream);
osd_free(dev);
#endif
}
bool osd_poll_midi_channel(osd_midi_device *dev)
{
#ifndef DISABLE_MIDI
PmError chk = Pm_Poll(dev->pmStream);
return (chk == pmGotData) ? true : false;
#else
return false;
#endif
}
int osd_read_midi_channel(osd_midi_device *dev, UINT8 *pOut)
{
#ifndef DISABLE_MIDI
int msgsRead = Pm_Read(dev->pmStream, dev->rx_evBuf, RX_EVENT_BUF_SIZE);
int bytesOut = 0;
if (msgsRead <= 0)
{
return 0;
}
for (int msg = 0; msg < msgsRead; msg++)
{
UINT8 status = Pm_MessageStatus(dev->rx_evBuf[msg].message);
if (dev->rx_sysex)
{
if (status & 0x80) // sys real-time imposing on us?
{
if ((status == 0xf2) || (status == 0xf3))
{
*pOut++ = status;
*pOut++ = Pm_MessageData1(dev->rx_evBuf[msg].message);
*pOut++ = Pm_MessageData2(dev->rx_evBuf[msg].message);
bytesOut += 3;
}
else
{
*pOut++ = status;
bytesOut++;
if (status == MIDI_EOX)
{
dev->rx_sysex = false;
}
}
}
else // shift out the sysex bytes
{
for (int i = 0; i < 4; i++)
{
UINT8 byte = dev->rx_evBuf[msg].message & 0xff;
*pOut++ = byte;
bytesOut++;
if (byte == MIDI_EOX)
{
dev->rx_sysex = false;
break;
}
dev->rx_evBuf[msg].message >>= 8;
}
}
}
else
{
switch ((status>>4) & 0xf)
{
case 0xc: // 2-byte messages
case 0xd:
*pOut++ = status;
*pOut++ = Pm_MessageData1(dev->rx_evBuf[msg].message);
bytesOut += 2;
break;
case 0xf: // system common
switch (status & 0xf)
{
case 0: // System Exclusive
{
*pOut++ = status; // this should be OK: the shortest legal sysex is F0 tt dd F7, I believe
*pOut++ = (dev->rx_evBuf[msg].message>>8) & 0xff;
*pOut++ = (dev->rx_evBuf[msg].message>>16) & 0xff;
UINT8 last = *pOut++ = (dev->rx_evBuf[msg].message>>24) & 0xff;
bytesOut += 4;
dev->rx_sysex = (last != MIDI_EOX);
break;
}
case 7: // End of System Exclusive
*pOut++ = status;
bytesOut += 1;
dev->rx_sysex = false;
break;
case 2: // song pos
case 3: // song select
*pOut++ = status;
*pOut++ = Pm_MessageData1(dev->rx_evBuf[msg].message);
*pOut++ = Pm_MessageData2(dev->rx_evBuf[msg].message);
bytesOut += 3;
break;
default: // all other defined Fx messages are 1 byte
break;
}
break;
default:
*pOut++ = status;
*pOut++ = Pm_MessageData1(dev->rx_evBuf[msg].message);
*pOut++ = Pm_MessageData2(dev->rx_evBuf[msg].message);
bytesOut += 3;
break;
}
}
}
return bytesOut;
#else
return 0;
#endif
}
void osd_write_midi_channel(osd_midi_device *dev, UINT8 data)
{
#ifndef DISABLE_MIDI
int bytes_needed = 0;
PmEvent ev;
ev.timestamp = 0; // use the current time
// printf("write: %02x (%d)\n", data, dev->xmit_cnt);
// reject data bytes when no valid status exists
if ((dev->last_status == 0) && !(data & 0x80))
{
dev->xmit_cnt = 0;
return;
}
if (dev->xmit_cnt >= 4)
{
printf("MIDI out: packet assembly overflow, contact MAMEdev!\n");
return;
}
// handle sysex
if (dev->last_status == MIDI_SYSEX)
{
// printf("sysex: %02x (%d)\n", data, dev->xmit_cnt);
// if we get a status that isn't sysex, assume it's system common
if ((data & 0x80) && (data != MIDI_EOX))
{
// printf("common during sysex!\n");
ev.message = Pm_Message(data, 0, 0);
Pm_Write(dev->pmStream, &ev, 1);
return;
}
dev->xmit_in[dev->xmit_cnt++] = data;
// if EOX or 4 bytes filled, transmit 4 bytes
if ((dev->xmit_cnt == 4) || (data == MIDI_EOX))
{
ev.message = dev->xmit_in[0] | (dev->xmit_in[1]<<8) | (dev->xmit_in[2]<<16) | (dev->xmit_in[3]<<24);
Pm_Write(dev->pmStream, &ev, 1);
dev->xmit_in[0] = dev->xmit_in[1] = dev->xmit_in[2] = dev->xmit_in[3] = 0;
dev->xmit_cnt = 0;
// printf("SysEx packet: %08x\n", ev.message);
// if this is EOX, kill the running status
if (data == MIDI_EOX)
{
dev->last_status = 0;
}
}
return;
}
// handle running status. don't allow system real-time messages to be considered as running status.
if ((dev->xmit_cnt == 0) && (data & 0x80) && (data < 0xf8))
{
dev->last_status = data;
}
if ((dev->xmit_cnt == 0) && !(data & 0x80))
{
dev->xmit_in[dev->xmit_cnt++] = dev->last_status;
dev->xmit_in[dev->xmit_cnt++] = data;
// printf("\trunning status: [%d] = %02x, [%d] = %02x, last_status = %02x\n", dev->xmit_cnt-2, dev->last_status, dev->xmit_cnt-1, data, dev->last_status);
}
else
{
dev->xmit_in[dev->xmit_cnt++] = data;
// printf("\tNRS: [%d] = %02x\n", dev->xmit_cnt-1, data);
}
if ((dev->xmit_cnt == 1) && (dev->xmit_in[0] == MIDI_SYSEX))
{
// printf("Start SysEx!\n");
dev->last_status = MIDI_SYSEX;
return;
}
// are we there yet?
// printf("status check: %02x\n", dev->xmit_in[0]);
switch ((dev->xmit_in[0]>>4) & 0xf)
{
case 0xc: // 2-byte messages
case 0xd:
bytes_needed = 2;
break;
case 0xf: // system common
switch (dev->xmit_in[0] & 0xf)
{
case 0: // System Exclusive is handled above
break;
case 7: // End of System Exclusive
bytes_needed = 1;
break;
case 2: // song pos
case 3: // song select
bytes_needed = 3;
break;
default: // all other defined Fx messages are 1 byte
bytes_needed = 1;
break;
}
break;
default:
bytes_needed = 3;
break;
}
if (dev->xmit_cnt == bytes_needed)
{
ev.message = Pm_Message(dev->xmit_in[0], dev->xmit_in[1], dev->xmit_in[2]);
Pm_Write(dev->pmStream, &ev, 1);
dev->xmit_cnt = 0;
}
#endif
}