/* * XMIDI: Miles XMIDI to MID Library * * Copyright (C) 2001 Ryan Nunn * Copyright (C) 2014 Bret Curtis * Copyright (C) WildMIDI Developers 2015-2016 * Copyright (c) 2015-2025 Vitaly Novichkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ /* XMIDI Converter */ #include #include #include #include #include #include #include #ifdef __DJGPP__ typedef signed char int8_t; typedef unsigned char uint8_t; typedef signed short int16_t; typedef unsigned short uint16_t; typedef signed long int32_t; typedef unsigned long uint32_t; #endif /* Conversion types for Midi files */ #define XMIDI_CONVERT_NOCONVERSION 0x00 #define XMIDI_CONVERT_MT32_TO_GM 0x01 #define XMIDI_CONVERT_MT32_TO_GS 0x02 #define XMIDI_CONVERT_MT32_TO_GS127 0x03 /* This one is broken, don't use */ #define XMIDI_CONVERT_MT32_TO_GS127DRUM 0x04 /* This one is broken, don't use */ #define XMIDI_CONVERT_GS127_TO_GS 0x05 /* Midi Status Bytes */ #define XMI2MID_MIDI_STATUS_NOTE_OFF 0x8 #define XMI2MID_MIDI_STATUS_NOTE_ON 0x9 #define XMI2MID_MIDI_STATUS_AFTERTOUCH 0xA #define XMI2MID_MIDI_STATUS_CONTROLLER 0xB #define XMI2MID_MIDI_STATUS_PROG_CHANGE 0xC #define XMI2MID_MIDI_STATUS_PRESSURE 0xD #define XMI2MID_MIDI_STATUS_PITCH_WHEEL 0xE #define XMI2MID_MIDI_STATUS_SYSEX 0xF #if 1 static int XMI2MID_TRACE(...) { return 0; } #else #include #include static int XMI2MID_TRACE(const char *fmt, int n, ...) { int ret; char ff[50] = ""; snprintf(ff, 49, "XMI2MID: %s\n", fmt); va_list args; va_start(args, n); ret = vfprintf(stderr, const_cast(ff), args); va_end(args); return ret; } #endif typedef struct _xmi2mid_midi_event { int32_t time; uint8_t status; uint8_t data[2]; uint32_t len; uint8_t *buffer; struct _xmi2mid_midi_event *next; } midi_event; typedef struct { uint16_t type; uint16_t tracks; } midi_descriptor; struct xmi2mid_xmi_ctx { uint8_t *src, *src_ptr, *src_end; uint32_t srcsize; uint32_t datastart; uint8_t *dst, *dst_ptr; uint32_t dstsize, dstrem; uint32_t convert_type; midi_descriptor info; int bank127[16]; midi_event **events; int16_t *timing; midi_event *list; midi_event *current; std::vector > *dyn_out; std::vector *dyn_out_cur; }; 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 */ static int xmi2mid_GetVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant); /* Variable length quantity */ static int xmi2mid_GetVLQ2(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant);/* Variable length quantity */ static int xmi2mid_PutVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t value); /* Variable length quantity */ 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, 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, int32_t dstTrackNumber); static uint32_t xmi2mid_ExtractTracksFromXmi(struct xmi2mid_xmi_ctx *ctx); static uint32_t xmi2mid_read1(struct xmi2mid_xmi_ctx *ctx) { uint8_t b0; assert(ctx->src_ptr + 1 < ctx->src_end); b0 = *ctx->src_ptr++; return (b0); } static uint32_t xmi2mid_read2(struct xmi2mid_xmi_ctx *ctx) { uint8_t b0, b1; assert(ctx->src_ptr + 2 < ctx->src_end); b0 = *ctx->src_ptr++; b1 = *ctx->src_ptr++; return (b0 + ((uint32_t)b1 << 8)); } static uint32_t xmi2mid_read4(struct xmi2mid_xmi_ctx *ctx) { uint8_t b0, b1, b2, b3; assert(ctx->src_ptr + 4 < ctx->src_end); b3 = *ctx->src_ptr++; b2 = *ctx->src_ptr++; b1 = *ctx->src_ptr++; b0 = *ctx->src_ptr++; 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; assert(ctx->src_ptr + 4 < ctx->src_end); 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) { assert(ctx->src_ptr + len < ctx->src_end); memcpy(b, ctx->src_ptr, len); ctx->src_ptr += len; } #define DST_CHUNK 8192 static void xmi2mid_resize_dst(struct xmi2mid_xmi_ctx *ctx) { uint32_t pos = (uint32_t)(ctx->dst_ptr - ctx->dst); if (ctx->dyn_out && ctx->dyn_out_cur) { ctx->dyn_out_cur->resize(ctx->dstsize + DST_CHUNK); ctx->dst = ctx->dyn_out_cur->data(); } else ctx->dst = (uint8_t *)realloc(ctx->dst, ctx->dstsize + DST_CHUNK); ctx->dstsize += DST_CHUNK; ctx->dstrem += DST_CHUNK; ctx->dst_ptr = ctx->dst + pos; } static void xmi2mid_write1(struct xmi2mid_xmi_ctx *ctx, uint32_t val) { if (ctx->dstrem < 1) xmi2mid_resize_dst(ctx); *ctx->dst_ptr++ = val & 0xff; ctx->dstrem--; } static void xmi2mid_write2(struct xmi2mid_xmi_ctx *ctx, uint32_t val) { if (ctx->dstrem < 2) xmi2mid_resize_dst(ctx); *ctx->dst_ptr++ = (val>>8) & 0xff; *ctx->dst_ptr++ = val & 0xff; ctx->dstrem -= 2; } static void xmi2mid_write4(struct xmi2mid_xmi_ctx *ctx, uint32_t val) { if (ctx->dstrem < 4) xmi2mid_resize_dst(ctx); *ctx->dst_ptr++ = (val>>24)&0xff; *ctx->dst_ptr++ = (val>>16)&0xff; *ctx->dst_ptr++ = (val>>8) & 0xff; *ctx->dst_ptr++ = val & 0xff; ctx->dstrem -= 4; } static void xmi2mid_seeksrc(struct xmi2mid_xmi_ctx *ctx, uint32_t pos) { ctx->src_ptr = ctx->src + pos; } static void xmi2mid_seekdst(struct xmi2mid_xmi_ctx *ctx, uint32_t pos) { ctx->dst_ptr = ctx->dst + pos; while (ctx->dstsize < pos) xmi2mid_resize_dst(ctx); ctx->dstrem = ctx->dstsize - pos; } static void xmi2mid_skipsrc(struct xmi2mid_xmi_ctx *ctx, int32_t pos) { ctx->src_ptr += pos; } static void xmi2mid_skipdst(struct xmi2mid_xmi_ctx *ctx, int32_t pos) { size_t newpos; ctx->dst_ptr += pos; newpos = ctx->dst_ptr - ctx->dst; while (ctx->dstsize < newpos) xmi2mid_resize_dst(ctx); ctx->dstrem = (uint32_t)(ctx->dstsize - newpos); } static uint32_t xmi2mid_getsrcsize(struct xmi2mid_xmi_ctx *ctx) { return (ctx->srcsize); } static uint32_t xmi2mid_getsrcpos(struct xmi2mid_xmi_ctx *ctx) { return (uint32_t)(ctx->src_ptr - ctx->src); } static uint32_t xmi2mid_getdstpos(struct xmi2mid_xmi_ctx *ctx) { return (uint32_t)(ctx->dst_ptr - ctx->dst); } /* This is a default set of patches to convert from MT32 to GM * The index is the MT32 Patch number and the value is the GM Patch * This is only suitable for music that doesn't do timbre changes * XMIDIs that contain Timbre changes will not convert properly. */ static const char xmi2mid_mt32asgm[128] = { 0, /* 0 Piano 1 */ 1, /* 1 Piano 2 */ 2, /* 2 Piano 3 (synth) */ 4, /* 3 EPiano 1 */ 4, /* 4 EPiano 2 */ 5, /* 5 EPiano 3 */ 5, /* 6 EPiano 4 */ 3, /* 7 Honkytonk */ 16, /* 8 Organ 1 */ 17, /* 9 Organ 2 */ 18, /* 10 Organ 3 */ 16, /* 11 Organ 4 */ 19, /* 12 Pipe Organ 1 */ 19, /* 13 Pipe Organ 2 */ 19, /* 14 Pipe Organ 3 */ 21, /* 15 Accordion */ 6, /* 16 Harpsichord 1 */ 6, /* 17 Harpsichord 2 */ 6, /* 18 Harpsichord 3 */ 7, /* 19 Clavinet 1 */ 7, /* 20 Clavinet 2 */ 7, /* 21 Clavinet 3 */ 8, /* 22 Celesta 1 */ 8, /* 23 Celesta 2 */ 62, /* 24 Synthbrass 1 (62) */ 63, /* 25 Synthbrass 2 (63) */ 62, /* 26 Synthbrass 3 Bank 8 */ 63, /* 27 Synthbrass 4 Bank 8 */ 38, /* 28 Synthbass 1 */ 39, /* 29 Synthbass 2 */ 38, /* 30 Synthbass 3 Bank 8 */ 39, /* 31 Synthbass 4 Bank 8 */ 88, /* 32 Fantasy */ 90, /* 33 Harmonic Pan - No equiv closest is polysynth(90) :( */ 52, /* 34 Choral ?? Currently set to SynthVox(54). Should it be ChoirAhhs(52)??? */ 92, /* 35 Glass */ 97, /* 36 Soundtrack */ 99, /* 37 Atmosphere */ 14, /* 38 Warmbell, sounds kind of like crystal(98) perhaps Tubular Bells(14) would be better. It is! */ 54, /* 39 FunnyVox, sounds alot like Bagpipe(109) and Shania(111) */ 98, /* 40 EchoBell, no real equiv, sounds like Crystal(98) */ 96, /* 41 IceRain */ 68, /* 42 Oboe 2001, no equiv, just patching it to normal oboe(68) */ 95, /* 43 EchoPans, no equiv, setting to SweepPad */ 81, /* 44 DoctorSolo Bank 8 */ 87, /* 45 SchoolDaze, no real equiv */ 112,/* 46 Bell Singer */ 80, /* 47 SquareWave */ 48, /* 48 Strings 1 */ 48, /* 49 Strings 2 - should be 49 */ 44, /* 50 Strings 3 (Synth) - Experimental set to Tremollo Strings - should be 50 */ 45, /* 51 Pizzicato Strings */ 40, /* 52 Violin 1 */ 40, /* 53 Violin 2 ? Viola */ 42, /* 54 Cello 1 */ 42, /* 55 Cello 2 */ 43, /* 56 Contrabass */ 46, /* 57 Harp 1 */ 46, /* 58 Harp 2 */ 24, /* 59 Guitar 1 (Nylon) */ 25, /* 60 Guitar 2 (Steel) */ 26, /* 61 Elec Guitar 1 */ 27, /* 62 Elec Guitar 2 */ 104,/* 63 Sitar */ 32, /* 64 Acou Bass 1 */ 32, /* 65 Acou Bass 2 */ 33, /* 66 Elec Bass 1 */ 34, /* 67 Elec Bass 2 */ 36, /* 68 Slap Bass 1 */ 37, /* 69 Slap Bass 2 */ 35, /* 70 Fretless Bass 1 */ 35, /* 71 Fretless Bass 2 */ 73, /* 72 Flute 1 */ 73, /* 73 Flute 2 */ 72, /* 74 Piccolo 1 */ 72, /* 75 Piccolo 2 */ 74, /* 76 Recorder */ 75, /* 77 Pan Pipes */ 64, /* 78 Sax 1 */ 65, /* 79 Sax 2 */ 66, /* 80 Sax 3 */ 67, /* 81 Sax 4 */ 71, /* 82 Clarinet 1 */ 71, /* 83 Clarinet 2 */ 68, /* 84 Oboe */ 69, /* 85 English Horn (Cor Anglais) */ 70, /* 86 Bassoon */ 22, /* 87 Harmonica */ 56, /* 88 Trumpet 1 */ 56, /* 89 Trumpet 2 */ 57, /* 90 Trombone 1 */ 57, /* 91 Trombone 2 */ 60, /* 92 French Horn 1 */ 60, /* 93 French Horn 2 */ 58, /* 94 Tuba */ 61, /* 95 Brass Section 1 */ 61, /* 96 Brass Section 2 */ 11, /* 97 Vibes 1 */ 11, /* 98 Vibes 2 */ 99, /* 99 Syn Mallet Bank 1 */ 112,/* 100 WindBell no real equiv Set to TinkleBell(112) */ 9, /* 101 Glockenspiel */ 14, /* 102 Tubular Bells */ 13, /* 103 Xylophone */ 12, /* 104 Marimba */ 107,/* 105 Koto */ 111,/* 106 Sho?? set to Shanai(111) */ 77, /* 107 Shakauhachi */ 78, /* 108 Whistle 1 */ 78, /* 109 Whistle 2 */ 76, /* 110 Bottle Blow */ 76, /* 111 Breathpipe no real equiv set to bottle blow(76) */ 47, /* 112 Timpani */ 117,/* 113 Melodic Tom */ 116,/* 114 Deap Snare no equiv, set to Taiko(116) */ 118,/* 115 Electric Perc 1 */ 118,/* 116 Electric Perc 2 */ 116,/* 117 Taiko */ 115,/* 118 Taiko Rim, no real equiv, set to Woodblock(115) */ 119,/* 119 Cymbal, no real equiv, set to reverse cymbal(119) */ 115,/* 120 Castanets, no real equiv, in GM set to Woodblock(115) */ 112,/* 121 Triangle, no real equiv, set to TinkleBell(112) */ 55, /* 122 Orchestral Hit */ 124,/* 123 Telephone */ 123,/* 124 BirdTweet */ 94, /* 125 Big Notes Pad no equiv, set to halo pad (94) */ 98, /* 126 Water Bell set to Crystal Pad(98) */ 121 /* 127 Jungle Tune set to Breath Noise */ }; /* Same as above, except include patch changes * so GS instruments can be used */ static const char xmi2mid_mt32asgs[256] = { 0, 0, /* 0 Piano 1 */ 1, 0, /* 1 Piano 2 */ 2, 0, /* 2 Piano 3 (synth) */ 4, 0, /* 3 EPiano 1 */ 4, 0, /* 4 EPiano 2 */ 5, 0, /* 5 EPiano 3 */ 5, 0, /* 6 EPiano 4 */ 3, 0, /* 7 Honkytonk */ 16, 0, /* 8 Organ 1 */ 17, 0, /* 9 Organ 2 */ 18, 0, /* 10 Organ 3 */ 16, 0, /* 11 Organ 4 */ 19, 0, /* 12 Pipe Organ 1 */ 19, 0, /* 13 Pipe Organ 2 */ 19, 0, /* 14 Pipe Organ 3 */ 21, 0, /* 15 Accordion */ 6, 0, /* 16 Harpsichord 1 */ 6, 0, /* 17 Harpsichord 2 */ 6, 0, /* 18 Harpsichord 3 */ 7, 0, /* 19 Clavinet 1 */ 7, 0, /* 20 Clavinet 2 */ 7, 0, /* 21 Clavinet 3 */ 8, 0, /* 22 Celesta 1 */ 8, 0, /* 23 Celesta 2 */ 62, 0, /* 24 Synthbrass 1 (62) */ 63, 0, /* 25 Synthbrass 2 (63) */ 62, 0, /* 26 Synthbrass 3 Bank 8 */ 63, 0, /* 27 Synthbrass 4 Bank 8 */ 38, 0, /* 28 Synthbass 1 */ 39, 0, /* 29 Synthbass 2 */ 38, 0, /* 30 Synthbass 3 Bank 8 */ 39, 0, /* 31 Synthbass 4 Bank 8 */ 88, 0, /* 32 Fantasy */ 90, 0, /* 33 Harmonic Pan - No equiv closest is polysynth(90) :( */ 52, 0, /* 34 Choral ?? Currently set to SynthVox(54). Should it be ChoirAhhs(52)??? */ 92, 0, /* 35 Glass */ 97, 0, /* 36 Soundtrack */ 99, 0, /* 37 Atmosphere */ 14, 0, /* 38 Warmbell, sounds kind of like crystal(98) perhaps Tubular Bells(14) would be better. It is! */ 54, 0, /* 39 FunnyVox, sounds alot like Bagpipe(109) and Shania(111) */ 98, 0, /* 40 EchoBell, no real equiv, sounds like Crystal(98) */ 96, 0, /* 41 IceRain */ 68, 0, /* 42 Oboe 2001, no equiv, just patching it to normal oboe(68) */ 95, 0, /* 43 EchoPans, no equiv, setting to SweepPad */ 81, 0, /* 44 DoctorSolo Bank 8 */ 87, 0, /* 45 SchoolDaze, no real equiv */ 112, 0, /* 46 Bell Singer */ 80, 0, /* 47 SquareWave */ 48, 0, /* 48 Strings 1 */ 48, 0, /* 49 Strings 2 - should be 49 */ 44, 0, /* 50 Strings 3 (Synth) - Experimental set to Tremollo Strings - should be 50 */ 45, 0, /* 51 Pizzicato Strings */ 40, 0, /* 52 Violin 1 */ 40, 0, /* 53 Violin 2 ? Viola */ 42, 0, /* 54 Cello 1 */ 42, 0, /* 55 Cello 2 */ 43, 0, /* 56 Contrabass */ 46, 0, /* 57 Harp 1 */ 46, 0, /* 58 Harp 2 */ 24, 0, /* 59 Guitar 1 (Nylon) */ 25, 0, /* 60 Guitar 2 (Steel) */ 26, 0, /* 61 Elec Guitar 1 */ 27, 0, /* 62 Elec Guitar 2 */ 104, 0, /* 63 Sitar */ 32, 0, /* 64 Acou Bass 1 */ 32, 0, /* 65 Acou Bass 2 */ 33, 0, /* 66 Elec Bass 1 */ 34, 0, /* 67 Elec Bass 2 */ 36, 0, /* 68 Slap Bass 1 */ 37, 0, /* 69 Slap Bass 2 */ 35, 0, /* 70 Fretless Bass 1 */ 35, 0, /* 71 Fretless Bass 2 */ 73, 0, /* 72 Flute 1 */ 73, 0, /* 73 Flute 2 */ 72, 0, /* 74 Piccolo 1 */ 72, 0, /* 75 Piccolo 2 */ 74, 0, /* 76 Recorder */ 75, 0, /* 77 Pan Pipes */ 64, 0, /* 78 Sax 1 */ 65, 0, /* 79 Sax 2 */ 66, 0, /* 80 Sax 3 */ 67, 0, /* 81 Sax 4 */ 71, 0, /* 82 Clarinet 1 */ 71, 0, /* 83 Clarinet 2 */ 68, 0, /* 84 Oboe */ 69, 0, /* 85 English Horn (Cor Anglais) */ 70, 0, /* 86 Bassoon */ 22, 0, /* 87 Harmonica */ 56, 0, /* 88 Trumpet 1 */ 56, 0, /* 89 Trumpet 2 */ 57, 0, /* 90 Trombone 1 */ 57, 0, /* 91 Trombone 2 */ 60, 0, /* 92 French Horn 1 */ 60, 0, /* 93 French Horn 2 */ 58, 0, /* 94 Tuba */ 61, 0, /* 95 Brass Section 1 */ 61, 0, /* 96 Brass Section 2 */ 11, 0, /* 97 Vibes 1 */ 11, 0, /* 98 Vibes 2 */ 99, 0, /* 99 Syn Mallet Bank 1 */ 112, 0, /* 100 WindBell no real equiv Set to TinkleBell(112) */ 9, 0, /* 101 Glockenspiel */ 14, 0, /* 102 Tubular Bells */ 13, 0, /* 103 Xylophone */ 12, 0, /* 104 Marimba */ 107, 0, /* 105 Koto */ 111, 0, /* 106 Sho?? set to Shanai(111) */ 77, 0, /* 107 Shakauhachi */ 78, 0, /* 108 Whistle 1 */ 78, 0, /* 109 Whistle 2 */ 76, 0, /* 110 Bottle Blow */ 76, 0, /* 111 Breathpipe no real equiv set to bottle blow(76) */ 47, 0, /* 112 Timpani */ 117, 0, /* 113 Melodic Tom */ 116, 0, /* 114 Deap Snare no equiv, set to Taiko(116) */ 118, 0, /* 115 Electric Perc 1 */ 118, 0, /* 116 Electric Perc 2 */ 116, 0, /* 117 Taiko */ 115, 0, /* 118 Taiko Rim, no real equiv, set to Woodblock(115) */ 119, 0, /* 119 Cymbal, no real equiv, set to reverse cymbal(119) */ 115, 0, /* 120 Castanets, no real equiv, in GM set to Woodblock(115) */ 112, 0, /* 121 Triangle, no real equiv, set to TinkleBell(112) */ 55, 0, /* 122 Orchestral Hit */ 124, 0, /* 123 Telephone */ 123, 0, /* 124 BirdTweet */ 94, 0, /* 125 Big Notes Pad no equiv, set to halo pad (94) */ 98, 0, /* 126 Water Bell set to Crystal Pad(98) */ 121, 0 /* 127 Jungle Tune set to Breath Noise */ }; static int Convert_xmi2midi(uint8_t *in, uint32_t insize, uint8_t **out, uint32_t *outsize, 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);*/ 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, 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; 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, hasSelectedTrack ? 1 : ctx.info.tracks); xmi2mid_write2(&ctx, ctx.timing[hasSelectedTrack ? trackNumber : 0]);/* write divisions from track0 */ 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; ret = 0; _end: /* cleanup */ if (ret < 0) { free(ctx.dst); *out = NULL; *outsize = 0; } 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 int Convert_xmi2midi_multi(uint8_t *in, uint32_t insize, std::vector > &out, 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; ctx.dyn_out = &out; 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++) { out.push_back(std::vector()); ctx.dyn_out_cur = &out.back(); ctx.dyn_out_cur->resize(DST_CHUNK); ctx.dst = ctx.dyn_out_cur->data(); 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]); ctx.dyn_out_cur->resize(ctx.dstsize - ctx.dstrem); } ret = 0; _end: /* cleanup */ if (ret < 0) { out.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; next = mlist; while ((event = next) != NULL) { next = event->next; free(event->buffer); free(event); } } /* Sets current to the new event and updates list */ static void xmi2mid_CreateNewEvent(struct xmi2mid_xmi_ctx *ctx, int32_t time) { if (!ctx->list) { ctx->list = ctx->current = (struct _xmi2mid_midi_event *)calloc(1, sizeof(midi_event)); ctx->current->time = (time < 0)? 0 : time; return; } if (time < 0) { midi_event *event = (midi_event *)calloc(1, sizeof(midi_event)); event->next = ctx->list; ctx->list = ctx->current = event; return; } if (ctx->current->time > time) ctx->current = ctx->list; while (ctx->current->next) { if (ctx->current->next->time > time) { midi_event *event = (midi_event *)calloc(1, sizeof(midi_event)); event->next = ctx->current->next; ctx->current->next = event; ctx->current = event; ctx->current->time = time; return; } ctx->current = ctx->current->next; } ctx->current->next = (struct _xmi2mid_midi_event *)calloc(1, sizeof(midi_event)); ctx->current = ctx->current->next; ctx->current->time = time; } /* Conventional Variable Length Quantity */ static int xmi2mid_GetVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant) { int i; uint32_t data; *quant = 0; for (i = 0; i < 4; i++) { if(ctx->src_ptr + 1 >= ctx->src + ctx->srcsize) break; data = xmi2mid_read1(ctx); *quant <<= 7; *quant |= data & 0x7F; if (!(data & 0x80)) { i++; break; } } return (i); } /* XMIDI Delta Variable Length Quantity */ static int xmi2mid_GetVLQ2(struct xmi2mid_xmi_ctx *ctx, uint32_t *quant) { int i; int32_t data; *quant = 0; for (i = 0; xmi2mid_getsrcpos(ctx) != xmi2mid_getsrcsize(ctx); ++i) { data = xmi2mid_read1(ctx); if (data & 0x80) { xmi2mid_skipsrc(ctx, -1); break; } *quant += data; } return (i); } static int xmi2mid_PutVLQ(struct xmi2mid_xmi_ctx *ctx, uint32_t value) { int32_t buffer; int i = 1, j; buffer = value & 0x7F; while (value >>= 7) { buffer <<= 8; buffer |= ((value & 0x7F) | 0x80); i++; } for (j = 0; j < i; j++) { xmi2mid_write1(ctx, buffer & 0xFF); buffer >>= 8; } return (i); } /* Converts Events * * Source is at the first data byte * size 1 is single data byte * size 2 is dual data byte * size 3 is XMI Note on * Returns bytes converted */ static int xmi2mid_ConvertEvent(struct xmi2mid_xmi_ctx *ctx, const int32_t time, const uint8_t status, const int size) { uint32_t delta = 0; int32_t data; midi_event *prev; int i; data = xmi2mid_read1(ctx); /*HACK!*/ if (((status >> 4) == 0xB) && (status & 0xF) != 9 && (data == 114)) { data = 32; /*Change XMI 114 controller into XG bank*/ } /* Bank changes are handled here */ if ((status >> 4) == 0xB && data == 0) { data = xmi2mid_read1(ctx); ctx->bank127[status & 0xF] = 0; if ( ctx->convert_type == XMIDI_CONVERT_MT32_TO_GM || ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS || ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127 || (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM && (status & 0xF) == 9) ) return (2); xmi2mid_CreateNewEvent(ctx, time); ctx->current->status = status; ctx->current->data[0] = 0; ctx->current->data[1] = data == 127 ? 0 : data;/*HACK:*/ if (ctx->convert_type == XMIDI_CONVERT_GS127_TO_GS && data == 127) ctx->bank127[status & 0xF] = 1; return (2); } /* Handling for patch change mt32 conversion, probably should go elsewhere */ if ((status >> 4) == 0xC && (status&0xF) != 9 && ctx->convert_type != XMIDI_CONVERT_NOCONVERSION) { if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GM) { data = xmi2mid_mt32asgm[data]; } else if ((ctx->convert_type == XMIDI_CONVERT_GS127_TO_GS && ctx->bank127[status&0xF]) || ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS || ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM) { xmi2mid_CreateNewEvent (ctx, time); ctx->current->status = 0xB0 | (status&0xF); ctx->current->data[0] = 0; ctx->current->data[1] = xmi2mid_mt32asgs[data*2+1]; data = xmi2mid_mt32asgs[data*2]; } else if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127) { xmi2mid_CreateNewEvent (ctx, time); ctx->current->status = 0xB0 | (status&0xF); ctx->current->data[0] = 0; ctx->current->data[1] = 127; } } /* Drum track handling */ else if ((status >> 4) == 0xC && (status&0xF) == 9 && (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM || ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127)) { xmi2mid_CreateNewEvent (ctx, time); ctx->current->status = 0xB9; ctx->current->data[0] = 0; ctx->current->data[1] = 127; } xmi2mid_CreateNewEvent(ctx, time); ctx->current->status = status; ctx->current->data[0] = data; if (size == 1) return (1); ctx->current->data[1] = xmi2mid_read1(ctx); if (size == 2) return (2); /* XMI Note On handling */ prev = ctx->current; i = xmi2mid_GetVLQ(ctx, &delta); xmi2mid_CreateNewEvent(ctx, time + delta * 3); ctx->current->status = status; ctx->current->data[0] = data; ctx->current->data[1] = 0; ctx->current = prev; return (i + 2); } /* Simple routine to convert system messages */ static int32_t xmi2mid_ConvertSystemMessage(struct xmi2mid_xmi_ctx *ctx, const int32_t time, const uint8_t status) { int32_t i = 0; xmi2mid_CreateNewEvent(ctx, time); ctx->current->status = status; /* Handling of Meta events */ if (status == 0xFF) { ctx->current->data[0] = xmi2mid_read1(ctx); i++; } i += xmi2mid_GetVLQ(ctx, &ctx->current->len); if (!ctx->current->len) return (i); ctx->current->buffer = (uint8_t *)malloc(sizeof(uint8_t)*ctx->current->len); xmi2mid_copy(ctx, (char *) ctx->current->buffer, ctx->current->len); return (i + ctx->current->len); } /* XMIDI and Midi to List * Returns XMIDI PPQN */ 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; int32_t tempo = 500000; 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) { xmi2mid_CreateNewEvent(ctx, 0); ctx->current->status = 0xB9; ctx->current->data[0] = 0; ctx->current->data[1] = 127; } 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; status = xmi2mid_read1(ctx); switch (status >> 4) { case XMI2MID_MIDI_STATUS_NOTE_ON: xmi2mid_ConvertEvent(ctx, time, status, 3); break; /* 2 byte data */ case XMI2MID_MIDI_STATUS_NOTE_OFF: case XMI2MID_MIDI_STATUS_AFTERTOUCH: case XMI2MID_MIDI_STATUS_CONTROLLER: case XMI2MID_MIDI_STATUS_PITCH_WHEEL: xmi2mid_ConvertEvent(ctx, time, status, 2); break; /* 1 byte data */ case XMI2MID_MIDI_STATUS_PROG_CHANGE: case XMI2MID_MIDI_STATUS_PRESSURE: xmi2mid_ConvertEvent(ctx, time, status, 1); break; case XMI2MID_MIDI_STATUS_SYSEX: if (status == 0xFF) { int32_t pos = xmi2mid_getsrcpos(ctx); uint32_t dat = xmi2mid_read1(ctx); if (dat == 0x2F) /* End */ end = 1; else if (dat == 0x51 && !tempo_set) /* Tempo. Need it for PPQN */ { xmi2mid_skipsrc(ctx, 1); tempo = xmi2mid_read1(ctx) << 16; tempo += xmi2mid_read1(ctx) << 8; tempo += xmi2mid_read1(ctx); tempo *= 3; tempo_set = 1; } else if (dat == 0x51 && tempo_set) /* Skip any other tempo changes */ { xmi2mid_GetVLQ(ctx, &dat); xmi2mid_skipsrc(ctx, dat); break; } xmi2mid_seeksrc(ctx, pos); } xmi2mid_ConvertSystemMessage(ctx, time, status); break; default: break; } } return ((tempo * 3) / 25000); } /* Converts and event list to a MTrk * Returns bytes of the array * buf can be NULL */ static uint32_t xmi2mid_ConvertListToMTrk(struct xmi2mid_xmi_ctx *ctx, midi_event *mlist) { int32_t time = 0; midi_event *event; uint32_t delta; uint8_t last_status = 0; uint32_t i = 8; uint32_t j; uint32_t size_pos, cur_pos; int end = 0; xmi2mid_write1(ctx, 'M'); xmi2mid_write1(ctx, 'T'); xmi2mid_write1(ctx, 'r'); xmi2mid_write1(ctx, 'k'); size_pos = xmi2mid_getdstpos(ctx); xmi2mid_skipdst(ctx, 4); for (event = mlist; event && !end; event = event->next) { delta = (event->time - time); time = event->time; i += xmi2mid_PutVLQ(ctx, delta); if ((event->status != last_status) || (event->status >= 0xF0)) { xmi2mid_write1(ctx, event->status); i++; } last_status = event->status; switch (event->status >> 4) { /* 2 bytes data * Note off, Note on, Aftertouch, Controller and Pitch Wheel */ case 0x8: case 0x9: case 0xA: case 0xB: case 0xE: xmi2mid_write1(ctx, event->data[0]); xmi2mid_write1(ctx, event->data[1]); i += 2; break; /* 1 bytes data * Program Change and Channel Pressure */ case 0xC: case 0xD: xmi2mid_write1(ctx, event->data[0]); i++; break; /* Variable length * SysEx */ case 0xF: if (event->status == 0xFF) { if (event->data[0] == 0x2f) end = 1; xmi2mid_write1(ctx, event->data[0]); i++; } i += xmi2mid_PutVLQ(ctx, event->len); if (event->len) { for (j = 0; j < event->len; j++) { xmi2mid_write1(ctx, event->buffer[j]); i++; } } break; /* Never occur */ default: /*_WM_DEBUG_MSG("%s: unrecognized event", __FUNCTION__);*/ break; } } cur_pos = xmi2mid_getdstpos(ctx); xmi2mid_seekdst(ctx, size_pos); xmi2mid_write4(ctx, i - 8); xmi2mid_seekdst(ctx, cur_pos); return (i); } /* Assumes correct xmidi */ static uint32_t xmi2mid_ExtractTracksFromXmi(struct xmi2mid_xmi_ctx *ctx) { uint32_t num = 0; signed short ppqn; 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 */ xmi2mid_copy(ctx, buf, 4); len = xmi2mid_read4(ctx); /* Skip the FORM entries */ if (!memcmp(buf, "FORM", 4)) { xmi2mid_skipsrc(ctx, 4); xmi2mid_copy(ctx, buf, 4); 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; } 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, &rbrn))) { /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, NULL, 0);*/ break; } ctx->timing[num] = ppqn; ctx->events[num] = ctx->list; /* Increment Counter */ num++; /* 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 */ return (num); } static int xmi2mid_ParseXMI(struct xmi2mid_xmi_ctx *ctx) { uint32_t i; uint32_t start; uint32_t len; uint32_t chunk_len; uint32_t file_size; char buf[32]; file_size = xmi2mid_getsrcsize(ctx); if (xmi2mid_getsrcpos(ctx) + 8 > file_size) { badfile: /*_WM_GLOBAL_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);*/ return (-1); } /* Read first 4 bytes of header */ xmi2mid_copy(ctx, buf, 4); /* Could be XMIDI */ if (!memcmp(buf, "FORM", 4)) { /* Read length of */ len = xmi2mid_read4(ctx); start = xmi2mid_getsrcpos(ctx); if (start + 4 > file_size) goto badfile; /* Read 4 bytes of type */ xmi2mid_copy(ctx, buf, 4); /* XDIRless XMIDI, we can handle them here. */ if (!memcmp(buf, "XMID", 4)) { /*_WM_DEBUG_MSG("Warning: XMIDI without XDIR");*/ ctx->info.tracks = 1; } /* Not an XMIDI that we recognise */ else if (memcmp(buf, "XDIR", 4)) { goto badfile; } else { /* Seems Valid */ ctx->info.tracks = 0; for (i = 4; i < len; i++) { /* check too short files */ if (xmi2mid_getsrcpos(ctx) + 10 > file_size) break; /* Read 4 bytes of type */ xmi2mid_copy(ctx, buf, 4); /* Read length of chunk */ chunk_len = xmi2mid_read4(ctx); /* Add eight bytes */ i += 8; if (memcmp(buf, "INFO", 4)) { /* Must align */ xmi2mid_skipsrc(ctx, (chunk_len + 1) & ~1); i += (chunk_len + 1) & ~1; continue; } /* Must be at least 2 bytes long */ if (chunk_len < 2) break; ctx->info.tracks = xmi2mid_read2(ctx); break; } /* Didn't get to fill the header */ if (ctx->info.tracks == 0) { goto badfile; } /* Ok now to start part 2 * Goto the right place */ xmi2mid_seeksrc(ctx, start + ((len + 1) & ~1)); if (xmi2mid_getsrcpos(ctx) + 12 > file_size) goto badfile; /* Read 4 bytes of type */ xmi2mid_copy(ctx, buf, 4); if (memcmp(buf, "CAT ", 4)) { /*_WM_ERROR_NEW("XMI error: expected \"CAT \", found \"%c%c%c%c\".", buf[0], buf[1], buf[2], buf[3]);*/ return (-1); } /* Now read length of this track */ xmi2mid_read4(ctx); /* Read 4 bytes of type */ xmi2mid_copy(ctx, buf, 4); if (memcmp(buf, "XMID", 4)) { /*_WM_ERROR_NEW("XMI error: expected \"XMID\", found \"%c%c%c%c\".", buf[0], buf[1], buf[2], buf[3]);*/ return (-1); } /* Valid XMID */ ctx->datastart = xmi2mid_getsrcpos(ctx); return (0); } } return (-1); } 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 && (dstTrackNumber < 0 || ctx->info.tracks >= dstTrackNumber))? 2 : 0; xmi2mid_seeksrc(ctx, ctx->datastart); i = xmi2mid_ExtractTracksFromXmi(ctx); if (i != ctx->info.tracks) { /*_WM_ERROR_NEW("XMI error: extracted only %u out of %u tracks from XMIDI", ctx->info.tracks, i);*/ return (-1); } return (0); }