diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/adlmidi.cpp | 57 | ||||
-rw-r--r-- | src/adlmidi_midiplay.hpp | 11 | ||||
-rw-r--r-- | src/adlmidi_sequencer.cpp | 5 | ||||
-rw-r--r-- | src/cvt_xmi2mid.hpp | 100 | ||||
-rw-r--r-- | src/midi_sequencer.hpp | 12 | ||||
-rw-r--r-- | src/midi_sequencer_impl.hpp | 71 |
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 |