001/** 002 * Portions Copyright 2003 Sun Microsystems, Inc. 003 * Portions Copyright 1999-2001 Language Technologies Institute, 004 * Carnegie Mellon University. 005 * All Rights Reserved. Use is subject to license terms. 006 * 007 * See the file "license.terms" for information on usage and 008 * redistribution of this file, and for a DISCLAIMER OF ALL 009 * WARRANTIES. 010 */ 011package com.sun.speech.freetts.clunits; 012 013import java.io.BufferedOutputStream; 014import java.io.BufferedReader; 015import java.io.DataInputStream; 016import java.io.DataOutputStream; 017import java.io.FileInputStream; 018import java.io.FileNotFoundException; 019import java.io.FileOutputStream; 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.InputStreamReader; 023import java.net.URL; 024import java.nio.ByteBuffer; 025import java.nio.MappedByteBuffer; 026import java.nio.channels.FileChannel; 027import java.util.ArrayList; 028import java.util.HashMap; 029import java.util.Iterator; 030import java.util.List; 031import java.util.Map; 032import java.util.NoSuchElementException; 033import java.util.StringTokenizer; 034 035import com.sun.speech.freetts.cart.CART; 036import com.sun.speech.freetts.cart.CARTImpl; 037import com.sun.speech.freetts.relp.SampleInfo; 038import com.sun.speech.freetts.relp.SampleSet; 039import com.sun.speech.freetts.util.BulkTimer; 040import com.sun.speech.freetts.util.Utilities; 041 042 043/** 044 * Provides support for the cluster unit database. The use of the 045 * cluster unit database is confined to this clunits package. This 046 * class provides a main program that can be used to convert from a 047 * text version of the database to a binary version of the database. 048 * 049 * The ClusterUnitDataBase can be loaded from a text or a binary 050 * source. The binary form of the database loads much faster and 051 * therefore is generally used in a deployed system. 052 * 053 */ 054public class ClusterUnitDatabase { 055 056 final static int CLUNIT_NONE = 65535; 057 058 private DatabaseClusterUnit[] units; 059 private UnitType[] unitTypes; 060 private SampleSet sts; 061 private SampleSet mcep; 062 063 private UnitOriginInfo[] unitOrigins; // for debugging 064 065 private int continuityWeight; 066 private int optimalCoupling; 067 private int extendSelections; 068 private int joinMethod; 069 private int[] joinWeights; 070 private int joinWeightShift; 071 072 private Map cartMap = new HashMap(); 073 private CART defaultCart = null; 074 075 private transient List unitList; 076 private transient int lineCount; 077 private transient List unitTypesList; 078 079 private final static int MAGIC = 0xf0cacc1a; 080 private final static int VERSION = 0x1000; 081 082 083 /** 084 * Creates the UnitDatabase from the given input stream. 085 * 086 * @param is the input stream to read the database from 087 * @param isBinary the input stream is a binary stream 088 * 089 * @throws IOException if there is trouble opening the DB 090 */ 091 ClusterUnitDatabase(URL url, boolean isBinary) throws IOException { 092 BulkTimer.LOAD.start("ClusterUnitDatabase"); 093 InputStream is = Utilities.getInputStream(url); 094 if (isBinary) { 095 loadBinary(is); 096 } else { 097 loadText(is); 098 } 099 is.close(); 100 // Attempt to load debug info from a .debug resource. 101 // This will silently fail if no debug info is available. 102 String urlString = url.toExternalForm(); 103 URL debugURL = new URL(urlString.substring(0, urlString.lastIndexOf(".")) + ".debug"); 104 try { 105 InputStream debugInfoStream = Utilities.getInputStream(debugURL); 106 loadUnitOrigins(debugInfoStream); 107 } catch (IOException ioe) { 108 // Silently ignore if you cannot load the debug info 109 } 110 BulkTimer.LOAD.stop("ClusterUnitDatabase"); 111 } 112 113 114 /** 115 * Retrieves the begininning sample index for the 116 * given entry. 117 * 118 * @param unitEntry the entry of interest 119 * 120 * @return the begininning sample index 121 */ 122 int getStart(int unitEntry) { 123 return units[unitEntry].start; 124 } 125 126 /** 127 * Retrieves the ending sample index for the 128 * given entry. 129 * 130 * @param unitEntry the entry of interest 131 * 132 * @return the ending sample index 133 */ 134 int getEnd(int unitEntry) { 135 return units[unitEntry].end; 136 } 137 138 /** 139 * Retrieves the phone for the given entry 140 * 141 * @param unitEntry the entry of interest 142 * 143 * @return the phone for the entry 144 */ 145 int getPhone(int unitEntry) { 146 return units[unitEntry].phone; 147 } 148 149 /** 150 * Returns the cart of the given unit type. 151 * 152 * @param unitType the type of cart 153 * 154 * @return the cart 155 */ 156 CART getTree(String unitType) { 157 CART cart = (CART) cartMap.get(unitType); 158 159 if (cart == null) { 160 System.err.println("ClusterUnitDatabase: can't find tree for " 161 + unitType); 162 return defaultCart; // "graceful" failrue 163 } 164 return cart; 165 } 166 167 /** 168 * Retrieves the type index for the name given a name. 169 * 170 * @param name the name 171 * 172 * @return the index for the name 173 */ 174// [[[TODO: perhaps replace this with java.util.Arrays.binarySearch]]] 175 int getUnitTypeIndex(String name) { 176 int start, end, mid, c; 177 178 start = 0; 179 end = unitTypes.length; 180 181 while (start < end) { 182 mid = (start + end) / 2; 183 c = unitTypes[mid].getName().compareTo(name); 184 if (c == 0) { 185 return mid; 186 } else if (c > 0) { 187 end = mid; 188 } else { 189 start = mid + 1; 190 } 191 } 192 return -1; 193 } 194 195 /** 196 * Retrieves the unit index given a unit type and val. 197 * 198 * @param unitType the type of the unit 199 * @param instance the value associated with the unit 200 * 201 * @return the index. 202 */ 203 int getUnitIndex(String unitType, int instance) { 204 int i = getUnitTypeIndex(unitType); 205 if (i == -1) { 206 error("getUnitIndex: can't find unit type " + unitType); 207 i = 0; 208 } 209 if (instance >= unitTypes[i].getCount()) { 210 error("getUnitIndex: can't find instance " 211 + instance + " of " + unitType); 212 instance = 0; 213 } 214 return unitTypes[i].getStart() + instance; 215 } 216 217 218 /** 219 * Retrieves the index for the name given a name. 220 * 221 * @param name the name 222 * 223 * @return the index for the name 224 */ 225 int getUnitIndexName(String name) { 226 int lastIndex = name.lastIndexOf('_'); 227 if (lastIndex == -1) { 228 error("getUnitIndexName: bad unit name " + name); 229 return -1; 230 } 231 int index = Integer.parseInt(name.substring(lastIndex + 1)); 232 String type = name.substring(0, lastIndex); 233 return getUnitIndex(type, index); 234 } 235 236 /** 237 * Retrieves the extend selections setting. 238 * 239 * @return the extend selections setting 240 */ 241 int getExtendSelections() { 242 return extendSelections; 243 } 244 245 /** 246 * Gets the next unit. 247 * 248 * @return the next unit 249 */ 250 int getNextUnit(int which) { 251 return units[which].next; 252 } 253 254 /** 255 * Gets the previous units. 256 * 257 * @param which which unit is of interest 258 * 259 * @return the previous unit 260 */ 261 int getPrevUnit(int which) { 262 return units[which].prev; 263 } 264 265 266 /** 267 * Determines if the unit types are equal. 268 * 269 * @param unitA the index of unit a 270 * @param unitB the index of unit B 271 * 272 * @return <code>true</code> if the types of units a and b are 273 * equal; otherwise return <code>false</code> 274 */ 275 boolean isUnitTypeEqual(int unitA, int unitB) { 276 return units[unitA].type == units[unitB].type; 277 // String nameA = units[unitA].getName(); 278 // String nameB = units[unitB].getName(); 279 // int lastUnderscore = nameA.lastIndexOf('_'); 280 // return nameA.regionMatches(0, nameB, 0, lastUnderscore + 1); 281 } 282 283 /** 284 * Retrieves the optimal coupling setting. 285 * 286 * @return the optimal coupling setting 287 */ 288 int getOptimalCoupling() { 289 return optimalCoupling; 290 } 291 292 /** 293 * Retrieves the continuity weight setting. 294 * 295 * 296 * @return the continuity weight setting 297 */ 298 int getContinuityWeight() { 299 return continuityWeight; 300 } 301 302 /** 303 * Retrieves the join weights. 304 * 305 * @return the join weights 306 */ 307 int[] getJoinWeights() { 308 return joinWeights; 309 } 310 311 312 /** 313 * Looks up the unit with the given name. 314 * 315 * @param unitName the name of the unit to look for 316 * 317 * @return the unit or the defaultUnit if not found. 318 */ 319 DatabaseClusterUnit getUnit(String unitName) { 320 return null; 321 } 322 323 /** 324 * Looks up the unit with the given index. 325 * 326 * @param index the index of the unit to look for 327 * 328 * @return the unit 329 */ 330 DatabaseClusterUnit getUnit(int which) { 331 return units[which]; 332 } 333 334 /** 335 * Looks up the origin info for the unit with the given index. 336 * 337 * @param index the index of the unit to look for 338 * 339 * @return the origin info for the unit, or null if none is available 340 */ 341 UnitOriginInfo getUnitOriginInfo(int which) { 342 if (unitOrigins != null) 343 return unitOrigins[which]; 344 else 345 return null; 346 } 347 348 349 /** 350 * Returns the name of this UnitDatabase. 351 * 352 * @return the name of the database 353 */ 354 String getName() { 355 return "ClusterUnitDatabase"; 356 } 357 358 /** 359 * Returns the sample info for this set of data. 360 * 361 * @return the sample info 362 */ 363 SampleInfo getSampleInfo() { 364 return sts.getSampleInfo(); 365 } 366 367 368 /** 369 * Gets the sample list. 370 * 371 * @return the sample list 372 */ 373 SampleSet getSts() { 374 return sts; 375 } 376 377 /** 378 * Gets the Mel Ceptra list. 379 * 380 * @return the Mel Ceptra list 381 */ 382 SampleSet getMcep() { 383 return mcep; 384 } 385 386 /** 387 * Determines if the application of the given join weights could 388 * be applied as a simple right-shift. If so return the shift 389 * otherwise return 0. 390 * 391 * @return the amount to right shift (or zero if not possible) 392 */ 393 int getJoinWeightShift() { 394 return joinWeightShift; 395 } 396 397 398 /** 399 * Calculates the join weight shift. 400 * 401 * @param joinWeights the weights to check 402 * 403 * @return the amount to right shift (or zero if not possible) 404 */ 405 private int calcJoinWeightShift(int[] joinWeights) { 406 int first = joinWeights[0]; 407 for (int i = 1; i < joinWeights.length; i++) { 408 if (joinWeights[i] != first) { 409 return 0; 410 } 411 } 412 413 int divisor = 65536 / first; 414 if (divisor == 2) { 415 return 1; 416 } else if (divisor == 4) { 417 return 2; 418 } 419 return 0; 420 } 421 422 /** 423 * Loads the database from the given input stream. 424 * 425 * @param is the input stream 426 */ 427 private void loadText(InputStream is) { 428 BufferedReader reader; 429 String line; 430 431 432 unitList = new ArrayList(); 433 unitTypesList = new ArrayList(); 434 435 if (is == null) { 436 throw new Error("Can't load cluster db file."); 437 } 438 439 reader = new BufferedReader(new InputStreamReader(is)); 440 try { 441 line = reader.readLine(); 442 lineCount++; 443 while (line != null) { 444 if (!line.startsWith("***")) { 445 parseAndAdd(line, reader); 446 } 447 line = reader.readLine(); 448 } 449 reader.close(); 450 451 units = new DatabaseClusterUnit[unitList.size()]; 452 units = (DatabaseClusterUnit[]) unitList.toArray(units); 453 unitList = null; 454 455 unitTypes = new UnitType[unitTypesList.size()]; 456 unitTypes = (UnitType[]) unitTypesList.toArray(unitTypes); 457 unitTypesList = null; 458 459 } catch (IOException e) { 460 throw new Error(e.getMessage() + " at line " + lineCount); 461 } finally { 462 } 463 } 464 465 /** 466 * Parses and process the given line. 467 * 468 * @param line the line to process 469 * @param reader the source for the lines 470 * 471 * @throws IOException if an error occurs while reading 472 */ 473 private void parseAndAdd(String line, BufferedReader reader) 474 throws IOException { 475 try { 476 StringTokenizer tokenizer = new StringTokenizer(line," "); 477 String tag = tokenizer.nextToken(); 478 if (tag.equals("CONTINUITY_WEIGHT")) { 479 continuityWeight = Integer.parseInt(tokenizer.nextToken()); 480 } else if (tag.equals("OPTIMAL_COUPLING")) { 481 optimalCoupling = Integer.parseInt(tokenizer.nextToken()); 482 } else if (tag.equals("EXTEND_SELECTIONS")) { 483 extendSelections = Integer.parseInt(tokenizer.nextToken()); 484 } else if (tag.equals("JOIN_METHOD")) { 485 joinMethod = Integer.parseInt(tokenizer.nextToken()); 486 } else if (tag.equals("JOIN_WEIGHTS")) { 487 int numWeights = Integer.parseInt(tokenizer.nextToken()); 488 joinWeights = new int[numWeights]; 489 for (int i = 0; i < numWeights; i++) { 490 joinWeights[i] = Integer.parseInt(tokenizer.nextToken()); 491 } 492 493 joinWeightShift = calcJoinWeightShift(joinWeights); 494 495 } else if (tag.equals("STS")) { 496 String name = tokenizer.nextToken(); 497 if (name.equals("STS")) { 498 sts = new SampleSet(tokenizer, reader); 499 } else { 500 mcep = new SampleSet(tokenizer, reader); 501 } 502 } else if (tag.equals("UNITS")) { 503 int type = Integer.parseInt(tokenizer.nextToken()); 504 int phone = Integer.parseInt(tokenizer.nextToken()); 505 int start = Integer.parseInt(tokenizer.nextToken()); 506 int end = Integer.parseInt(tokenizer.nextToken()); 507 int prev = Integer.parseInt(tokenizer.nextToken()); 508 int next = Integer.parseInt(tokenizer.nextToken()); 509 DatabaseClusterUnit unit 510 = new DatabaseClusterUnit(type, phone, start, 511 end, prev, next); 512 unitList.add(unit); 513 } else if (tag.equals("CART")) { 514 String name = tokenizer.nextToken(); 515 int nodes = Integer.parseInt(tokenizer.nextToken()); 516 CART cart = new CARTImpl(reader, nodes); 517 cartMap.put(name, cart); 518 519 if (defaultCart == null) { 520 defaultCart = cart; 521 } 522 } else if (tag.equals("UNIT_TYPE")) { 523 String name = tokenizer.nextToken(); 524 int start = Integer.parseInt(tokenizer.nextToken()); 525 int count = Integer.parseInt(tokenizer.nextToken()); 526 UnitType unitType = new UnitType(name, start, count); 527 unitTypesList.add(unitType); 528 } else { 529 throw new Error("Unsupported tag " + tag + " in db line `" + line + "'"); 530 } 531 } catch (NoSuchElementException nse) { 532 throw new Error("Error parsing db " + nse.getMessage()); 533 } catch (NumberFormatException nfe) { 534 throw new Error("Error parsing numbers in db line `" + line + "':" + nfe.getMessage()); 535 } 536 } 537 538 /** 539 * Loads a binary file from the input stream. 540 * 541 * @param is the input stream to read the database from 542 * 543 * @throws IOException if there is trouble opening the DB 544 * 545 */ 546 private void loadBinary(InputStream is) throws IOException { 547 // we get better performance if we can map the file in 548 // 1.0 seconds vs. 1.75 seconds, but we can't 549 // always guarantee that we can do that. 550 if (is instanceof FileInputStream) { 551 FileInputStream fis = (FileInputStream) is; 552 FileChannel fc = fis.getChannel(); 553 554 MappedByteBuffer bb = 555 fc.map(FileChannel.MapMode.READ_ONLY, 0, (int) fc.size()); 556 bb.load(); 557 loadBinary(bb); 558 is.close(); 559 } else { 560 loadBinary(new DataInputStream(is)); 561 } 562 } 563 564 /** 565 * Loads the database from the given byte buffer. 566 * 567 * @param bb the byte buffer to load the db from 568 * 569 * @throws IOException if there is trouble opening the DB 570 */ 571 private void loadBinary(ByteBuffer bb) throws IOException { 572 573 if (bb.getInt() != MAGIC) { 574 throw new Error("Bad magic in db"); 575 } 576 if (bb.getInt() != VERSION) { 577 throw new Error("Bad VERSION in db"); 578 } 579 580 continuityWeight = bb.getInt(); 581 optimalCoupling = bb.getInt(); 582 extendSelections = bb.getInt(); 583 joinMethod = bb.getInt(); 584 joinWeightShift = bb.getInt(); 585 586 int weightLength = bb.getInt(); 587 joinWeights = new int[weightLength]; 588 for (int i = 0; i < joinWeights.length; i++) { 589 joinWeights[i] = bb.getInt(); 590 } 591 592 int unitsLength = bb.getInt(); 593 units = new DatabaseClusterUnit[unitsLength]; 594 for (int i = 0; i < units.length; i++) { 595 units[i] = new DatabaseClusterUnit(bb); 596 } 597 598 int unitTypesLength = bb.getInt(); 599 unitTypes = new UnitType[unitTypesLength]; 600 for (int i = 0; i < unitTypes.length; i++) { 601 unitTypes[i] = new UnitType(bb); 602 } 603 sts = new SampleSet(bb); 604 mcep = new SampleSet(bb); 605 606 int numCarts = bb.getInt(); 607 cartMap = new HashMap(); 608 for (int i = 0; i < numCarts; i++) { 609 String name = Utilities.getString(bb); 610 CART cart = CARTImpl.loadBinary(bb); 611 cartMap.put(name, cart); 612 613 if (defaultCart == null) { 614 defaultCart = cart; 615 } 616 } 617 } 618 619 /** 620 * Loads the database from the given input stream. 621 * 622 * @param is the input stream to load the db from 623 * 624 * @throws IOException if there is trouble opening the DB 625 */ 626 private void loadBinary(DataInputStream is) throws IOException { 627 628 if (is.readInt() != MAGIC) { 629 throw new Error("Bad magic in db"); 630 } 631 if (is.readInt() != VERSION) { 632 throw new Error("Bad VERSION in db"); 633 } 634 635 continuityWeight = is.readInt(); 636 optimalCoupling = is.readInt(); 637 extendSelections = is.readInt(); 638 joinMethod = is.readInt(); 639 joinWeightShift = is.readInt(); 640 641 int weightLength = is.readInt(); 642 joinWeights = new int[weightLength]; 643 for (int i = 0; i < joinWeights.length; i++) { 644 joinWeights[i] = is.readInt(); 645 } 646 647 int unitsLength = is.readInt(); 648 units = new DatabaseClusterUnit[unitsLength]; 649 for (int i = 0; i < units.length; i++) { 650 units[i] = new DatabaseClusterUnit(is); 651 } 652 653 int unitTypesLength = is.readInt(); 654 unitTypes = new UnitType[unitTypesLength]; 655 for (int i = 0; i < unitTypes.length; i++) { 656 unitTypes[i] = new UnitType(is); 657 } 658 sts = new SampleSet(is); 659 mcep = new SampleSet(is); 660 661 int numCarts = is.readInt(); 662 cartMap = new HashMap(); 663 for (int i = 0; i < numCarts; i++) { 664 String name = Utilities.getString(is); 665 CART cart = CARTImpl.loadBinary(is); 666 cartMap.put(name, cart); 667 668 if (defaultCart == null) { 669 defaultCart = cart; 670 } 671 } 672 } 673 674 /** 675 * Load debug info about the origin of units from the given input stream. 676 * The file format is identical to that of the Festvox .catalogue files. 677 * This is useful when creating and debugging new voices: For a selected 678 * unit, you can find out which unit from which original sound file 679 * was used. 680 * @param is the input stream from which to read the debug info. 681 * @throws IOException if a read problem occurs. 682 */ 683 private void loadUnitOrigins(InputStream is) throws IOException 684 { 685 unitOrigins = new UnitOriginInfo[units.length]; 686 BufferedReader in = new BufferedReader(new InputStreamReader(is)); 687 688 String currentLine = null; 689 // Skip EST header: 690 while ((currentLine = in.readLine()) != null) { 691 if (currentLine.startsWith("EST_Header_End")) break; 692 } 693 while ((currentLine = in.readLine()) != null) { 694 String[] tokens = currentLine.split(" "); 695 String name = tokens[0]; 696 int index = getUnitIndexName(name); 697 try { 698 unitOrigins[index] = new UnitOriginInfo(); 699 unitOrigins[index].originFile = tokens[1]; 700 unitOrigins[index].originStart = Float.valueOf(tokens[2]).floatValue(); 701 unitOrigins[index].originEnd = Float.valueOf(tokens[4]).floatValue(); 702 } catch (NumberFormatException nfe) {} 703 } 704 in.close(); 705 } 706 707 708 /** 709 * Dumps a binary form of the database. 710 * 711 * @param path the path to dump the file to 712 */ 713 void dumpBinary(String path) { 714 try { 715 FileOutputStream fos = new FileOutputStream(path); 716 DataOutputStream os = new DataOutputStream(new 717 BufferedOutputStream(fos)); 718 719 os.writeInt(MAGIC); 720 os.writeInt(VERSION); 721 os.writeInt(continuityWeight); 722 os.writeInt(optimalCoupling); 723 os.writeInt(extendSelections); 724 os.writeInt(joinMethod); 725 os.writeInt(joinWeightShift); 726 os.writeInt(joinWeights.length); 727 for (int i = 0; i < joinWeights.length; i++) { 728 os.writeInt(joinWeights[i]); 729 } 730 731 os.writeInt(units.length); 732 for (int i = 0; i < units.length; i++) { 733 units[i].dumpBinary(os); 734 } 735 736 os.writeInt(unitTypes.length); 737 for (int i = 0; i < unitTypes.length; i++) { 738 unitTypes[i].dumpBinary(os); 739 } 740 sts.dumpBinary(os); 741 mcep.dumpBinary(os); 742 743 os.writeInt(cartMap.size()); 744 for (Iterator i = cartMap.keySet().iterator(); i.hasNext();) { 745 String name = (String) i.next(); 746 CART cart = (CART) cartMap.get(name); 747 748 Utilities.outString(os, name); 749 cart.dumpBinary(os); 750 } 751 os.close(); 752 753 // note that we are not currently saving the state 754 // of the default cart 755 756 } catch (FileNotFoundException fe) { 757 throw new Error("Can't dump binary database " + 758 fe.getMessage()); 759 } catch (IOException ioe) { 760 throw new Error("Can't write binary database " + 761 ioe.getMessage()); 762 } 763 } 764 765 766 /** 767 * Determines if two databases are identical. 768 * 769 * @param other the database to compare this one to 770 * 771 * @return true if the databases are identical 772 */ 773 public boolean compare(ClusterUnitDatabase other) { 774 System.out.println("Warning: Compare not implemented yet"); 775 return false; 776 } 777 778 /** 779 * Manipulates a ClusterUnitDatabase. 780 * 781 * <p> 782 * <b> Usage </b> 783 * <p> 784 * <code> java com.sun.speech.freetts.clunits.ClusterUnitDatabase 785 * [options]</code> 786 * <p> 787 * <b> Options </b> 788 * <p> 789 * <ul> 790 * <li> <code> -src path </code> provides a directory 791 * path to the source text for the database 792 * <li> <code> -dest path </code> provides a directory 793 * for where to place the resulting binaries 794 * <li> <code> -generate_binary [filename]</code> reads 795 * in the text version of the database and generates 796 * the binary version of the database. 797 * <li> <code> -compare </code> Loads the text and 798 * binary versions of the database and compares them to 799 * see if they are equivalent. 800 * <li> <code> -showTimes </code> shows timings for any 801 * loading, comparing or dumping operation 802 * </ul> 803 * 804 */ 805 public static void main(String[] args) { 806 boolean showTimes = false; 807 String srcPath = "."; 808 String destPath = "."; 809 810 try { 811 if (args.length > 0) { 812 BulkTimer timer = new BulkTimer(); 813 timer.start(); 814 for (int i = 0 ; i < args.length; i++) { 815 if (args[i].equals("-src")) { 816 srcPath = args[++i]; 817 } else if (args[i].equals("-dest")) { 818 destPath = args[++i]; 819 } else if (args[i].equals("-generate_binary")) { 820 String name = "clunits.txt"; 821 if (i + 1 < args.length) { 822 String nameArg = args[++i]; 823 if (!nameArg.startsWith("-")) { 824 name = nameArg; 825 } 826 } 827 828 int suffixPos = name.lastIndexOf(".txt"); 829 830 String binaryName = "clunits.bin"; 831 if (suffixPos != -1) { 832 binaryName = name.substring(0, suffixPos) + ".bin"; 833 } 834 835 System.out.println("Loading " + name); 836 timer.start("load_text"); 837 ClusterUnitDatabase udb = new 838 ClusterUnitDatabase( 839 new URL("file:" + srcPath + "/" + name), 840 false); 841 timer.stop("load_text"); 842 843 System.out.println("Dumping " + binaryName); 844 timer.start("dump_binary"); 845 udb.dumpBinary(destPath + "/" + binaryName); 846 timer.stop("dump_binary"); 847 848 } else if (args[i].equals("-compare")) { 849 850 timer.start("load_text"); 851 ClusterUnitDatabase udb = new 852 ClusterUnitDatabase( 853 new URL("file:./cmu_time_awb.txt"), false); 854 timer.stop("load_text"); 855 856 timer.start("load_binary"); 857 ClusterUnitDatabase budb = 858 new ClusterUnitDatabase( 859 new URL("file:./cmu_time_awb.bin"), true); 860 timer.stop("load_binary"); 861 862 timer.start("compare"); 863 if (udb.compare(budb)) { 864 System.out.println("other compare ok"); 865 } else { 866 System.out.println("other compare different"); 867 } 868 timer.stop("compare"); 869 } else if (args[i].equals("-showtimes")) { 870 showTimes = true; 871 } else { 872 System.out.println("Unknown option " + args[i]); 873 } 874 } 875 timer.stop(); 876 if (showTimes) { 877 timer.show("ClusterUnitDatabase"); 878 } 879 } else { 880 System.out.println("Options: "); 881 System.out.println(" -src path"); 882 System.out.println(" -dest path"); 883 System.out.println(" -compare"); 884 System.out.println(" -generate_binary"); 885 System.out.println(" -showTimes"); 886 } 887 } catch (IOException ioe) { 888 System.err.println(ioe); 889 } 890 } 891 892 893 /** 894 * Represents a unit for the cluster database. 895 */ 896 class DatabaseClusterUnit { 897 898 int type; 899 int phone; 900 int start; 901 int end; 902 int prev; 903 int next; 904 905 /** 906 * Constructs a unit. 907 * 908 * @param type the name of the unit 909 * @param phone the name of the unit 910 * @param start the starting frame 911 * @param end the ending frame 912 * @param prev the previous index 913 * @param next the next index 914 */ 915 DatabaseClusterUnit(int type, int phone, int start, 916 int end, int prev, int next) { 917 this.type = type; 918 this.phone = phone; 919 this.start = start; 920 this.end = end; 921 this.prev = prev; 922 this.next = next; 923 } 924 925 /** 926 * Creates a unit by reading it from the given byte buffer. 927 * 928 * @param bb source of the DatabaseClusterUnit data 929 * 930 * @throws IOException if an IO error occurs 931 */ 932 DatabaseClusterUnit(ByteBuffer bb) throws IOException { 933 this.type = bb.getInt(); 934 this.phone = bb.getInt(); 935 this.start = bb.getInt(); 936 this.end = bb.getInt(); 937 this.prev = bb.getInt(); 938 this.next = bb.getInt(); 939 } 940 941 /** 942 * Creates a unit by reading it from the given input stream. 943 * 944 * @param is source of the DatabaseClusterUnit data 945 * 946 * @throws IOException if an IO error occurs 947 */ 948 DatabaseClusterUnit(DataInputStream is) throws IOException { 949 this.type = is.readInt(); 950 this.phone = is.readInt(); 951 this.start = is.readInt(); 952 this.end = is.readInt(); 953 this.prev = is.readInt(); 954 this.next = is.readInt(); 955 } 956 957 /** 958 * Returns the name of the unit. 959 * 960 * @return the name 961 */ 962 String getName() { 963 return unitTypes[type].getName(); 964 } 965 966 /** 967 * Dumps this unit to the given output stream. 968 * 969 * @param os the output stream 970 * 971 * @throws IOException if an error occurs. 972 */ 973 void dumpBinary(DataOutputStream os) throws IOException { 974 os.writeInt(type); 975 os.writeInt(phone); 976 os.writeInt(start); 977 os.writeInt(end); 978 os.writeInt(prev); 979 os.writeInt(next); 980 } 981 } 982 983 /** 984 * Represents debug information about the origin of a unit. 985 */ 986 class UnitOriginInfo { 987 String originFile; 988 float originStart; 989 float originEnd; 990 } 991 992 /** 993 * Displays an error message 994 * 995 * @param s the error message 996 */ 997 private void error(String s) { 998 System.out.println("ClusterUnitDatabase Error: " + s); 999 } 1000} 1001 1002/** 1003 * Represents a unit type in the system 1004 */ 1005class UnitType { 1006 private String name; 1007 private int start; 1008 private int count; 1009 1010 /** 1011 * Constructs a UnitType from the given parameters 1012 * 1013 * @param name the name of the type 1014 * @param start the starting index for this type 1015 * @param count the number of elements for this type 1016 */ 1017 UnitType(String name, int start, int count) { 1018 this.name = name; 1019 this.start = start; 1020 this.count = count; 1021 } 1022 1023 /** 1024 * Creates a unit type by reading it from the given input stream. 1025 * 1026 * @param is source of the UnitType data 1027 * 1028 * @throws IOException if an IO error occurs 1029 */ 1030 UnitType(DataInputStream is) throws IOException { 1031 this.name = Utilities.getString(is); 1032 this.start = is.readInt(); 1033 this.count = is.readInt(); 1034 } 1035 1036 /** 1037 * Creates a unit type by reading it from the given byte buffer. 1038 * 1039 * @param bb source of the UnitType data 1040 * 1041 * @throws IOException if an IO error occurs 1042 */ 1043 UnitType(ByteBuffer bb) throws IOException { 1044 this.name = Utilities.getString(bb); 1045 this.start = bb.getInt(); 1046 this.count = bb.getInt(); 1047 } 1048 1049 /** 1050 * Gets the name for this unit type 1051 * 1052 * @return the name for the type 1053 */ 1054 String getName() { 1055 return name; 1056 } 1057 1058 /** 1059 * Gets the start index for this type 1060 * 1061 * @return the start index 1062 */ 1063 int getStart() { 1064 return start; 1065 } 1066 1067 /** 1068 * Gets the count for this type 1069 * 1070 * @return the count for this type 1071 */ 1072 int getCount() { 1073 return count; 1074 } 1075 1076 /** 1077 * Dumps this unit to the given output stream. 1078 * 1079 * @param os the output stream 1080 * 1081 * @throws IOException if an error occurs. 1082 */ 1083 void dumpBinary(DataOutputStream os) throws IOException { 1084 Utilities.outString(os, name); 1085 os.writeInt(start); 1086 os.writeInt(count); 1087 } 1088}