/** * Copyright 2000-2006 DFKI GmbH. * All Rights Reserved. Use is subject to license terms. * * This file is part of MARY TTS. * * MARY TTS is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package marytts.unitselection.data; import static org.junit.Assert.assertEquals; import java.io.File; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.Random; import marytts.exceptions.MaryConfigurationException; import marytts.tools.voiceimport.TimelineWriter; import marytts.util.Pair; import marytts.util.data.Datagram; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; /** * Provides the actual timeline test case for the timeline reading/writing symmetry. */ public class TimelineTest { private static TestableTimelineReader tlr; private static String hdrContents; private static int NUMDATAGRAMS; private static int MAXDATAGRAMBYTESIZE; private static int MAXDATAGRAMDURATION; private static int sampleRate; private static Datagram[] origDatagrams; private static final String tlFileName = "timelineTest.bin"; @BeforeClass public static void setUp() throws Exception { Random rand = new Random(); // New random number generator NUMDATAGRAMS = rand.nextInt(87) + 6; // Number of datagrams to test with, between 6 and 100. System.out.println("Testing with [" + NUMDATAGRAMS + "] random datagrams."); MAXDATAGRAMBYTESIZE = 64; // Maximum datagram length in bytes MAXDATAGRAMDURATION = 20; // Maximum datagram duration (in samples) hdrContents = "Blah This is the procHeader Blah"; sampleRate = 1000; origDatagrams = new Datagram[NUMDATAGRAMS]; // An array of datagrams with random length int len = 0; long dur = 0l; /* Fill the array of random datagrams */ long lenCumul = 74; long durCumul = 0l; for (int i = 0; i < NUMDATAGRAMS; i++) { /* Make the first datagram very long, for special tests */ if (i == 0 || i == 2) { len = 1234567; } else { /* Make a random length */ len = rand.nextInt(MAXDATAGRAMBYTESIZE) + 1; } lenCumul += (len + 12); /* Allocate the corresponding byte array */ byte[] buff = new byte[len]; /* Fill all the bytes with the datagram index */ for (int l = 0; l < len; l++) { buff[l] = (byte) i; } /* Make a random datagram duration */ dur = (long) (rand.nextInt(MAXDATAGRAMDURATION) + 2); durCumul += dur; /* Store the datagram */ origDatagrams[i] = new Datagram(dur, buff); /* Check */ System.out.println("[ " + len + " , " + dur + " ]\t( " + lenCumul + " , " + durCumul + " )"); } /* Write the datagram array in a timeline */ System.out.println("Opening new timeline file..."); TimelineWriter tlw = new TimelineWriter(tlFileName, hdrContents, sampleRate, 0.1d); System.out.println("Feeding..."); tlw.feed(origDatagrams, sampleRate); System.out.println("Closing..."); tlw.close(); System.out.println("Done."); System.out.println("Datagram zone pos. = " + tlw.getDatagramsBytePos()); System.out.println("WRITTEN INDEX:"); tlw.getIndex().print(); /* Testing the readonly file opening */ System.out.println("Testing the TimelineReader construction..."); /* Re-read the datagrams */ tlr = new TestableTimelineReader(tlFileName, true); } @Test public void procHeader() throws IOException { /* Check the procHeader */ Assert.assertEquals("The procHeader is out of sync.", tlr.getProcHeaderContents(), hdrContents); } @Test public void numDatagrams() throws IOException { /* Check the number of datagrams */ Assert.assertEquals("numDatagrams is out of sync.", tlr.getNumDatagrams(), NUMDATAGRAMS); } @Test public void testSkip() throws IOException { System.out.println("READ INDEX:"); tlr.getIndex().print(); /* Testing skip */ System.out.println("Testing skip..."); long timeNow = 0; long timeBefore = 0; Pair<ByteBuffer, Long> p = tlr.getByteBufferAtTime(timeNow); ByteBuffer bb = p.getFirst(); int byteNow = bb.position(); int byteBefore = 0; for (int i = 0; i < NUMDATAGRAMS; i++) { timeBefore = timeNow; byteBefore = byteNow; try { long skippedDuration = tlr.skipNextDatagram(bb); timeNow += skippedDuration; } catch (BufferUnderflowException e) { // reached end of byte buffer break; } byteNow = bb.position(); Assert.assertEquals("Skipping fails on datagram [" + i + "].", (long) (origDatagrams[i].getLength()) + 12l, (byteNow - byteBefore)); Assert.assertEquals("Time is out of sync after skipping datagram [" + i + "].", origDatagrams[i].getDuration(), (timeNow - timeBefore)); } /* Testing the EOF trap for skip */ try { tlr.skipNextDatagram(bb); Assert.fail("should have thrown BufferUnderflowException to indicate end of byte buffer"); } catch (BufferUnderflowException e) { // OK, expected } } @Test public void testGet() throws IOException { /* Testing get */ System.out.println("Testing get..."); Datagram[] readDatagrams = new Datagram[NUMDATAGRAMS]; Pair<ByteBuffer, Long> p = tlr.getByteBufferAtTime(0); ByteBuffer bb = p.getFirst(); for (int i = 0; i < NUMDATAGRAMS; i++) { readDatagrams[i] = tlr.getNextDatagram(bb); if (readDatagrams[i] == null) { System.err.println("Could read " + i + " datagrams"); break; } Assert.assertTrue("Datagram [" + i + "] is out of sync.", areEqual(origDatagrams[i].getData(), readDatagrams[i].getData())); Assert.assertEquals("Time for datagram [" + i + "] is out of sync.", origDatagrams[i].getDuration(), readDatagrams[i].getDuration()); } /* Testing the EOF trap for get */ Assert.assertEquals(null, tlr.getNextDatagram(bb)); } @Test public void gotoTime1() throws IOException { // setup final int testIdx = NUMDATAGRAMS / 2; long onTime = getTimeOfIndex(testIdx); // exercise ByteBuffer bb = tlr.getByteBufferAtTime(onTime).getFirst(); Datagram d = tlr.getNextDatagram(bb); // verify assertEquals(origDatagrams[testIdx], d); } @Test public void gotoTime2() throws IOException { // setup final int testIdx = NUMDATAGRAMS / 2; long onTime = getTimeOfIndex(testIdx); long afterTime = onTime + origDatagrams[testIdx].getDuration(); long midTime = onTime + ((afterTime - onTime) / 2); // exercise ByteBuffer bb = tlr.getByteBufferAtTime(midTime).getFirst(); Datagram d = tlr.getNextDatagram(bb); // verify assertEquals(origDatagrams[testIdx], d); } @Test public void gotoTime3() throws IOException { // setup final int testIdx = NUMDATAGRAMS / 2; long onTime = getTimeOfIndex(testIdx); long afterTime = onTime + origDatagrams[testIdx].getDuration(); // exercise ByteBuffer bb = tlr.getByteBufferAtTime(afterTime).getFirst(); Datagram d = tlr.getNextDatagram(bb); // verify assertEquals(origDatagrams[testIdx + 1], d); } @Test public void getDatagrams1() throws IOException { // setup final int testIdx = NUMDATAGRAMS / 2; long onTime = getTimeOfIndex(testIdx); Datagram[] D = null; long span = origDatagrams[testIdx].getDuration(); long[] offset = new long[1]; // exercise D = tlr.getDatagrams(onTime, span, sampleRate, offset); // verify assertEquals(1, D.length); assertEquals(origDatagrams[testIdx], D[0]); assertEquals(0l, offset[0]); } @Test public void getDatagrams2() throws IOException { // setup final int testIdx = NUMDATAGRAMS / 2; long onTime = getTimeOfIndex(testIdx); Datagram[] D = null; long span = origDatagrams[testIdx].getDuration() / 2; long[] offset = new long[1]; // exercise D = tlr.getDatagrams(onTime, span, sampleRate, offset); // verify assertEquals(1, D.length); assertEquals(origDatagrams[testIdx], D[0]); assertEquals(0l, offset[0]); } @Test public void getDatagrams3() throws IOException { // setup final int testIdx = NUMDATAGRAMS / 2; long onTime = getTimeOfIndex(testIdx); long afterTime = onTime + origDatagrams[testIdx].getDuration(); long midTime = onTime + ((afterTime - onTime) / 2); Datagram[] D = null; long span = origDatagrams[testIdx].getDuration() / 2; // exercise D = tlr.getDatagrams(midTime, span, sampleRate); // verify assertEquals(1, D.length); assertEquals(origDatagrams[testIdx], D[0]); } @Test public void getDatagrams4() throws IOException { // setup final int testIdx = NUMDATAGRAMS / 2; long onTime = getTimeOfIndex(testIdx); Datagram[] D = null; long span = origDatagrams[testIdx].getDuration() + 1; long[] offset = new long[1]; // exercise D = tlr.getDatagrams(onTime, span, sampleRate, offset); // verify assertEquals(2, D.length); assertEquals(origDatagrams[testIdx], D[0]); assertEquals(origDatagrams[testIdx + 1], D[1]); assertEquals(0l, offset[0]); } @Test public void getDatagrams5() throws IOException { // setup final int testIdx = NUMDATAGRAMS / 2; long onTime = getTimeOfIndex(testIdx); Datagram[] D = null; long span = origDatagrams[testIdx].getDuration() + origDatagrams[testIdx + 1].getDuration(); long[] offset = new long[1]; // exercise D = tlr.getDatagrams(onTime, span, sampleRate, offset); // verify assertEquals(2, D.length); assertEquals(origDatagrams[testIdx], D[0]); assertEquals(origDatagrams[testIdx + 1], D[1]); assertEquals(0l, offset[0]); } @Test public void getDatagrams6() throws IOException { // setup final int testIdx = NUMDATAGRAMS / 2; long onTime = getTimeOfIndex(testIdx); Datagram[] D = null; long span = origDatagrams[testIdx].getDuration() + origDatagrams[testIdx + 1].getDuration(); long[] offset = new long[1]; // exercise D = tlr.getDatagrams(onTime + 1, span, sampleRate, offset); // verify assertEquals("textIdx=" + testIdx + ", span=" + span + ", dur[" + testIdx + "]=" + origDatagrams[testIdx].getDuration() + ", dur[" + (testIdx + 1) + "]=" + origDatagrams[testIdx + 1].getDuration() + ", offset=" + offset[0], 3, D.length); assertEquals(origDatagrams[testIdx], D[0]); assertEquals(origDatagrams[testIdx + 1], D[1]); assertEquals(origDatagrams[testIdx + 2], D[2]); assertEquals(1l, offset[0]); } @Test public void getDatagrams7() throws IOException { // setup final int testIdx = NUMDATAGRAMS / 2; long onTime = getTimeOfIndex(testIdx); long afterTime = onTime + origDatagrams[testIdx].getDuration(); long midTime = onTime + ((afterTime - onTime) / 2); Datagram[] D = null; long dur = origDatagrams[testIdx].getDuration(); long span = dur - dur / 2 + 1; long[] offset = new long[1]; // exercise D = tlr.getDatagrams(midTime, span, sampleRate, offset); // verify assertEquals(2, D.length); assertEquals(origDatagrams[testIdx], D[0]); assertEquals(origDatagrams[testIdx + 1], D[1]); assertEquals(dur / 2, offset[0]); } @Test public void otherSampleRate() throws IOException { // setup final int testIdx = NUMDATAGRAMS / 2; long onTime = getTimeOfIndex(testIdx); Datagram[] D = null; long dur = origDatagrams[testIdx].getDuration(); long span = dur; // exercise D = tlr.getDatagrams(onTime * 2, span * 2, sampleRate / 2); // verify assertEquals(1, D.length); Assert.assertTrue(areEqual(D[0].getData(), origDatagrams[testIdx].getData())); Assert.assertTrue(D[0].getDuration() != origDatagrams[testIdx].getDuration()); } /** * @param testIdx * @return */ private long getTimeOfIndex(final int testIdx) { long onTime = 0l; for (int i = 0; i < testIdx; i++) { onTime += origDatagrams[i].getDuration(); } return onTime; } @Test public void getLastDatagram() throws MaryConfigurationException, IOException { long totalDur = tlr.getTotalDuration(); Assert.assertTrue(totalDur > 0); Datagram d = tlr.getDatagram(totalDur - 1); Assert.assertTrue(d != null); long dur = d.getDuration(); d = tlr.getDatagram(totalDur - dur); Assert.assertTrue(d != null); } @Test public void getLastDatagrams() throws MaryConfigurationException, IOException { long totalDur = tlr.getTotalDuration(); Assert.assertTrue(totalDur > 0); Datagram[] ds = tlr.getDatagrams(totalDur - 1, 1); Assert.assertTrue(ds != null); Assert.assertTrue(ds.length == 1); ds = tlr.getDatagrams(totalDur - 1, 2); Assert.assertTrue(ds != null); Assert.assertTrue(ds.length == 1); } @Test public void cannotGetAfterLastDatagram() throws MaryConfigurationException, IOException { long totalDur = tlr.getTotalDuration(); Assert.assertTrue(totalDur > 0); try { Datagram d = tlr.getDatagram(totalDur); Assert.fail("Should have thrown a BufferUnderflowException"); } catch (BufferUnderflowException e) { // OK, expected } } @Test public void cannotGetAfterLastDatagrams() throws MaryConfigurationException, IOException { long totalDur = tlr.getTotalDuration(); Assert.assertTrue(totalDur > 0); try { Datagram[] ds = tlr.getDatagrams(totalDur, 1); Assert.fail("Should have thrown a BufferUnderflowException"); } catch (BufferUnderflowException e) { // OK, expected } } @Test public void canReadLongDatagram() throws MaryConfigurationException, IOException { // setup custom fixture for this method TimelineReader timeline = new TimelineReader(tlFileName, false); // do not try memory mapping // exercise Datagram d = timeline.getDatagram(0); // verify Assert.assertEquals(origDatagrams[0].getLength(), d.getLength()); } @Test public void canReadLongDatagrams1() throws MaryConfigurationException, IOException { // setup custom fixture for this method TimelineReader timeline = new TimelineReader(tlFileName, false); // do not try memory mapping // exercise Datagram[] ds = timeline.getDatagrams(0, origDatagrams[0].getDuration() + 1); // verify Assert.assertEquals(2, ds.length); Assert.assertEquals(origDatagrams[0].getLength(), ds[0].getLength()); } @Test public void canReadLongDatagrams2() throws MaryConfigurationException, IOException { // setup custom fixture for this method TimelineReader timeline = new TimelineReader(tlFileName, false); // do not try memory mapping // exercise Datagram[] ds = timeline.getDatagrams(origDatagrams[0].getDuration(), origDatagrams[1].getDuration() + 1); // verify Assert.assertEquals(2, ds.length); Assert.assertEquals(origDatagrams[1].getLength(), ds[0].getLength()); } @AfterClass public static void tearDown() throws IOException { /* Delete the test file */ File fid = new File(tlFileName); fid.delete(); /* ----------- */ System.out.println("End of the timeline symmetry test."); } /** * Compare two arrays of long. * * @param a1 * an array of longs * @param a2 * an array of longs * @return true if they are equal, false if not. */ private static boolean areEqual(long[] a1, long[] a2) { /* Check number of longs */ if (a1.length != a2.length) return false; /* Check contents */ for (int i = 0; i < a1.length; i++) { if (a1[i] != a2[i]) return false; } /* If all passed, then the arrays are equal */ return (true); } /** * Compare two arrays of bytes. * * @param a1 * an array of bytes * @param a2 * an array of bytes * @return true if they are equal, false if not. */ private static boolean areEqual(byte[] a1, byte[] a2) { /* Check number of longs */ if (a1.length != a2.length) return false; /* Check contents */ for (int i = 0; i < a1.length; i++) { if (a1[i] != a2[i]) return false; } /* If all passed, then the arrays are equal */ return (true); } }