From 079e433a8f9fcee06f85f2291a1ec0c52d77f342 Mon Sep 17 00:00:00 2001 From: atsushieno Date: Tue, 11 Oct 2022 16:13:57 +0900 Subject: [PATCH] BREAKING CHANGE: *.umpx now stores track size in bytes, not # of UMPs. This addresses https://github.com/atsushieno/ktmidi/issues/23 --- docs/MIDI2_FORMATS.md | 18 +++++++++--------- .../atsushieno/ktmidi/Midi2ReaderWriter.kt | 17 ++++++++++++----- .../dev/atsushieno/ktmidi/Midi2MusicTest.kt | 2 +- .../1-das-stromreiters.umpx | Bin 121272 -> 120984 bytes .../mugene-fantasy-suite/2-f.h.t.u.umpx | Bin 99296 -> 99296 bytes .../mugene-fantasy-suite/3-ghostship.umpx | Bin 56264 -> 56212 bytes .../4-coral-labyrinth.umpx | Bin 68136 -> 67816 bytes 7 files changed, 22 insertions(+), 15 deletions(-) diff --git a/docs/MIDI2_FORMATS.md b/docs/MIDI2_FORMATS.md index acc48d42c..715ea3209 100644 --- a/docs/MIDI2_FORMATS.md +++ b/docs/MIDI2_FORMATS.md @@ -17,18 +17,18 @@ We provide two options for UMP-based music files: ``` // Data Format: -// identifier: 0xAAAAAAAAAAAAAAAA (16 bytes) -// i32 deltaTimeSpec -// i32 numTracks -// tracks - each track contains: -// identifier: 0xEEEEEEEEEEEEEEEE (16 bytes) -// i32 numUMPs -// umps (i32, i64 or i128) in BIG endian +// - identifier 0xAAAAAAAAAAAAAAAA (16 bytes) +// - i32 deltaTimeSpec +// - i32 numTracks +// - tracks - each track contains these items in order: +// - identifier 0xEEEEEEEEEEEEEEEE (16 bytes) +// - i32 sizeInBytes +// - UMPs (i32, i64 or i128) in BIG endian ``` -If `deltaTimeSpec` is a positive integer, it works like the value in SMF header chunk. There is no "format" specifier in this format - if "numTracks" is 1 then it is obviously compatible with FORMAT 0. +If `deltaTimeSpec` is a positive integer, it works like the `division` field value in SMF header chunk. There is no "format" specifier in this format - if "numTracks" is 1 then it is obviously compatible with FORMAT 0. -All the UMPs are serialized in BIG endian for persistant stability across platforms. +All the UMPs are serialized in BIG endian per 32-bit integer for persistant stability across platforms and better binary readability. ## META events diff --git a/ktmidi/src/commonMain/kotlin/dev/atsushieno/ktmidi/Midi2ReaderWriter.kt b/ktmidi/src/commonMain/kotlin/dev/atsushieno/ktmidi/Midi2ReaderWriter.kt index b307ca512..db8565562 100644 --- a/ktmidi/src/commonMain/kotlin/dev/atsushieno/ktmidi/Midi2ReaderWriter.kt +++ b/ktmidi/src/commonMain/kotlin/dev/atsushieno/ktmidi/Midi2ReaderWriter.kt @@ -27,13 +27,16 @@ internal class Midi2MusicWriter(val stream: MutableList) { ret.add(music.tracks.size) for (track in music.tracks) { (0..3).forEach { _ -> ret.add(0xEEEEEEEE.toInt()) } - ret.add(track.messages.size) - for (message in track.messages) + ret.add(0) // stub for `size` field, to be replaced later + val trackStartOffset = ret.size + for (message in track.messages) { when (message.messageType) { 5 -> ret.addAll(sequenceOf(message.int1, message.int2, message.int3, message.int4)) 3, 4 -> ret.addAll(sequenceOf(message.int1, message.int2)) else -> ret.add(message.int1) } + } + ret[trackStartOffset - 1] = (ret.size - trackStartOffset) * 4 // sizeof(Int) } return ret } @@ -89,9 +92,13 @@ internal class Midi2MusicReader(val music: Midi2Music, stream: List) { private fun readTrack(): Midi2Track { val ret = Midi2Track() expectIdentifier16(0xEE.toByte()) - val numUMPs = reader.readInt32() - for (t in 0 until numUMPs) - ret.messages.add(reader.readUmp()) + val numBytes = reader.readInt32() + var readSize = 0 + while (readSize < numBytes) { + val ump = reader.readUmp() + readSize += ump.sizeInBytes + ret.messages.add(ump) + } return ret } } diff --git a/ktmidi/src/commonTest/kotlin/dev/atsushieno/ktmidi/Midi2MusicTest.kt b/ktmidi/src/commonTest/kotlin/dev/atsushieno/ktmidi/Midi2MusicTest.kt index 2eea98bc2..5ecac9746 100644 --- a/ktmidi/src/commonTest/kotlin/dev/atsushieno/ktmidi/Midi2MusicTest.kt +++ b/ktmidi/src/commonTest/kotlin/dev/atsushieno/ktmidi/Midi2MusicTest.kt @@ -39,6 +39,6 @@ class Midi2MusicTest { // Note that "stream id" also counts in the packet size in sysex8 (hence 12, not 11). // Note that umpx is always serialized in BIG endian. assertContentEquals(musicFileIdentifier + listOf(0, 0, 0, 0) + listOf(0, 0, 0, 1) +musicTrackIdentifier + - listOf(0, 0, 0, 1) + listOf(0x50, 12, 0, 0, 0, 0, 0, ff, ff, ff, 3, 7, 0xA1.toByte(), 0x20, 0, 0), bytes, "umpx") + listOf(0, 0, 0, 16) + listOf(0x50, 12, 0, 0, 0, 0, 0, ff, ff, ff, 3, 7, 0xA1.toByte(), 0x20, 0, 0), bytes, "umpx") } } \ No newline at end of file diff --git a/ktmidi/src/commonTest/resources/mugene-fantasy-suite/1-das-stromreiters.umpx b/ktmidi/src/commonTest/resources/mugene-fantasy-suite/1-das-stromreiters.umpx index c3e3df760c9d632ddf72bff0682a21bafbed0dff..daaa0ed6e3ccd324f22a425afb14aa8e85a41d39 100644 GIT binary patch delta 1241 zcmdn7nSI7a_6gdIEfaO7F>x?#JR-!wBsO954Gs&|$qs_tle@)a!L&D@DwwW^(wq4V z!Tirq8m2CuAEItDls?W6R=1g5fR%BwfS|=>A3=x76M*=NpwDC#As?{V1|f&ZUx3&` z*k^NxumB^9%`lU1i$YwYjYh-dVKk}gVCrGCwRk82`voU|5RhSZXmp(HFADK3%x)MB zGfP_(tah@4q{ZYKC^nF?m<-3_Dx1$p)v+=OHf;7$NMN13LDd3`HPkF7H>hh&Hqhjr zJX=i{oA~DK>aL8FG%bK=as?27fM8US%`)0T>`eL&n+=SD^qE8^Y;H+hB*MfUuvw!l ziJQsw#pa&IU#?8L0-HOIE9o)?PuRTX%c5W=uA1!u9*q55j4YE6aELLfEZP1eh;f?= zle)t84dskR>P(Cewm+H2_?eNBWqQG4MsdcL?H?8}PGe#d)L~GNb(kFBDzbgSVn#JV zCgy_eC$=y)F*6A;Z1>s02#i~n=?4}7b&OJ@K|oF_04GQmh3y&_7>!t%824`S`Rzlo_89h)RP{)A;N_ zyh{)Yri34&1VT;Y2PxTniC>73ok4+t!J*M{vbTujBtaRFPANer5ETWXW(oR%c+Uj= zuqe$E0xMkwp9aVBRW-Ae6$RWb-dg5q2iF&zl7dgY=o0l{Tj&ED~X2+`O5i zG>MyuTXl0z!!K7R_JYkB$CPxLcn@su`MfBYiE;XN19!%LE=I=52ROu-Sf_5k5y-er zg^Bgc_6cQ-M(Ruqe%p`CW&F&@$T)q$d`5A`r0o~xGfrb-W17sMAnP#Mz(r(x!y-mC zK_-TY+gEI6Y+`0&s@$%#ol#f-rUR;Rlo|~Xangfe^>&W)j7BU>3?AEUE;F73W)zR@ OJXfKNJJ%TZFaiJ!QB!mP diff --git a/ktmidi/src/commonTest/resources/mugene-fantasy-suite/2-f.h.t.u.umpx b/ktmidi/src/commonTest/resources/mugene-fantasy-suite/2-f.h.t.u.umpx index 06c764c385c06ce8c41e550dd1d14bbb091454e6..9b5c0d01d8929e4bd6362e53f4a621990bf75e91 100644 GIT binary patch delta 788 zcmaFR&i0_4ZGtvq%S4@NOe_K$k0`M)af@tz!J@{^q%N^pKtM~Ki9KYqjd>b76Z4hL zOYEL8Gig8A+~f3Fn{mVBhP*>eN;fvU=?6HnV9cvzvImKo|%zlx`HjE_@Jaf zUSU(_U{Fwcu>C_I<1)7Ga!HK0xVAe4LD?U<2Bo_PZ-@@8%YgAI&bVOvgCfRFj7*$1 U+ozN;`f)O`{@MPdmhlfO0EYRU4gdfE delta 789 zcmaFR&i0_4ZGtu<=R}=pObm$|k0`M)F>c)af<=v+iA`&>fPj`d6Qjgt8}l@FCWgMv zOYEL8GqF`~?s598%_u#&A@2|qYvE>>{MSm1YMTuvF)=dn?cZ!NSwDf1ak_#nqd241 z=9IBQX9j8z$OUQ`jMU@{ zEMjb|P7Df454L{@WL(C!T`r077T0!%ASnAI*PwLw;0@8i;4-`I4~iH!F)}f_Z=X`a Q=*P*#aAy0HTE;)D0M#XoIZgqT zW9sId<(oK}7&dNJ*tm{`iRtX-3tO9j`kOX~oJeA3Vw$me$7vZBCI*GceHRobe?IRD WVMbAf@|gVYGSDuC&HpY>V+R0jTU8SP diff --git a/ktmidi/src/commonTest/resources/mugene-fantasy-suite/4-coral-labyrinth.umpx b/ktmidi/src/commonTest/resources/mugene-fantasy-suite/4-coral-labyrinth.umpx index 41aead3eb49a10d711f95e5630936be7b3fb69a3..7c2507f4e2602472ebcaf13b486f1f3f60988e92 100644 GIT binary patch delta 704 zcmZ26h2_OWmI>O7EfaO7F){jVJQByq#Byb`3ez(dCO(JFJnTVyOpH%9Cy0JyWHL(F z93ft0#MB(H`HZIs3x~rtZiash3=ESW_$o}E;KwmJCX8$IsyHc@$p+a991cJk1_cIz z%>_YQm=S8%q$x}`@Z;DV6Q%-ECpNh+N^rBb*H$DYqsEPf9m8a_gd#He3!4~|<%7)y R=RR37F&^2T!ohfi5dhx*)tCSP delta 888 zcmaDck!8gcmI>O7oD+4XF)_$&JQByq#IR$t3ez(dCMMy{JnTVyObmgW6GXo;GO;&q zju0<0ViLZ+`HZIs%j6fn3cL>6xEUA@ye$UN|2DJw>9b6>36dA(0P$_@g?d1=0|O8! zFmP;+3EILuSt3kf@&qpyPJ}MgFrcoSD0xn}uE{1r3Y#6g7BG+6F&b8bK3&Oz61PJe iIGKYJI!fZ7{E