aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/adlmidi.cpp57
-rw-r--r--src/adlmidi_midiplay.hpp11
-rw-r--r--src/adlmidi_sequencer.cpp5
-rw-r--r--src/cvt_xmi2mid.hpp100
-rw-r--r--src/midi_sequencer.hpp12
-rw-r--r--src/midi_sequencer_impl.hpp71
6 files changed, 240 insertions, 16 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp
index a9ec918..f613544 100644
--- a/src/adlmidi.cpp
+++ b/src/adlmidi.cpp
@@ -597,6 +597,19 @@ ADLMIDI_EXPORT void adl_setLoopCount(ADL_MIDIPlayer *device, int loopCount)
#endif
}
+ADLMIDI_EXPORT void adl_setLoopHooksOnly(ADL_MIDIPlayer *device, int loopHooksOnly)
+{
+#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
+ if(!device)
+ return;
+ MidiPlayer *play = GET_MIDI_PLAYER(device);
+ assert(play);
+ play->m_sequencer->setLoopHooksOnly(loopHooksOnly);
+#else
+ ADL_UNUSED(device);
+ ADL_UNUSED(loopHooksOnly);
+#endif
+}
ADLMIDI_EXPORT void adl_setSoftPanEnabled(ADL_MIDIPlayer *device, int softPanEn)
{
@@ -772,6 +785,20 @@ ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, const void *mem, unsigne
return -1;
}
+ADLMIDI_EXPORT void adl_selectSongNum(struct ADL_MIDIPlayer *device, int songNumber)
+{
+#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
+ if(!device)
+ return;
+
+ MidiPlayer *play = GET_MIDI_PLAYER(device);
+ assert(play);
+ play->m_sequencer->setLoadTrack(songNumber);
+#else
+ ADL_UNUSED(device);
+ ADL_UNUSED(songNumber);
+#endif
+}
ADLMIDI_EXPORT const char *adl_emulatorName()
{
@@ -1139,6 +1166,36 @@ ADLMIDI_EXPORT void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_D
#endif
}
+/* Set loop start hook */
+ADLMIDI_EXPORT void adl_setLoopStartHook(struct ADL_MIDIPlayer *device, ASL_LoopPointHook loopStartHook, void *userData)
+{
+ if(!device)
+ return;
+ MidiPlayer *play = GET_MIDI_PLAYER(device);
+ assert(play);
+ play->hooks.onLoopStart = loopStartHook;
+ play->hooks.onLoopStart_userData = userData;
+#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
+ play->m_sequencerInterface->onloopStart = loopStartHook;
+ play->m_sequencerInterface->onloopStart_userData = userData;
+#endif
+}
+
+/* Set loop end hook */
+ADLMIDI_EXPORT void adl_setLoopEndHook(struct ADL_MIDIPlayer *device, ASL_LoopPointHook loopEndHook, void *userData)
+{
+ if(!device)
+ return;
+ MidiPlayer *play = GET_MIDI_PLAYER(device);
+ assert(play);
+ play->hooks.onLoopEnd = loopEndHook;
+ play->hooks.onLoopEnd_userData = userData;
+#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER
+ play->m_sequencerInterface->onloopEnd = loopEndHook;
+ play->m_sequencerInterface->onloopEnd_userData = userData;
+#endif
+}
+
#ifndef ADLMIDI_HW_OPL
# ifndef __WATCOMC__
diff --git a/src/adlmidi_midiplay.hpp b/src/adlmidi_midiplay.hpp
index 181350a..5208f7b 100644
--- a/src/adlmidi_midiplay.hpp
+++ b/src/adlmidi_midiplay.hpp
@@ -37,6 +37,10 @@ struct MIDIEventHooks
MIDIEventHooks() :
onNote(NULL),
onNote_userData(NULL),
+ onLoopStart(NULL),
+ onLoopStart_userData(NULL),
+ onLoopEnd(NULL),
+ onLoopEnd_userData(NULL),
onDebugMessage(NULL),
onDebugMessage_userData(NULL)
{}
@@ -46,6 +50,13 @@ struct MIDIEventHooks
NoteHook onNote;
void *onNote_userData;
+ // Loop start/end hooks
+ ASL_LoopPointHook onLoopStart;
+ void *onLoopStart_userData;
+ ASL_LoopPointHook onLoopEnd;
+ void *onLoopEnd_userData;
+
+
//! Library internal debug messages
typedef void (*DebugMessageHook)(void *userdata, const char *fmt, ...);
DebugMessageHook onDebugMessage;
diff --git a/src/adlmidi_sequencer.cpp b/src/adlmidi_sequencer.cpp
index ce68ca8..53338e0 100644
--- a/src/adlmidi_sequencer.cpp
+++ b/src/adlmidi_sequencer.cpp
@@ -141,6 +141,11 @@ void MIDIplay::initSequencerInterface()
seq->onSongStart = rtSongBegin;
seq->onSongStart_userData = this;
+
+ seq->onloopStart = hooks.onLoopStart;
+ seq->onloopStart_userData = hooks.onLoopStart_userData;
+ seq->onloopEnd = hooks.onLoopEnd;
+ seq->onloopEnd_userData = hooks.onLoopEnd_userData;
/* NonStandard calls End */
m_sequencer->setInterface(seq);
diff --git a/src/cvt_xmi2mid.hpp b/src/cvt_xmi2mid.hpp
index 84c2f83..181c7db 100644
--- a/src/cvt_xmi2mid.hpp
+++ b/src/cvt_xmi2mid.hpp
@@ -31,6 +31,8 @@
#include <stdint.h>
#include <assert.h>
+#include <vector>
+
#ifdef __DJGPP__
typedef signed char int8_t;
typedef unsigned char uint8_t;
@@ -124,7 +126,7 @@ static int32_t xmi2mid_ConvertSystemMessage(struct xmi2mid_xmi_ctx *ctx,
static int32_t xmi2mid_ConvertFiletoList(struct xmi2mid_xmi_ctx *ctx, const xmi2mid_rbrn *rbrn);
static uint32_t xmi2mid_ConvertListToMTrk(struct xmi2mid_xmi_ctx *ctx, midi_event *mlist);
static int xmi2mid_ParseXMI(struct xmi2mid_xmi_ctx *ctx);
-static int xmi2mid_ExtractTracks(struct xmi2mid_xmi_ctx *ctx);
+static int xmi2mid_ExtractTracks(struct xmi2mid_xmi_ctx *ctx, int32_t dstTrackNumber);
static uint32_t xmi2mid_ExtractTracksFromXmi(struct xmi2mid_xmi_ctx *ctx);
static uint32_t xmi2mid_read1(struct xmi2mid_xmi_ctx *ctx)
@@ -517,11 +519,12 @@ static const char xmi2mid_mt32asgs[256] = {
static int Convert_xmi2midi(uint8_t *in, uint32_t insize,
uint8_t **out, uint32_t *outsize,
- uint32_t convert_type)
+ uint32_t convert_type, int32_t trackNumber = -1)
{
struct xmi2mid_xmi_ctx ctx;
unsigned int i;
int ret = -1;
+ int hasSelectedTrack = 0;
if (convert_type > XMIDI_CONVERT_MT32_TO_GS) {
/*_WM_ERROR_NEW("%s:%i: %d is an invalid conversion type.", __FUNCTION__, __LINE__, convert_type);*/
@@ -539,11 +542,15 @@ static int Convert_xmi2midi(uint8_t *in, uint32_t insize,
goto _end;
}
- if (xmi2mid_ExtractTracks(&ctx) < 0) {
+ if (xmi2mid_ExtractTracks(&ctx, trackNumber) < 0) {
/*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MIDI, NULL, 0);*/
goto _end;
}
+ hasSelectedTrack = trackNumber >= 0;
+ if (trackNumber >= ctx.info.tracks)
+ trackNumber = ctx.info.tracks - 1;
+
ctx.dst = (uint8_t*)malloc(DST_CHUNK);
ctx.dst_ptr = ctx.dst;
ctx.dstsize = DST_CHUNK;
@@ -558,10 +565,12 @@ static int Convert_xmi2midi(uint8_t *in, uint32_t insize,
xmi2mid_write4(&ctx, 6);
xmi2mid_write2(&ctx, ctx.info.type);
- xmi2mid_write2(&ctx, ctx.info.tracks);
- xmi2mid_write2(&ctx, ctx.timing[0]);/* write divisions from track0 */
+ xmi2mid_write2(&ctx, hasSelectedTrack ? 1 : ctx.info.tracks);
+ xmi2mid_write2(&ctx, ctx.timing[hasSelectedTrack ? trackNumber : 0]);/* write divisions from track0 */
- for (i = 0; i < ctx.info.tracks; i++)
+ if (hasSelectedTrack)
+ xmi2mid_ConvertListToMTrk(&ctx, ctx.events[trackNumber]);
+ else for (i = 0; i < ctx.info.tracks; i++)
xmi2mid_ConvertListToMTrk(&ctx, ctx.events[i]);
*out = ctx.dst;
*outsize = ctx.dstsize - ctx.dstrem;
@@ -583,6 +592,81 @@ _end: /* cleanup */
return (ret);
}
+static int Convert_xmi2midi_multi(uint8_t *in, uint32_t insize,
+ std::vector<uint8_t *> &out, std::vector<uint32_t> &outsize,
+ uint32_t convert_type)
+{
+ struct xmi2mid_xmi_ctx ctx;
+ unsigned int i;
+ int ret = -1;
+
+ if (convert_type > XMIDI_CONVERT_MT32_TO_GS) {
+ /*_WM_ERROR_NEW("%s:%i: %d is an invalid conversion type.", __FUNCTION__, __LINE__, convert_type);*/
+ return (ret);
+ }
+
+ memset(&ctx, 0, sizeof(struct xmi2mid_xmi_ctx));
+ ctx.src = ctx.src_ptr = in;
+ ctx.srcsize = insize;
+ ctx.src_end = ctx.src + insize;
+ ctx.convert_type = convert_type;
+
+ if (xmi2mid_ParseXMI(&ctx) < 0) {
+ /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_XMI, NULL, 0);*/
+ goto _end;
+ }
+
+ if (xmi2mid_ExtractTracks(&ctx, 0) < 0) {
+ /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MIDI, NULL, 0);*/
+ goto _end;
+ }
+
+ for (i = 0; i < ctx.info.tracks; i++)
+ {
+ ctx.dst = (uint8_t*)malloc(DST_CHUNK);
+ ctx.dst_ptr = ctx.dst;
+ ctx.dstsize = DST_CHUNK;
+ ctx.dstrem = DST_CHUNK;
+
+ /* Header is 14 bytes long and add the rest as well */
+ xmi2mid_write1(&ctx, 'M');
+ xmi2mid_write1(&ctx, 'T');
+ xmi2mid_write1(&ctx, 'h');
+ xmi2mid_write1(&ctx, 'd');
+
+ xmi2mid_write4(&ctx, 6);
+
+ xmi2mid_write2(&ctx, ctx.info.type);
+ xmi2mid_write2(&ctx, 1);
+ xmi2mid_write2(&ctx, ctx.timing[i]);/* write divisions from track0 */
+
+ xmi2mid_ConvertListToMTrk(&ctx, ctx.events[i]);
+ out.push_back(ctx.dst);
+ outsize.push_back(ctx.dstsize - ctx.dstrem);
+ }
+
+ ret = 0;
+
+_end: /* cleanup */
+ if (ret < 0) {
+ for(size_t j = 0; j < out.size(); ++j)
+ {
+ free(out[j]);
+ out[j] = NULL;
+ }
+ out.clear();
+ outsize.clear();
+ }
+ if (ctx.events) {
+ for (i = 0; i < ctx.info.tracks; i++)
+ xmi2mid_DeleteEventList(ctx.events[i]);
+ free(ctx.events);
+ }
+ free(ctx.timing);
+
+ return (ret);
+}
+
static void xmi2mid_DeleteEventList(midi_event *mlist) {
midi_event *event;
midi_event *next;
@@ -1222,13 +1306,13 @@ badfile: /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too shor
return (-1);
}
-static int xmi2mid_ExtractTracks(struct xmi2mid_xmi_ctx *ctx) {
+static int xmi2mid_ExtractTracks(struct xmi2mid_xmi_ctx *ctx, int32_t dstTrackNumber) {
uint32_t i;
ctx->events = (midi_event **)calloc(ctx->info.tracks, sizeof(midi_event*));
ctx->timing = (int16_t *)calloc(ctx->info.tracks, sizeof(int16_t));
/* type-2 for multi-tracks, type-0 otherwise */
- ctx->info.type = (ctx->info.tracks > 1)? 2 : 0;
+ ctx->info.type = (ctx->info.tracks > 1 && (dstTrackNumber < 0 || ctx->info.tracks >= dstTrackNumber))? 2 : 0;
xmi2mid_seeksrc(ctx, ctx->datastart);
i = xmi2mid_ExtractTracksFromXmi(ctx);
diff --git a/src/midi_sequencer.hpp b/src/midi_sequencer.hpp
index 8872fde..2cba080 100644
--- a/src/midi_sequencer.hpp
+++ b/src/midi_sequencer.hpp
@@ -387,6 +387,12 @@ private:
//! Set the number of loops limit. Lesser than 0 - loop infinite
int m_loopCount;
+ //! The number of track of multi-track file (for exmaple, XMI) to load
+ int m_loadTrackNumber;
+
+ //! The XMI-specific list of raw songs, converted into SMF format
+ std::vector<std::vector<uint8_t > > m_rawSongsData;
+
/**
* @brief Loop stack entry
*/
@@ -612,6 +618,12 @@ public:
void setSoloTrack(size_t track);
/**
+ * @brief Set the track number of a multi-track file (such as XMI)
+ * @param trackNumber Identifier of the track to load (or -1 to mix all tracks as one song)
+ */
+ void setLoadTrack(int track);
+
+ /**
* @brief Defines a handler for callback trigger events
* @param handler Handler to invoke from the sequencer when triggered, or NULL.
* @param userData Instance of the library
diff --git a/src/midi_sequencer_impl.hpp b/src/midi_sequencer_impl.hpp
index bfd7c40..3c94968 100644
--- a/src/midi_sequencer_impl.hpp
+++ b/src/midi_sequencer_impl.hpp
@@ -315,6 +315,7 @@ BW_MidiSequencer::BW_MidiSequencer() :
m_tempoMultiplier(1.0),
m_atEnd(false),
m_loopCount(-1),
+ m_loadTrackNumber(0),
m_trackSolo(~static_cast<size_t>(0)),
m_triggerHandler(NULL),
m_triggerUserData(NULL)
@@ -450,6 +451,37 @@ void BW_MidiSequencer::setSoloTrack(size_t track)
m_trackSolo = track;
}
+void BW_MidiSequencer::setLoadTrack(int track)
+{
+ m_loadTrackNumber = track;
+
+ if(!m_rawSongsData.empty() && m_format == Format_XMIDI) // Reload the song
+ {
+ if(m_loadTrackNumber >= (int)m_rawSongsData.size())
+ m_loadTrackNumber = m_rawSongsData.size() - 1;
+
+ if(m_interface && m_interface->rt_controllerChange)
+ {
+ for(int i = 0; i < 15; i++)
+ m_interface->rt_controllerChange(m_interface->rtUserData, i, 123, 0);
+ }
+
+ m_atEnd = false;
+ m_loop.fullReset();
+ m_loop.caughtStart = true;
+
+ m_smfFormat = 0;
+
+ FileAndMemReader fr;
+ fr.openData(m_rawSongsData[m_loadTrackNumber].data(),
+ m_rawSongsData[m_loadTrackNumber].size());
+ parseSMF(fr);
+
+ m_format = Format_XMIDI;
+ }
+}
+
+
void BW_MidiSequencer::setTriggerHandler(TriggerHandler handler, void *userData)
{
m_triggerHandler = handler;
@@ -2285,6 +2317,7 @@ bool BW_MidiSequencer::loadMIDI(FileAndMemReader &fr)
m_smfFormat = 0;
m_cmfInstruments.clear();
+ m_rawSongsData.clear();
const size_t headerSize = 4 + 4 + 2 + 2 + 2; // 14
char headerBuf[headerSize] = "";
@@ -2888,7 +2921,12 @@ bool BW_MidiSequencer::parseXMI(FileAndMemReader &fr)
const size_t headerSize = 14;
char headerBuf[headerSize] = "";
size_t fsize = 0;
- BufferGuard<uint8_t> cvt_buf;
+// BufferGuard<uint8_t> cvt_buf;
+ std::vector<uint8_t *> song_buf;
+ std::vector<uint32_t> song_size;
+ bool ret;
+
+ (void)Convert_xmi2midi; /* Shut up the warning */
fsize = fr.read(headerBuf, 1, headerSize);
if(fsize < headerSize)
@@ -2931,24 +2969,41 @@ bool BW_MidiSequencer::parseXMI(FileAndMemReader &fr)
// Close source stream
fr.close();
- uint8_t *mid = NULL;
- uint32_t mid_len = 0;
- int m2mret = Convert_xmi2midi(mus, static_cast<uint32_t>(mus_len + 20),
- &mid, &mid_len, XMIDI_CONVERT_NOCONVERSION);
+// uint8_t *mid = NULL;
+// uint32_t mid_len = 0;
+ int m2mret = Convert_xmi2midi_multi(mus, static_cast<uint32_t>(mus_len + 20),
+ song_buf, song_size, XMIDI_CONVERT_NOCONVERSION);
if(mus)
free(mus);
if(m2mret < 0)
{
+ for(size_t i = 0; i < song_buf.size(); ++i)
+ std::free(song_buf[i]);
m_errorString = "Invalid XMI data format!";
return false;
}
- cvt_buf.set(mid);
+ if(m_loadTrackNumber >= (int)song_buf.size())
+ m_loadTrackNumber = song_buf.size() - 1;
+
+ for(size_t i = 0; i < song_buf.size(); ++i)
+ {
+ m_rawSongsData.push_back(std::vector<uint8_t >(song_buf[i], song_buf[i] + song_size[i]));
+ std::free(song_buf[i]);
+ }
+
+ song_buf.clear();
+ song_size.clear();
+
+ // cvt_buf.set(mid);
// Open converted MIDI file
- fr.openData(mid, static_cast<size_t>(mid_len));
+ fr.openData(m_rawSongsData[m_loadTrackNumber].data(),
+ m_rawSongsData[m_loadTrackNumber].size());
// Set format as XMIDI
m_format = Format_XMIDI;
- return parseSMF(fr);
+ ret = parseSMF(fr);
+
+ return ret;
}
#endif