diff --git a/pom.xml b/pom.xml
index a860f0f..933925b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,11 +6,20 @@
+ * Some numbers in MIDI Files are represented in + * a form called a variable-length quantity. These numbers are represented 7 bits per byte, most significant + * bits first. All bytes except the last have bit 7 (the most significant bit) set, and the last byte has bit + * 7 clear. If the number is between 0 and 127, it is thus represented exactly as one byte. + *
+ * Here are some examples of numbers represented as variable-length quantities: + *
+ * Number (hex) = Representation (hex)
+ * 00 00 00 00 = 00
+ * 00 00 00 40 = 40
+ * 00 00 00 7F = 7F
+ * 00 00 00 80 = 81 00
+ * 00 00 20 00 = C0 00
+ * 00 00 3F FF = FF 7F
+ * 00 00 40 00 = 81 80 00
+ * 00 10 00 00 = C0 80 00
+ * 00 1F FF FF = FF FF 7F
+ * 00 20 00 00 = 81 80 80 00
+ * 08 00 00 00 = C0 80 80 00
+ * 0F FF FF FF = FF FF FF 7F
+ *
+ * The largest number which is allowed is 0FFFFFFF + */ public class VariableLengthInt { private int mValue; @@ -75,7 +101,7 @@ private void parseBytes(InputStream in) throws IOException ints[mSizeInBytes - 1] = (b & 0x7F); break; } - ints[mSizeInBytes - 1] = (b & 0x7F); + ints[mSizeInBytes - 1] = (b & 0xFF); b = in.read(); } @@ -90,7 +116,7 @@ private void parseBytes(InputStream in) throws IOException { mBytes[i] = (byte) ints[i]; - mValue += ints[i] << shift; + mValue += (ints[i] & 0x7F) << shift; shift -= 7; } } diff --git a/src/test/java/com/leff/midi/util/TestVariableLengthInt.java b/src/test/java/com/leff/midi/util/TestVariableLengthInt.java new file mode 100644 index 0000000..689c96e --- /dev/null +++ b/src/test/java/com/leff/midi/util/TestVariableLengthInt.java @@ -0,0 +1,177 @@ +package com.leff.midi.util; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import static org.junit.Assert.*; + +public class TestVariableLengthInt +{ + @Test + public void testMinimumOneByteValueFromInt() { + VariableLengthInt vli = new VariableLengthInt(0x00); + assertEquals(0x00, vli.getValue()); + assertEquals(1, vli.getByteCount()); + assertArrayEquals(new byte[] { 0x00 }, vli.getBytes()); + } + + @Test + public void testMinimumOneByteValueFromStream() throws Exception { + InputStream stream = new ByteArrayInputStream(new byte[] { 0x00 }); + VariableLengthInt vli = new VariableLengthInt(stream); + assertEquals(0x00, vli.getValue()); + assertEquals(1, vli.getByteCount()); + assertArrayEquals(new byte[] { 0x00 }, vli.getBytes()); + } + + @Test + public void testMaximumOneByteValueFromInt() { + VariableLengthInt vli = new VariableLengthInt(0x7F); + assertEquals(0x7F, vli.getValue()); + assertEquals(1, vli.getByteCount()); + assertArrayEquals(new byte[] { 0x7F }, vli.getBytes()); + } + + @Test + public void testMaximumOneByteValueFromStream() throws Exception { + InputStream stream = new ByteArrayInputStream(new byte[] {0x7F}); + VariableLengthInt vli = new VariableLengthInt(stream); + assertEquals(0x7F, vli.getValue()); + assertEquals(1, vli.getByteCount()); + assertArrayEquals(new byte[] { 0x7F }, vli.getBytes()); + } + + @Test + public void testMinimumTwoByteValueFromInt() { + VariableLengthInt vli = new VariableLengthInt(0x80); + assertEquals(0x80, vli.getValue()); + assertEquals(2, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0x81, 0x00 }, vli.getBytes()); + } + + @Test + public void testMinimumTwoByteValueFromStream() throws Exception { + InputStream stream = new ByteArrayInputStream(new byte[] { (byte) 0x81, 0x00 }); + VariableLengthInt vli = new VariableLengthInt(stream); + assertEquals(0x80, vli.getValue()); + assertEquals(2, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0x81, 0x00 }, vli.getBytes()); + } + + @Test + public void testMaximumTwoByteValueFromInt() { + VariableLengthInt vli = new VariableLengthInt(0x3F_FF); + assertEquals(0x3F_FF, vli.getValue()); + assertEquals(2, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0xFF, 0x7F }, vli.getBytes()); + } + + @Test + public void testMaximumTwoByteValueFromStream() throws Exception { + InputStream stream = new ByteArrayInputStream(new byte[] { (byte) 0xFF, 0x7F }); + VariableLengthInt vli = new VariableLengthInt(stream); + assertEquals(0x3F_FF, vli.getValue()); + assertEquals(2, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0xFF, 0x7F }, vli.getBytes()); + } + + @Test + public void testMinimumThreeByteValueFromInt() { + VariableLengthInt vli = new VariableLengthInt(0x40_00); + assertEquals(0x40_00, vli.getValue()); + assertEquals(3, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0x81, (byte) 0x80, 0x00 }, vli.getBytes()); + } + + @Test + public void testMinimumThreeByteValueFromStream() throws Exception { + InputStream stream = new ByteArrayInputStream(new byte[] { (byte) 0x81, (byte) 0x80, 0x00 }); + VariableLengthInt vli = new VariableLengthInt(stream); + assertEquals(0x40_00, vli.getValue()); + assertEquals(3, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0x81, (byte) 0x80, 0x00 }, vli.getBytes()); + } + + @Test + public void testMaximumThreeByteValueFromInt() { + VariableLengthInt vli = new VariableLengthInt(0x1F_FF_FF); + assertEquals(0x1F_FF_FF, vli.getValue()); + assertEquals(3, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0xFF, (byte) 0xFF, 0x7F }, vli.getBytes()); + } + + @Test + public void testMaximumThreeByteValueFromStream() throws Exception { + InputStream stream = new ByteArrayInputStream(new byte[] { (byte) 0xFF, (byte) 0xFF, 0x7F }); + VariableLengthInt vli = new VariableLengthInt(stream); + assertEquals(0x1F_FF_FF, vli.getValue()); + assertEquals(3, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0xFF, (byte) 0xFF, 0x7F }, vli.getBytes()); + } + + @Test + public void testMinimumFourByteValueFromInt() { + VariableLengthInt vli = new VariableLengthInt(0x20_00_00); + assertEquals(0x20_00_00, vli.getValue()); + assertEquals(4, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0x81, (byte) 0x80, (byte) 0x80, 0x00 }, vli.getBytes()); + } + + @Test + public void testMinimumFourByteValueFromStream() throws Exception { + InputStream stream = new ByteArrayInputStream(new byte[] { (byte) 0x81, (byte) 0x80, (byte) 0x80, 0x00 }); + VariableLengthInt vli = new VariableLengthInt(stream); + assertEquals(0x20_00_00, vli.getValue()); + assertEquals(4, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0x81, (byte) 0x80, (byte) 0x80, 0x00 }, vli.getBytes()); + } + + @Test + public void testMaximumFourByteValueFromInt() { + VariableLengthInt vli = new VariableLengthInt(0x0F_FF_FF_FF); + assertEquals(0x0F_FF_FF_FF, vli.getValue()); + assertEquals(4, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 0x7F }, vli.getBytes()); + } + + @Test + public void testMaximumFourByteValueFromStream() throws Exception { + InputStream stream = new ByteArrayInputStream(new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 0x7F }); + VariableLengthInt vli = new VariableLengthInt(stream); + assertEquals(0x0F_FF_FF_FF, vli.getValue()); + assertEquals(4, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 0x7F }, vli.getBytes()); + } + + @Test + public void testSetValue() { + VariableLengthInt vli = new VariableLengthInt(0); + + vli.setValue(0x40); + assertEquals(0x40, vli.getValue()); + assertEquals(1, vli.getByteCount()); + assertArrayEquals(new byte[] { 0x40 }, vli.getBytes()); + + vli.setValue(0x08_00_00_00); + assertEquals(0x08_00_00_00, vli.getValue()); + assertEquals(4, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0xC0, (byte) 0x80, (byte) 0x80, 0x00 }, vli.getBytes()); + + vli.setValue(0x20_00); + assertEquals(0x20_00, vli.getValue()); + assertEquals(2, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0xC0, 0x00 }, vli.getBytes()); + + vli.setValue(0x10_00_00); + assertEquals(0x10_00_00, vli.getValue()); + assertEquals(3, vli.getByteCount()); + assertArrayEquals(new byte[] { (byte) 0xC0, (byte) 0x80, 0x00 }, vli.getBytes()); + } + + @Test + public void testToString() { + assertEquals("00 (0)", new VariableLengthInt(0).toString()); + assertEquals("8F FF FF 7F (33554431)", new VariableLengthInt(0x01_FF_FF_FF).toString()); + } +}