summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/imagedev/midiin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/imagedev/midiin.cpp')
-rw-r--r--src/devices/imagedev/midiin.cpp319
1 files changed, 238 insertions, 81 deletions
diff --git a/src/devices/imagedev/midiin.cpp b/src/devices/imagedev/midiin.cpp
index 63afc1c83f5..62c8954e93e 100644
--- a/src/devices/imagedev/midiin.cpp
+++ b/src/devices/imagedev/midiin.cpp
@@ -10,39 +10,76 @@
#include "emu.h"
#include "midiin.h"
+
#include "osdepend.h"
+
/***************************************************************************
IMPLEMENTATION
***************************************************************************/
DEFINE_DEVICE_TYPE(MIDIIN, midiin_device, "midiin", "MIDI In image device")
+namespace {
+
+INPUT_PORTS_START(midiin)
+ PORT_START("CFG")
+ PORT_CONFNAME(0xff, 0xff, "MIDI file mode")
+ PORT_CONFSETTING( 0xff, "Multi")
+ PORT_CONFSETTING( 0x00, "Poly: Channel 1")
+ PORT_CONFSETTING( 0x01, "Poly: Channel 2")
+ PORT_CONFSETTING( 0x02, "Poly: Channel 3")
+ PORT_CONFSETTING( 0x03, "Poly: Channel 4")
+ PORT_CONFSETTING( 0x04, "Poly: Channel 5")
+ PORT_CONFSETTING( 0x05, "Poly: Channel 6")
+ PORT_CONFSETTING( 0x06, "Poly: Channel 7")
+ PORT_CONFSETTING( 0x07, "Poly: Channel 8")
+ PORT_CONFSETTING( 0x08, "Poly: Channel 9")
+ PORT_CONFSETTING( 0x09, "Poly: Channel 10")
+ PORT_CONFSETTING( 0x0a, "Poly: Channel 11")
+ PORT_CONFSETTING( 0x0b, "Poly: Channel 12")
+ PORT_CONFSETTING( 0x0c, "Poly: Channel 13")
+ PORT_CONFSETTING( 0x0d, "Poly: Channel 14")
+ PORT_CONFSETTING( 0x0e, "Poly: Channel 15")
+ PORT_CONFSETTING( 0x0f, "Poly: Channel 16")
+INPUT_PORTS_END
+
+} // anonymous namespace
+
/*-------------------------------------------------
ctor
-------------------------------------------------*/
midiin_device::midiin_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, MIDIIN, tag, owner, clock),
- device_image_interface(mconfig, *this),
- device_serial_interface(mconfig, *this),
- m_midi(),
- m_timer(nullptr),
- m_input_cb(*this),
- m_xmit_read(0),
- m_xmit_write(0),
- m_tx_busy(false)
+ : device_t(mconfig, MIDIIN, tag, owner, clock)
+ , device_image_interface(mconfig, *this)
+ , device_serial_interface(mconfig, *this)
+ , m_midi()
+ , m_config(*this, "CFG")
+ , m_timer(nullptr)
+ , m_input_cb(*this)
+ , m_xmit_read(0)
+ , m_xmit_write(0)
+ , m_tx_busy(false)
+{
+}
+
+midiin_device::~midiin_device()
{
}
+ioport_constructor midiin_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME(midiin);
+}
+
/*-------------------------------------------------
device_start
-------------------------------------------------*/
void midiin_device::device_start()
{
- m_input_cb.resolve_safe();
- m_timer = timer_alloc(0);
+ m_timer = timer_alloc(FUNC(midiin_device::midi_update), this);
m_midi.reset();
m_timer->enable(false);
}
@@ -59,62 +96,66 @@ void midiin_device::device_reset()
}
/*-------------------------------------------------
- device_timer
+ midi_update
-------------------------------------------------*/
-void midiin_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+TIMER_CALLBACK_MEMBER(midiin_device::midi_update)
{
- if (id == 0)
+ // if there's a sequence playing, that takes priority
+ midi_event *event = m_sequence.current_event();
+ if (event != nullptr)
{
- // if there's a sequence playing, that takes priority
- midi_event *event = m_sequence.current_event();
- if (event != nullptr)
+ attotime curtime = m_timer->expire();
+ if (curtime < m_sequence_start)
{
- attotime curtime = timer.expire();
- if (curtime < m_sequence_start)
- {
- // we could get called before we're supposed to start; show a countdown
- attotime delta = m_sequence_start - curtime;
- popmessage("Waiting to start MIDI playback... %d", delta.seconds());
- m_timer->adjust(std::min(delta, attotime(1, 0)));
- }
- else
+ // we could get called before we're supposed to start; show a countdown
+ attotime delta = m_sequence_start - curtime;
+ popmessage("Waiting to start MIDI playback... %d", delta.seconds());
+ m_timer->adjust(std::min(delta, attotime(1, 0)));
+ }
+ else
+ {
+ // update the playback time
+ curtime -= m_sequence_start;
+ popmessage("Playing MIDI file: %d:%02d / %d:%02d", curtime.seconds() / 60, curtime.seconds() % 60, m_sequence.duration().seconds() / 60, m_sequence.duration().seconds() % 60);
+
+ // if it's time to process the current event, do it and advance
+ if (curtime >= event->time())
{
- // update the playback time
- curtime -= m_sequence_start;
- popmessage("Playing MIDI file: %d:%02d / %d:%02d", curtime.seconds() / 60, curtime.seconds() % 60, m_sequence.duration().seconds() / 60, m_sequence.duration().seconds() % 60);
+ const u8 force_channel = m_config->read();
- // if it's time to process the current event, do it and advance
- if (curtime >= event->time())
+ for (u8 curbyte : event->data())
{
- for (auto &curbyte : event->data())
- xmit_char(curbyte);
- event = m_sequence.advance_event();
- }
+ if (force_channel <= 15 && curbyte >= 0x80 && curbyte < 0xf0)
+ curbyte = (curbyte & 0xf0) | force_channel;
- // if there are more events, set a timer to trigger them
- // (minimum duration 1 sec so that our playback time doesn't skip)
- if (event != nullptr)
- m_timer->adjust(std::min(event->time() - curtime, attotime(1, 0)));
- else
- popmessage("End of MIDI file");
+ xmit_char(curbyte);
+ }
+ event = m_sequence.advance_event();
}
+
+ // if there are more events, set a timer to trigger them
+ // (minimum duration 1 sec so that our playback time doesn't skip)
+ if (event != nullptr)
+ m_timer->adjust(std::min(event->time() - curtime, attotime(1, 0)));
+ else
+ popmessage("End of MIDI file");
}
- else if (m_midi)
+ }
+ else if (m_midi)
+ {
+ uint8_t buf[8192*4];
+ int bytesRead;
+
+ while (m_midi->poll())
{
- uint8_t buf[8192*4];
- int bytesRead;
+ bytesRead = m_midi->read(buf);
- while (m_midi->poll())
+ if (bytesRead > 0)
{
- bytesRead = m_midi->read(buf);
-
- if (bytesRead > 0)
+ for (int i = 0; i < bytesRead; i++)
{
- for (int i = 0; i < bytesRead; i++)
- {
- xmit_char(buf[i]);
- }
+ xmit_char(buf[i]);
}
}
}
@@ -125,35 +166,33 @@ void midiin_device::device_timer(emu_timer &timer, device_timer_id id, int param
call_load
-------------------------------------------------*/
-image_init_result midiin_device::call_load()
+std::pair<std::error_condition, std::string> midiin_device::call_load()
{
// attempt to load if it's a real file
- m_err = load_image_by_path(OPEN_FLAG_READ, filename());
- if (!m_err)
+ std::error_condition err = load_image_by_path(OPEN_FLAG_READ, filename());
+ if (!err)
{
// if the parsing succeeds, schedule the start to happen at least
// 10 seconds after starting to allow the keyboards to initialize
// TODO: this should perhaps be a driver-configurable parameter?
- if (m_sequence.parse(reinterpret_cast<u8 const *>(ptr()), length()))
+ err = m_sequence.parse(image_core_file(), length());
+ if (!err)
{
m_sequence_start = std::max(machine().time(), attotime(10, 0));
m_timer->adjust(attotime::zero);
- return image_init_result::PASS;
}
- return image_init_result::FAIL;
+ return std::make_pair(err, std::string());
}
else
{
- m_midi = machine().osd().create_midi_device();
-
- if (!m_midi->open_input(filename()))
+ m_midi = machine().osd().create_midi_input(filename());
+ if (!m_midi)
{
- m_midi.reset();
- return image_init_result::FAIL;
+ return std::make_pair(image_error::UNSPECIFIED, std::string());
}
m_timer->adjust(attotime::from_hz(1500), 0, attotime::from_hz(1500));
- return image_init_result::PASS;
+ return std::make_pair(std::error_condition(), std::string());
}
}
@@ -163,9 +202,15 @@ image_init_result midiin_device::call_load()
void midiin_device::call_unload()
{
- if (m_midi)
+ if (!m_midi)
{
- m_midi->close();
+ // send "all notes off" CC if unloading a MIDI file
+ for (u8 channel = 0; channel < 0x10; channel++)
+ {
+ xmit_char(0xb0 | channel);
+ xmit_char(123);
+ xmit_char(0);
+ }
}
m_midi.reset();
m_sequence.clear();
@@ -234,8 +279,8 @@ static constexpr u32 fourcc_le(char const *string)
// midi_parser - constructor
//-------------------------------------------------
-midiin_device::midi_parser::midi_parser(u8 const *data, u32 length, u32 offset) :
- m_data(data),
+midiin_device::midi_parser::midi_parser(util::random_read &stream, u32 length, u32 offset) :
+ m_stream(stream),
m_length(length),
m_offset(offset)
{
@@ -251,7 +296,7 @@ midiin_device::midi_parser::midi_parser(u8 const *data, u32 length, u32 offset)
midiin_device::midi_parser midiin_device::midi_parser::subset(u32 length)
{
check_bounds(length);
- midi_parser result(m_data + m_offset, length, 0);
+ midi_parser result(m_stream, m_offset + length, m_offset);
m_offset += length;
return result;
}
@@ -289,6 +334,91 @@ u32 midiin_device::midi_parser::variable()
//-------------------------------------------------
+// byte - read 8 bits of data
+//-------------------------------------------------
+
+u8 midiin_device::midi_parser::byte()
+{
+ check_bounds(1);
+
+ u8 result = 0;
+ auto const [err, actual] = read_at(m_stream, m_offset, &result, 1);
+ if (err || actual != 1)
+ throw error("Error reading data");
+ m_offset++;
+ return result;
+}
+
+
+//-------------------------------------------------
+// word_be - read 16 bits of big-endian data
+//-------------------------------------------------
+
+u16 midiin_device::midi_parser::word_be()
+{
+ check_bounds(2);
+
+ u16 result = 0;
+ auto const [err, actual] = read_at(m_stream, m_offset, &result, 2);
+ if (err || actual != 2)
+ throw error("Error reading data");
+ m_offset += 2;
+ return big_endianize_int16(result);
+}
+
+
+//-------------------------------------------------
+// triple_be - read 24 bits of big-endian data
+//-------------------------------------------------
+
+u32 midiin_device::midi_parser::triple_be()
+{
+ check_bounds(3);
+
+ u32 result = 0;
+ auto const [err, actual] = read_at(m_stream, m_offset, &result, 3);
+ if (err || actual != 3)
+ throw error("Error reading data");
+ m_offset += 3;
+ return big_endianize_int32(result) >> 8;
+}
+
+
+//-------------------------------------------------
+// dword_be - read 32 bits of big-endian data
+//-------------------------------------------------
+
+u32 midiin_device::midi_parser::dword_be()
+{
+ check_bounds(4);
+
+ u32 result = 0;
+ auto const [err, actual] = read_at(m_stream, m_offset, &result, 4);
+ if (err || actual != 4)
+ throw error("Error reading data");
+ m_offset += 4;
+ return big_endianize_int32(result);
+}
+
+
+//-------------------------------------------------
+// dword_le - read 32 bits of little-endian data
+//-------------------------------------------------
+
+u32 midiin_device::midi_parser::dword_le()
+{
+ check_bounds(4);
+
+ u32 result = 0;
+ auto const [err, actual] = read_at(m_stream, m_offset, &result, 4);
+ if (err || actual != 4)
+ throw error("Error reading data");
+ m_offset += 4;
+ return little_endianize_int32(result);
+}
+
+
+//-------------------------------------------------
// check_bounds - check to see if we have at least
// 'length' bytes left to consume; if not,
// throw an error
@@ -325,21 +455,19 @@ midiin_device::midi_event &midiin_device::midi_sequence::event_at(u32 tick)
// parse - parse a MIDI sequence from a buffer
//-------------------------------------------------
-bool midiin_device::midi_sequence::parse(u8 const *data, u32 length)
+std::error_condition midiin_device::midi_sequence::parse(util::random_read &stream, u32 length)
{
// start with an empty list of events
m_list.clear();
// by default parse the whole data
- midi_parser buffer(data, length, 0);
+ midi_parser buffer(stream, length, 0);
// catch errors to make parsing easier
try
{
- // if not a RIFF-encoed MIDI, just parse as-is
- if (buffer.dword_le() != fourcc_le("RIFF"))
- parse_midi_data(buffer.reset());
- else
+ const u32 type = buffer.dword_le();
+ if (type == fourcc_le("RIFF"))
{
// check the RIFF type and size
u32 riffsize = buffer.dword_le();
@@ -361,14 +489,20 @@ bool midiin_device::midi_sequence::parse(u8 const *data, u32 length)
}
}
}
+ else if ((u8)type == 0xf0)
+ parse_sysex_data(buffer.reset());
+ else
+ parse_midi_data(buffer.reset());
+
m_iterator = m_list.begin();
- return true;
+ return std::error_condition();
}
- catch (midi_parser::error &)
+ catch (midi_parser::error &err)
{
+ osd_printf_error("MIDI file error: %s\n", err.description());
m_list.clear();
m_iterator = m_list.begin();
- return false;
+ return image_error::UNSPECIFIED;
}
}
@@ -469,9 +603,7 @@ u32 midiin_device::midi_sequence::parse_track_data(midi_parser &buffer, u32 star
if (eclass != 15)
{
// simple events: all but program change and aftertouch have a second parameter
- // TODO: should we respect the channel for these? or maybe the drivers should
- // configure us with the number of channels they support?
- event.append(type & 0xf0);
+ event.append(type);
event.append(buffer.byte());
if (eclass != 12 && eclass != 13)
event.append(buffer.byte());
@@ -480,6 +612,7 @@ u32 midiin_device::midi_sequence::parse_track_data(midi_parser &buffer, u32 star
{
// handle non-meta events
midi_parser eventdata = buffer.subset(buffer.variable());
+ event.append(type);
while (!eventdata.eob())
event.append(eventdata.byte());
}
@@ -505,3 +638,27 @@ u32 midiin_device::midi_sequence::parse_track_data(midi_parser &buffer, u32 star
}
return curtick;
}
+
+//-------------------------------------------------
+// parse_sysex_data - parse a sysex dump into a
+// single MIDI event
+//-------------------------------------------------
+
+void midiin_device::midi_sequence::parse_sysex_data(midi_parser &buffer)
+{
+ u32 msg = 0;
+ attotime curtime;
+ while (!buffer.eob())
+ {
+ midi_event &event = event_at(msg++);
+ event.set_time(curtime);
+
+ u8 data = 0;
+ while (!buffer.eob() && data != 0xf7)
+ event.append(data = buffer.byte());
+
+ // add 100 ms between the end of this sysex and the start of the next one, if there is one
+ curtime += attotime::from_ticks((u64)10 * event.data().size(), 31250)
+ + attotime::from_msec(100);
+ }
+}