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.cpp297
1 files changed, 210 insertions, 87 deletions
diff --git a/src/devices/imagedev/midiin.cpp b/src/devices/imagedev/midiin.cpp
index 30838a36eb9..62c8954e93e 100644
--- a/src/devices/imagedev/midiin.cpp
+++ b/src/devices/imagedev/midiin.cpp
@@ -10,8 +10,10 @@
#include "emu.h"
#include "midiin.h"
+
#include "osdepend.h"
+
/***************************************************************************
IMPLEMENTATION
***************************************************************************/
@@ -49,16 +51,20 @@ INPUT_PORTS_END
-------------------------------------------------*/
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_config(*this, "CFG"),
- 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()
{
}
@@ -73,8 +79,7 @@ ioport_constructor midiin_device::device_input_ports() const
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);
}
@@ -91,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]);
}
}
}
@@ -157,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(), m_config->read()))
+ 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());
}
}
@@ -195,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();
@@ -266,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)
{
@@ -283,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;
}
@@ -321,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
@@ -357,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, u8 force_channel)
+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(), force_channel);
- else
+ const u32 type = buffer.dword_le();
+ if (type == fourcc_le("RIFF"))
{
// check the RIFF type and size
u32 riffsize = buffer.dword_le();
@@ -388,19 +484,25 @@ bool midiin_device::midi_sequence::parse(u8 const *data, u32 length, u8 force_ch
midi_parser chunk = riffdata.subset(chunksize);
if (chunktype == fourcc_le("data"))
{
- parse_midi_data(chunk, force_channel);
+ parse_midi_data(chunk);
break;
}
}
}
+ 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;
}
}
@@ -410,7 +512,7 @@ bool midiin_device::midi_sequence::parse(u8 const *data, u32 length, u8 force_ch
// into tracks
//-------------------------------------------------
-void midiin_device::midi_sequence::parse_midi_data(midi_parser &buffer, u8 force_channel)
+void midiin_device::midi_sequence::parse_midi_data(midi_parser &buffer)
{
// scan for syntactic correctness, and to find global state
u32 headertype = buffer.dword_le();
@@ -447,7 +549,7 @@ void midiin_device::midi_sequence::parse_midi_data(midi_parser &buffer, u8 force
// parse the track data
midi_parser trackdata = buffer.subset(chunksize);
- u32 numticks = parse_track_data(trackdata, curtick, force_channel);
+ u32 numticks = parse_track_data(trackdata, curtick);
if (format == 2)
curtick += numticks;
}
@@ -476,7 +578,7 @@ void midiin_device::midi_sequence::parse_midi_data(midi_parser &buffer, u8 force
// add it to the buffer
//-------------------------------------------------
-u32 midiin_device::midi_sequence::parse_track_data(midi_parser &buffer, u32 start_tick, u8 force_channel)
+u32 midiin_device::midi_sequence::parse_track_data(midi_parser &buffer, u32 start_tick)
{
u32 curtick = start_tick;
u8 last_type = 0;
@@ -501,10 +603,6 @@ 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
- if (force_channel <= 15)
- {
- type = (type & 0xf0) | force_channel;
- }
event.append(type);
event.append(buffer.byte());
if (eclass != 12 && eclass != 13)
@@ -514,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());
}
@@ -539,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);
+ }
+}