aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt7
-rw-r--r--src/cvt_xmi2mid.hpp108
-rw-r--r--utils/xmi2mid/xmi2mid.cpp74
3 files changed, 186 insertions, 3 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5283a18..ab3e7e5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -87,6 +87,7 @@ option(MIDIPLAY_WAVE_ONLY "Build Demo MIDI player without support of real time
option(WITH_ADLMIDI2 "Build also classic ADLMIDI player [EXPERIMENTAL]" OFF)
option(WITH_VLC_PLUGIN "Build also a plugin for VLC Media Player" OFF)
option(WITH_OLD_UTILS "Build also old utilities" OFF)
+option(WITH_XMI2MID "Build a XMI to MIDI converter" OFF)
option(EXAMPLE_SDL2_AUDIO "Build also a simple SDL2 demo MIDI player" OFF)
@@ -437,6 +438,11 @@ if(WITH_ADLMIDI2)
list(APPEND libADLMIDI_INSTALLS adlmidi2)
endif()
+if(WITH_XMI2MID)
+ add_executable(xmi2mid
+ ${libADLMIDI_SOURCE_DIR}/utils/xmi2mid/xmi2mid.cpp)
+endif()
+
if(EXAMPLE_SDL2_AUDIO AND NOT MSDOS AND NOT DJGPP)
find_library(SDL2_LIBRARY SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIR})
@@ -557,4 +563,5 @@ message("MIDIPLAY_WAVE_ONLY = ${MIDIPLAY_WAVE_ONLY}")
message("WITH_ADLMIDI2 = ${WITH_ADLMIDI2}")
message("WITH_VLC_PLUGIN = ${WITH_VLC_PLUGIN}")
message("WITH_OLD_UTILS = ${WITH_OLD_UTILS}")
+message("WITH_XMI2MID = ${WITH_XMI2MID}")
message("EXAMPLE_SDL2_AUDIO = ${EXAMPLE_SDL2_AUDIO}")
diff --git a/src/cvt_xmi2mid.hpp b/src/cvt_xmi2mid.hpp
index c164e3d..decb25c 100644
--- a/src/cvt_xmi2mid.hpp
+++ b/src/cvt_xmi2mid.hpp
@@ -57,6 +57,14 @@ typedef unsigned long uint32_t;
#define XMI2MID_MIDI_STATUS_PITCH_WHEEL 0xE
#define XMI2MID_MIDI_STATUS_SYSEX 0xF
+#if 1
+#define XMI2MID_TRACE(...)
+#else
+#include <stdio.h>
+#define XMI2MID_TRACE(fmt, ...) \
+ fprintf(stderr, "XMI2MID: " fmt "\n", ## __VA_ARGS__)
+#endif
+
typedef struct _xmi2mid_midi_event {
int32_t time;
uint8_t status;
@@ -86,6 +94,12 @@ struct xmi2mid_xmi_ctx {
midi_event *current;
};
+typedef struct {
+ unsigned count;
+ uint8_t id[128];
+ uint32_t offset[128];
+} xmi2mid_rbrn;
+
/* forward declarations of private functions */
static void xmi2mid_DeleteEventList(midi_event *mlist);
static void xmi2mid_CreateNewEvent(struct xmi2mid_xmi_ctx *ctx, int32_t time); /* List manipulation */
@@ -96,7 +110,7 @@ static int xmi2mid_ConvertEvent(struct xmi2mid_xmi_ctx *ctx,
const int32_t time, const uint8_t status, const int size);
static int32_t xmi2mid_ConvertSystemMessage(struct xmi2mid_xmi_ctx *ctx,
const int32_t time, const uint8_t status);
-static int32_t xmi2mid_ConvertFiletoList(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);
@@ -127,6 +141,16 @@ static uint32_t xmi2mid_read4(struct xmi2mid_xmi_ctx *ctx)
return (b0 + ((uint32_t)b1<<8) + ((uint32_t)b2<<16) + ((uint32_t)b3<<24));
}
+static uint32_t xmi2mid_read4le(struct xmi2mid_xmi_ctx *ctx)
+{
+ uint8_t b0, b1, b2, b3;
+ b3 = *ctx->src_ptr++;
+ b2 = *ctx->src_ptr++;
+ b1 = *ctx->src_ptr++;
+ b0 = *ctx->src_ptr++;
+ return (b3 + ((uint32_t)b2<<8) + ((uint32_t)b1<<16) + ((uint32_t)b0<<24));
+}
+
static void xmi2mid_copy(struct xmi2mid_xmi_ctx *ctx, char *b, uint32_t len)
{
memcpy(b, ctx->src_ptr, len);
@@ -779,7 +803,7 @@ static int32_t xmi2mid_ConvertSystemMessage(struct xmi2mid_xmi_ctx *ctx, const i
/* XMIDI and Midi to List
* Returns XMIDI PPQN */
-static int32_t xmi2mid_ConvertFiletoList(struct xmi2mid_xmi_ctx *ctx) {
+static int32_t xmi2mid_ConvertFiletoList(struct xmi2mid_xmi_ctx *ctx, const xmi2mid_rbrn *rbrn) {
int32_t time = 0;
uint32_t data;
int32_t end = 0;
@@ -787,6 +811,7 @@ static int32_t xmi2mid_ConvertFiletoList(struct xmi2mid_xmi_ctx *ctx) {
int32_t tempo_set = 0;
uint32_t status = 0;
uint32_t file_size = xmi2mid_getsrcsize(ctx);
+ uint32_t begin = xmi2mid_getsrcpos(ctx);
/* Set Drum track to correct setting if required */
if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127) {
@@ -797,6 +822,32 @@ static int32_t xmi2mid_ConvertFiletoList(struct xmi2mid_xmi_ctx *ctx) {
}
while (!end && xmi2mid_getsrcpos(ctx) < file_size) {
+ uint32_t offset = xmi2mid_getsrcpos(ctx) - begin;
+
+ /* search for branch to this offset */
+ for (unsigned i = 0, n = rbrn->count; i < n; ++i) {
+ if (offset == rbrn->offset[i]) {
+ unsigned id = rbrn->id[i];
+
+ xmi2mid_CreateNewEvent(ctx, time);
+
+ uint8_t *marker = (uint8_t *)malloc(sizeof(uint8_t)*8);
+ memcpy(marker, ":XBRN:", 6);
+ const char hex[] = "0123456789ABCDEF";
+ marker[6] = hex[id >> 4];
+ marker[7] = hex[id & 15];
+
+ XMI2MID_TRACE("Branch %u @ %u marker \"%.8s\"",
+ id, offset, marker);
+
+ ctx->current->status = 0xFF;
+ ctx->current->data[0] = 0x06;
+ ctx->current->len = 8;
+
+ ctx->current->buffer = marker;
+ }
+ }
+
xmi2mid_GetVLQ2(ctx, &data);
time += data * 3;
@@ -950,6 +1001,11 @@ static uint32_t xmi2mid_ExtractTracksFromXmi(struct xmi2mid_xmi_ctx *ctx) {
uint32_t len = 0;
int32_t begin;
char buf[32];
+ uint32_t branch[128];
+
+ /* clear branch points */
+ for (unsigned i = 0; i < 128; ++i)
+ branch[i] = ~0u;
while (xmi2mid_getsrcpos(ctx) < xmi2mid_getsrcsize(ctx) && num != ctx->info.tracks) {
/* Read first 4 bytes of name */
@@ -963,6 +1019,36 @@ static uint32_t xmi2mid_ExtractTracksFromXmi(struct xmi2mid_xmi_ctx *ctx) {
len = xmi2mid_read4(ctx);
}
+ if (!memcmp(buf, "RBRN", 4)) {
+ begin = xmi2mid_getsrcpos(ctx);
+ uint32_t count;
+
+ if (len < 2) {
+ /* insufficient data */
+ goto rbrn_nodata;
+ }
+
+ count = xmi2mid_read2(ctx);
+ if (len - 2 < 6 * count) {
+ /* insufficient data */
+ goto rbrn_nodata;
+ }
+
+ for (uint32_t i = 0; i < count; ++i) {
+ /* read branch point as byte offset */
+ uint32_t ctlvalue = xmi2mid_read2(ctx);
+ uint32_t evtoffset = xmi2mid_read4le(ctx);
+ if(ctlvalue < 128)
+ branch[ctlvalue] = evtoffset;
+ XMI2MID_TRACE("RBRN %u/%u: id %u -> offset %u",
+ i + 1, count, ctlvalue, evtoffset);
+ }
+
+ rbrn_nodata:
+ xmi2mid_seeksrc(ctx, begin + ((len + 1) & ~1));
+ continue;
+ }
+
if (memcmp(buf, "EVNT", 4)) {
xmi2mid_skipsrc(ctx, (len + 1) & ~1);
continue;
@@ -971,8 +1057,20 @@ static uint32_t xmi2mid_ExtractTracksFromXmi(struct xmi2mid_xmi_ctx *ctx) {
ctx->list = NULL;
begin = xmi2mid_getsrcpos(ctx);
+ /* Rearrange branches as structure */
+ xmi2mid_rbrn rbrn;
+ rbrn.count = 0;
+ for (unsigned i = 0; i < 128; ++i) {
+ if (branch[i] != ~0u) {
+ unsigned index = rbrn.count;
+ rbrn.id[index] = i;
+ rbrn.offset[index] = branch[i];
+ rbrn.count = index + 1;
+ }
+ }
+
/* Convert it */
- if (!(ppqn = xmi2mid_ConvertFiletoList(ctx))) {
+ if (!(ppqn = xmi2mid_ConvertFiletoList(ctx, &rbrn))) {
/*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, NULL, 0);*/
break;
}
@@ -984,6 +1082,10 @@ static uint32_t xmi2mid_ExtractTracksFromXmi(struct xmi2mid_xmi_ctx *ctx) {
/* go to start of next track */
xmi2mid_seeksrc(ctx, begin + ((len + 1) & ~1));
+
+ /* clear branch points */
+ for (unsigned i = 0; i < 128; ++i)
+ branch[i] = ~0u;
}
/* Return how many were converted */
diff --git a/utils/xmi2mid/xmi2mid.cpp b/utils/xmi2mid/xmi2mid.cpp
new file mode 100644
index 0000000..65d5858
--- /dev/null
+++ b/utils/xmi2mid/xmi2mid.cpp
@@ -0,0 +1,74 @@
+
+#include "cvt_xmi2mid.hpp"
+#include <stdio.h>
+#include <sys/stat.h>
+#if !defined(_WIN32)
+#include <unistd.h>
+#else
+#include <io.h>
+#define fileno(fd) _fileno(fd)
+#define isatty(fd) _isatty(fd)
+#endif
+
+int main(int argc, char *argv[])
+{
+ if(argc != 2)
+ {
+ fprintf(stderr, "Usage: xmi2mid <midi-file>\n");
+ return 1;
+ }
+
+ const char *filename = argv[1];
+
+ FILE *fh = fopen(filename, "rb");
+ if(!fh)
+ {
+ fprintf(stderr, "Error opening file.\n");
+ return 1;
+ }
+
+ struct stat st;
+ if(fstat(fileno(fh), &st) != 0)
+ {
+ fprintf(stderr, "Error reading file status.\n");
+ return 1;
+ }
+
+ size_t insize = (size_t)st.st_size;
+ if(insize > 8 * 1024 * 1024)
+ {
+ fprintf(stderr, "File too large.\n");
+ return 1;
+ }
+
+ uint8_t *filedata = new uint8_t[insize];
+ if(fread(filedata, 1, insize, fh) != insize)
+ {
+ fprintf(stderr, "Error reading file data.\n");
+ return 1;
+ }
+
+ uint8_t *xmidata = NULL;
+ uint32_t xmisize = 0;
+ if(Convert_xmi2midi(filedata, insize, &xmidata, &xmisize, XMIDI_CONVERT_NOCONVERSION) < 0)
+ {
+ fprintf(stderr, "Error converting XMI to SMF.\n");
+ return 1;
+ }
+
+ FILE *out = stdout;
+ if(isatty(fileno(out)))
+ {
+ fprintf(stderr, "Not writing SMF data on the text terminal.\n");
+ }
+ else
+ {
+ if (fwrite(xmidata, 1, xmisize, out) != xmisize || fflush(out) != 0)
+ {
+ fprintf(stderr, "Error writing SMF data.\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}