001 ///////////////////////////////////////////////// 002 // This file is part of Sears project. 003 // Subtitle Editor And Re-Synch 004 // A tool to easily modify and resynch movies subtitles. 005 ///////////////////////////////////////////////// 006 //This program is free software; 007 //you can redistribute it and/or modify it under the terms 008 //of the GNU General Public License 009 //as published by the Free Software Foundation; 010 //either version 2 of the License, or (at your option) any later version. 011 ///////////////////////////////////////////////// 012 //Sears project is available under sourceforge 013 // at adress: http://sourceforge.net/projects/sears/ 014 //Copyright (C) 2005 Booba Skaya 015 //Mail: booba.skaya@gmail.com 016 //////////////////////////////////////////////// 017 package sears.file; 018 019 import java.io.File; 020 import java.util.ArrayList; 021 import java.util.Collections; 022 import java.util.Iterator; 023 import java.util.NoSuchElementException; 024 import java.util.StringTokenizer; 025 026 import sears.tools.LinearInterpolation; 027 import sears.tools.Trace; 028 029 /** 030 * Class SubtitleFile. 031 * <br><b>Summary:</b><br> 032 * This method represent a subtitle file. 033 * It provides facilities on subtitles, as delay, resynchro. 034 * It must be specialized to the subtitle file type you want to open. 035 */ 036 public abstract class SubtitleFile { 037 /**The system File that contains the file*/ 038 protected File file; 039 040 /**The ArrayList of subtitles found*/ 041 protected ArrayList<Subtitle> subtitleList; 042 043 /**A boolean to know if file has changed*/ 044 protected boolean fileChanged; 045 046 /**The temporary file */ 047 protected File temporaryFile = null; 048 049 /** (<b>LinearInterpolation</b>) LinearInterpolation: The LinearInterpolation used for magic resynchro. */ 050 private LinearInterpolation linearInterpolation; 051 052 /** (<b>String</b>) END_OF_LINE: The system END_OF_LINE */ 053 protected final String END_OF_LINE = System.getProperty("line.separator"); 054 055 /** 056 * Constructor SubtitleFile. 057 * <br><b>Summary:</b><br> 058 * Constructor of the class. 059 * Beware not to use this file directly, because it does contains no ST. 060 * You will have to fill the list of ST, and save the File first. 061 */ 062 public SubtitleFile() { 063 //set file to null 064 file = null; 065 //Construct an empty list. 066 subtitleList = new ArrayList<Subtitle>(); 067 //File is changed 068 fileChanged = true; 069 } 070 071 /** 072 * Constructor SubtitleFile. 073 * <br><b>Summary:</b><br> 074 * Constructor of the class. 075 * @param fileToOpen The <b>(String)</b> path to file to open. 076 * @param _subtitlesList The <b>(ArrayList)</b> List of subtitles. 077 */ 078 public SubtitleFile(String fileToOpen, ArrayList<Subtitle> _subtitlesList) { 079 construct(new File(fileToOpen), _subtitlesList); 080 } 081 082 /** 083 * Constructor SubtitleFile. 084 * <br><b>Summary:</b><br> 085 * Constructor of the class. 086 * @param _file The <b>(File)</b> file to open. 087 * @param _subtitlesList The <b>(ArrayList)</b> List of subtitles. 088 */ 089 public SubtitleFile(File _file, ArrayList<Subtitle> _subtitlesList) { 090 construct(_file, _subtitlesList); 091 } 092 093 /** 094 * Method construct. 095 * <br><b>Summary:</b><br> 096 * Construct the file. 097 * @param _srtFile The <b>(File)</b> file to open. 098 * @param _subtitlesList The <b>(ArrayList<Subtitle>)</b> List of subtitles. 099 */ 100 private void construct(File _srtFile, ArrayList<Subtitle> _subtitlesList) { 101 if (!_srtFile.exists()) 102 Trace.trace(("File " + _srtFile.getAbsolutePath() + " Does not exist !"), Trace.WARNING_PRIORITY); 103 if (_subtitlesList == null) 104 Trace.trace("The given subtitleList is null !", Trace.WARNING_PRIORITY); 105 file = _srtFile; 106 subtitleList = _subtitlesList; 107 parse(); 108 //File is not changed 109 fileChanged = false; 110 111 } 112 113 /** 114 * Method parse. 115 * <br><b>Summary:</b><br> 116 * This method parse the current file, and construct the subtitleList. 117 */ 118 protected abstract void parse(); 119 120 /** 121 * Method getContentDirectory. 122 * <br><b>Summary:</b><br> 123 * This method return the file's parent folder. 124 * @return <b>(File)</b> The parent of the current file. 125 */ 126 public File getContentDirectory() { 127 return this.getFile().getParentFile(); 128 } 129 130 /** 131 * Method getFile. 132 * <br><b>Summary:</b><br> 133 * Return the current file. 134 * @return <b>(File)</b> The current file. 135 */ 136 public File getFile() { 137 return file; 138 } 139 140 /** 141 * Method getTemporaryFile. 142 * <br><b>Summary:</b><br> 143 * Return the temporary file. 144 * @return <b>(File)</b> The temporary file. 145 */ 146 public File getTemporaryFile() { 147 return temporaryFile; 148 } 149 150 /** 151 * Method timeToString. 152 * <br><b>Summary:</b><br> 153 * This method transform a number of milliseconds in a string representation. 154 * @param milliseconds The number of milliseconds to transform 155 * @return <b>(String)</b> The corresponding String representation of the number of milliseconds. 156 */ 157 public static String timeToString(int milliseconds){ 158 //keep the sign in memory. 159 boolean positive = milliseconds >= 0; 160 //And set time aboslute, not to be annoyed with signs. 161 milliseconds = Math.abs(milliseconds); 162 //Compute seconds/minutes/hours from milliseconds. 163 int seconds = milliseconds / 1000; 164 milliseconds = milliseconds - seconds * 1000; 165 int hours = (seconds - seconds % 3600) / 3600; 166 seconds -= hours * 3600; 167 int minutes = (seconds - seconds % 60) / 60; 168 seconds -= minutes * 60; 169 //Compute String representation of these values. 170 //Using 2 digits formatting 171 String hoursString = ""; 172 hours = Math.abs(hours); 173 if (hours < 10){ 174 hoursString += "0" + hours; 175 } 176 else{ 177 hoursString += hours; 178 } 179 String minutesString = ""; 180 if (minutes < 10){ 181 minutesString += "0" + minutes; 182 }else{ 183 minutesString += minutes; 184 } 185 String secondsString = ""; 186 if (seconds < 10){ 187 secondsString += "0" + seconds; 188 } 189 else{ 190 secondsString += seconds; 191 } 192 String millisecondsString =""; 193 if (milliseconds < 10){ 194 millisecondsString += "00" + milliseconds; 195 }else if (milliseconds < 100){ 196 millisecondsString += "0" + milliseconds; 197 }else{ 198 millisecondsString += "" +milliseconds; 199 } 200 String result = ""; 201 //Remember to set the String negative, if it was negative at the begining. 202 if(!positive){ 203 result = "-"; 204 } 205 result = result + hoursString + ":" + minutesString + ":" + secondsString + "," +millisecondsString; 206 return result; 207 208 } 209 210 /** 211 * Method stringToTime. 212 * <br><b>Summary:</b><br> 213 * Return the number of miliseconds that correspond to the given String time representation. 214 * @param time The string srt time representation. 215 * @return <b>(int)</b> The corresponding number of miliseconds. 216 */ 217 public static int stringToTime(String time) throws NumberFormatException{ 218 //the result of the method. 219 int result = 0; 220 //Check the sign. 221 boolean positive = true; 222 //If time start with a '-', it means it is a negative time. 223 if(time != null && time.trim().startsWith("-")){ 224 positive =false; 225 //remove the '-' char. 226 int indexOfMinus = time.indexOf("-"); 227 if(indexOfMinus != -1){ 228 time = time.substring(indexOfMinus+1); 229 } 230 } 231 //Parse the time String. 232 StringTokenizer stk = new StringTokenizer(time, ":,. "); 233 try { 234 result += 3600 * Integer.parseInt(stk.nextToken()); 235 result += 60 * Integer.parseInt(stk.nextToken()); 236 result += Integer.parseInt(stk.nextToken()); 237 result = result*1000; 238 result += Integer.parseInt(stk.nextToken()); 239 //restore the sign. 240 if(!positive){ 241 result = -result; 242 } 243 } catch (NoSuchElementException e) { 244 //If there is a time error, throw a number format exception. 245 throw new NumberFormatException("Bad Time Format found:"+time); 246 } 247 //return the result of the method. 248 return result; 249 } 250 251 /** 252 * Method writeToFile. 253 * <br><b>Summary:</b><br> 254 * Use this method to write subtitle file to the given File. 255 * @param fileToWrite The File to write the file. 256 */ 257 public abstract void writeToFile(File fileToWrite); 258 259 /** 260 * Method writeToTemporaryFile. 261 * <br><b>Summary:</b><br> 262 * Use this method to write subtitle file to the temporary File. 263 */ 264 public abstract void writeToTemporaryFile(); 265 266 /** 267 * Method addFakeSub. 268 * <br><b>Summary:</b><br> 269 * Use this method to create an empty subtitle file. 270 * 271 */ 272 public void addFakeSub(){ 273 subtitleList.add(new Subtitle(0,0,1,"")); 274 } 275 276 /** 277 * Method delay. 278 * <br><b>Summary:</b><br> 279 * Apply a delay on the given range subtitles. 280 * @param beginIndex The first index to begin delay. 281 * @param endIndex The last index to put a delay 282 * @param delay The delay to Apply. 283 */ 284 public void delay(int beginIndex, int endIndex, int delay) { 285 for (int index = beginIndex; index <= endIndex; index++) { 286 delaySubtitle(delay, index); 287 } 288 } 289 290 291 292 /** 293 * Method delay. 294 * <br><b>Summary:</b><br> 295 * Delay a list of subtitles, idetified by their index. 296 * @param indexToDelay The array of subtitle's index to be delayed. 297 * @param delay The delay to apply. 298 */ 299 public void delay(int[] indexToDelay, int delay) { 300 if (indexToDelay.length == 0){ 301 delay(delay); 302 }else { 303 for (int i = 0; i < indexToDelay.length; i++) { 304 delaySubtitle(delay, indexToDelay[i]); 305 } 306 } 307 } 308 309 /** 310 * Method delay. 311 * <br><b>Summary:</b><br> 312 * Use this method to delay whole file. 313 * @param delay The delay to apply. 314 */ 315 public void delay(int delay) { 316 delay(0, subtitleList.size() - 1, delay); 317 } 318 319 /** 320 * Method delaySubtitle. 321 * <br><b>Summary:</b><br> 322 * Delay a given subtitle, idetified by its index. 323 * @param delay The delay to apply. 324 * @param index The index of the subtitle to delay. 325 */ 326 private void delaySubtitle(int delay, int index) { 327 Subtitle subtitle = (Subtitle) subtitleList.get(index); 328 subtitle.delay(delay); 329 //indicate the file has changed. 330 fileChanged = true; 331 } 332 333 /** 334 * Method normalizeDuration. 335 * <br><b>Summary:</b><br> 336 * This method permits to normalize the subtitle duration for whole file. 337 * It ensures that subtitle display duration is beetween given minDuration, and maxDuration. 338 * It raise or lower it to fit in the interval. 339 * It takes care that subtitle do not ends before the start of its follower. 340 * If minDuration equals to -1, it does not checks the minDuration. 341 * If maxDuration equals to -1, it does not checks the maxDuration. 342 * @param minDuration The min duration to ensure. 343 * @param maxDuration The max duration to ensure. 344 */ 345 public void normalizeDuration(int minDuration, int maxDuration){ 346 normalizeDuration(0, subtitleList.size() - 1, minDuration, maxDuration); 347 } 348 349 /** 350 * Method normalizeDuration. 351 * <br><b>Summary:</b><br> 352 * This method permits to normalize the subtitle duration for given index interval. 353 * It ensures that subtitle display duration is beetween given minDuration, and maxDuration. 354 * It raise or lower it to fit in the interval. 355 * It takes care that subtitle do not ends before the start of its follower. 356 * If minDuration equals to -1, it does not checks the minDuration. 357 * If maxDuration equals to -1, it does not checks the maxDuration. 358 * @param beginIndex The start index to begin the normalization. 359 * @param endIndex The end index to finish the normalization. 360 * @param minDuration The min duration to ensure. 361 * @param maxDuration The max duration to ensure. 362 */ 363 private void normalizeDuration(int beginIndex, int endIndex, int minDuration, int maxDuration) { 364 for (int index = beginIndex; index <= endIndex; index++) { 365 normalizeSubtitleDuration(minDuration, maxDuration, index); 366 } 367 } 368 369 /** 370 * Method normalizeDuration. 371 * <br><b>Summary:</b><br> 372 * This method permits to normalize the subtitle duration for given subtitle indexes. 373 * It ensures that subtitle display duration is beetween given minDuration, and maxDuration. 374 * It raise or lower it to fit in the interval. 375 * It takes care that subtitle do not ends before the start of its follower. 376 * If minDuration equals to -1, it does not checks the minDuration. 377 * If maxDuration equals to -1, it does not checks the maxDuration. 378 * If given indexes array is empty, it normalizes the whole file. 379 * @param indexToNormalize The array of subtitle index to be normalized. 380 * @param minDuration The min duration to ensure. 381 * @param maxDuration The max duration to ensure. 382 */ 383 public void normalizeDuration(int[] indexToNormalize, int minDuration, int maxDuration) { 384 if (indexToNormalize.length == 0){ 385 normalizeDuration(minDuration, maxDuration); 386 }else { 387 for (int i = 0; i < indexToNormalize.length; i++) { 388 normalizeSubtitleDuration(minDuration, maxDuration, indexToNormalize[i]); 389 } 390 } 391 } 392 393 /** 394 * Method normalizeSubtitleDuration. 395 * <br><b>Summary:</b><br> 396 * This method permits to normalize the subtitle duration. 397 * It ensures that subtitle display duration is beetween given minDuration, and maxDuration. 398 * It raise or lower it to fit in the interval. 399 * It takes care that subtitle do not ends before the start of its follower. 400 * If minDuration equals to -1, it does not checks the minDuration. 401 * If maxDuration equals to -1, it does not checks the maxDuration. 402 * @param minDuration The min duration to ensure. 403 * @param maxDuration The max duration to ensure. 404 * @param index The index of the subtitle to be normalized. 405 */ 406 private void normalizeSubtitleDuration(int minDuration, int maxDuration, int index) { 407 //retrieve the Subtitle at the given index. 408 Subtitle subtitle = (Subtitle) subtitleList.get(index); 409 //Compute its duration. 410 int endDate = subtitle.getEndDate(); 411 int startDate = subtitle.getStartDate(); 412 int duration = endDate - startDate; 413 int newEndDate = endDate; 414 //Then check the duration with given criterias. 415 //The minduration. 416 if(minDuration != -1 && duration < minDuration){ 417 //The duration is not enough long. 418 //We must get the next subtitle start date, not to have the end date after 419 //The next subtitle start date. 420 newEndDate = startDate + minDuration; 421 422 } 423 //The max duration. 424 if(maxDuration != -1 && duration > maxDuration){ 425 //The duration is too long. 426 //Normalize ! 427 newEndDate = startDate + maxDuration; 428 } 429 //Beware not to ovveride the next subtitle startDate. 430 //check that subtitle is not the last one. 431 if(index < getSubtitles().size()-1){ 432 //retrieve next subtitle start date. 433 int nextSubtitleStartDate = ((Subtitle) getSubtitles().get(index+1)).getStartDate(); 434 if(nextSubtitleStartDate <= newEndDate){ 435 //If overriding problem may occurs, readjust newEndDate. 436 newEndDate = nextSubtitleStartDate - 1; 437 } 438 } 439 //And beware not to be inferior to current start date 440 //Yeah i know, its a lot of verification. 441 if(newEndDate < startDate){ 442 //There is a glitch with next subtitle. do nothing. 443 //The file need a time repair. 444 newEndDate = endDate; 445 } 446 //Normalize ! 447 subtitle.setEndDate(newEndDate); 448 //indicate that the file has changed. 449 fileChanged = true; 450 } 451 452 /** 453 * Method resynchro. 454 * <br><b>Summary:</b><br> 455 * Use this method to apply a resynchronisation. 456 * @param result The resynchro parameter, an int array organized like this: 457 * [0]:The source 1 458 * [1]:The destination 1 459 * [2]:The source 2 460 * [3]:The destination 2 461 */ 462 public void resynchro(int[] result) { 463 int source1 = result[0]; 464 int dest1 = result[1]; 465 int source2 = result[2]; 466 int dest2 = result[3]; 467 int delay1 = dest1 - source1; 468 int delay2 = dest2 - source2; 469 float scale = (float) (delay2 - delay1) / ((float) source2 - (float) source1); 470 Trace.trace("Computed scale : " + scale, Trace.ALGO_PRIORITY); 471 Iterator subtitles = subtitleList.iterator(); 472 while (subtitles.hasNext()) { 473 Subtitle subtitle = (Subtitle) subtitles.next(); 474 int localDelay = Math.round(((float) (subtitle.getStartDate() - source1) * scale) + (float) delay1); 475 subtitle.delay(localDelay); 476 } 477 //file has changed. 478 fileChanged = true; 479 } 480 481 /** 482 * Method setFile. 483 * <br><b>Summary:</b><br> 484 * Set the file to the given file. 485 * @param file The file to set. 486 */ 487 public void setFile(File file) { 488 this.file = file; 489 // file has changed. 490 fileChanged = true; 491 } 492 493 /** 494 * Method addSubtitle. 495 * <br><b>Summary:</b><br> 496 * Add a subtitle to subtitle list. 497 * It may update the given subtitle number if needed. 498 * @param subtitle The <b>Subtitle</b> to add to the file. 499 */ 500 public void addSubtitle(Subtitle subtitle) { 501 addSubtitle(subtitle, true); 502 } 503 504 /** 505 * Method addSubtitle. 506 * <br><b>Summary:</b><br> 507 * Add a subtitle to subtitle list, recomputing its sequence number. 508 * @param subtitle The <b>Subtitle</b> to add to the file. 509 * @param updateNumber A <b>boolean</b>, true if want to update the number with its index. False not to update it. 510 */ 511 public void addSubtitle(Subtitle subtitle, boolean updateNumber) { 512 subtitleList.add(subtitle); 513 if(updateNumber){ 514 subtitle.setNumber(subtitleList.size()); 515 } 516 // file has changed. 517 fileChanged = true; 518 } 519 520 /** 521 * Method split. 522 * <br><b>Summary:</b><br> 523 * This method split the subtitle file in two part at the given subtitle index. 524 * The two part will be saved in the given destination files, and a delay will be applied to the second part. 525 * @param destinationFiles The <b>File[]</b> where to save the two parts of the file. 526 * @param subtitleIndex The <b>int</b> subtitle index, from wich create the second part of the subtitle. 527 * @param secondPartDelay The <b>int</b> initial delay to apply to the second part. 528 */ 529 public SubtitleFile[] split(File[] destinationFiles, int subtitleIndex, int secondPartDelay) { 530 //The result of the method 531 SubtitleFile[] result = new SubtitleFile[2]; 532 //Construct first part SrtFile. 533 result[0] = getNewInstance(); 534 //set its file. 535 result[0].setFile(destinationFiles[0]); 536 //and second part. 537 result[1] = getNewInstance(); 538 //set its file. 539 result[1].setFile(destinationFiles[1]); 540 //Fill in the srtFiles. 541 int index = 0; 542 //by parsing current STs list, and add to one or other file. 543 for (Subtitle currentSubtitle : subtitleList) { 544 //If number is before limit, add to first part. 545 if (index < subtitleIndex) { 546 result[0].addSubtitle(new Subtitle(currentSubtitle)); 547 } else { 548 //else, add to the second part. 549 result[1].addSubtitle(new Subtitle(currentSubtitle), true); 550 } 551 index++; 552 } 553 554 //Apply delay. 555 if(secondPartDelay >= 0){ 556 //Delay second part, so first ST is at time 0 557 result[1].shiftToZero(); 558 result[1].delay(secondPartDelay); 559 } 560 //return the result; 561 return result; 562 } 563 564 /** 565 * Method shiftToZero. 566 * <br><b>Summary:</b><br> 567 * This method delays the subtitles, so the first one appears at time 0:00:00.000 568 */ 569 protected void shiftToZero() { 570 //get the first subtile time. 571 int firstTime = ((Subtitle) subtitleList.get(0)).getStartDate(); 572 //delay whole file from this delay. 573 delay(-firstTime); 574 //file has changed. 575 fileChanged = true; 576 } 577 578 /** 579 * Method getNewInstance. 580 * <br><b>Summary:</b><br> 581 * This method should return a new instance of the current SubtitleFile class. 582 * @return <b>SubtitleFile</b> A new instance of the current SubtitleFile class. 583 */ 584 protected abstract SubtitleFile getNewInstance(); 585 586 /** 587 * Method append. 588 * <br><b>Summary:</b><br> 589 * Use this method to append a Subtitle file, to the current one. 590 * Using the given delay before last ST of current one and first ST of the one to append. 591 * @param subtitleFileToAppend The <b>SubtitleFile</b> subtitle file to append. 592 * @param delay The <b>int</b> delay to use. 593 */ 594 public void append(SubtitleFile subtitleFileToAppend, int delay) { 595 //First is to shift to zero the file to append. 596 subtitleFileToAppend.shiftToZero(); 597 //Then Delay using the last ST time end date. 598 //SO first ST of the appended file, start after the last ST of this file. 599 int lastSubtitleEndDate = ((Subtitle) subtitleList.get(subtitleList.size()-1)).getEndDate(); 600 subtitleFileToAppend.delay(lastSubtitleEndDate); 601 if(delay >0){ 602 //Then delay it. 603 subtitleFileToAppend.delay(delay); 604 } 605 //Then parse all the subtitles, and add them to current subtitle file. 606 for(Subtitle currentSubtitle : subtitleFileToAppend.getSubtitles()){ 607 //add it, updating its number. 608 addSubtitle(currentSubtitle, true); 609 } 610 } 611 612 /** 613 * Method getSubtitles. 614 * <br><b>Summary:</b><br> 615 * return the subtitle list. 616 * @return <b>ArrayList</b> The subtitle list. 617 */ 618 protected ArrayList<Subtitle> getSubtitles() { 619 return subtitleList; 620 } 621 622 /** 623 * @return Returns the fileChanged. 624 */ 625 public boolean isFileChanged() { 626 return fileChanged; 627 } 628 629 630 631 /** 632 * Method <b>fileChanged</b> 633 * <br><b>Summary:</b><br> 634 * Set the fileChanged status flag to true. 635 */ 636 public void fileChanged(){ 637 fileChanged = true; 638 } 639 640 /** 641 * Method accentRepair. 642 * <br><b>Summary:</b><br> 643 * This method is called when user want to remove 644 * the accents and special characters from the given index. 645 * If no index is precised, it will remove accents from all the Subtitles. 646 * @param selectedIndex The index to remove the accents. 647 */ 648 public void accentRepair(int[] selectedIndex) { 649 Trace.trace("Accent repair.", Trace.ALGO_PRIORITY); 650 if(selectedIndex == null || selectedIndex.length == 0){ 651 //There is no index, so we will proceed on whole file. 652 for (Subtitle currentSubtitle : subtitleList) { 653 currentSubtitle.accentRemove(); 654 } 655 }else{ 656 for (int i = 0; i < selectedIndex.length; i++) { 657 Subtitle currentSubtitle = (Subtitle) subtitleList.get(selectedIndex[i]); 658 currentSubtitle.accentRemove(); 659 } 660 } 661 //file has changed. 662 fileChanged = true; 663 } 664 665 /** 666 * Method htmlRepair. 667 * <br><b>Summary:</b><br> 668 * This method is called when user want to remove 669 * the htmls. 670 * If no index is precised, it will remove html tags from all the Subtitles. 671 * @param selectedIndex The index to remove the accents. 672 */ 673 public void htmlRepair(int[] selectedIndex) { 674 Trace.trace("HTML repair.", Trace.ALGO_PRIORITY); 675 if(selectedIndex == null || selectedIndex.length == 0){ 676 //There is no index, so we will proceed on whole file. 677 for (Subtitle currentSubtitle : subtitleList) { 678 currentSubtitle.htmlRemove(); 679 } 680 }else{ 681 for (int i = 0; i < selectedIndex.length; i++) { 682 Subtitle currentSubtitle = (Subtitle) subtitleList.get(selectedIndex[i]); 683 currentSubtitle.htmlRemove(); 684 } 685 } 686 //file has changed. 687 fileChanged = true; 688 } 689 690 /** 691 * Method timeRepair. 692 * <br><b>Summary:</b><br> 693 * This method is called when user want to time repair. 694 * It will correct the time superposition problem. 695 * When subtitle ends after next ST start time. 696 */ 697 public void timeRepair() { 698 Trace.trace("Time repair.", Trace.ALGO_PRIORITY); 699 //The time that will be used to keep ends time 700 //It is initialized to 0, so frist ST will not start before 0. 701 int time =0; 702 //Just have to parse all ST. 703 for (Subtitle currentSubtitle : subtitleList) { 704 //if it starts before time, fix start time 705 if(currentSubtitle.getStartDate() < time){ 706 //Add 1 to avoid player issues. 707 currentSubtitle.setStartDate(time+1); 708 } 709 //check that start date is not after end date. 710 if(currentSubtitle.getEndDate() < currentSubtitle.getStartDate()){ 711 //If that happens, re shift end date, just after start date. 712 //St will not be visible, but player will not crash. 713 currentSubtitle.setEndDate(currentSubtitle.getStartDate()+1); 714 } 715 //Keep end date in time var. 716 time = currentSubtitle.getEndDate(); 717 } 718 //indicate the file has changed. 719 fileChanged = true; 720 } 721 722 /** 723 * Method orderRepair. 724 * <br><b>Summary:</b><br> 725 * This method is called when user want to repair the order of the subtitle file. 726 * It will check chronology, order ST's with their start time, and finally fix ST's numbers. 727 */ 728 public void orderRepair() { 729 Trace.trace("Order repair.", Trace.ALGO_PRIORITY); 730 //Just sort the list. 731 //subtitles are comparable elements, ordered with their start date. 732 Collections.sort(subtitleList); 733 //then fix ST numbers. 734 //Just have to parse all ST. 735 for (int i = 0; i < subtitleList.size(); i++) { 736 //get current subtitle. 737 Subtitle currentSubtitle = (Subtitle) subtitleList.get(i); 738 //And fix its number, add 1 because ST number starts at 1. 739 currentSubtitle.setNumber(i+1); 740 } 741 //indicate the file has changed. 742 fileChanged = true; 743 } 744 745 /** 746 * Method getSubtitleIndex. 747 * <br><b>Summary:</b><br> 748 * This method is used to know the subtitle that should be active at the given date. 749 * @param date The date (in milliseconds). 750 * @return <b>Subtitle</b> The subtitle. 751 */ 752 public Subtitle getSubtitleAtDate(int date) { 753 //The result of the method. 754 Subtitle result = null; 755 for (int i = 0; i < subtitleList.size(); i++) { 756 //get subtitle. 757 Subtitle subtitle = (Subtitle) subtitleList.get(i); 758 if ((result == null) 759 || ((subtitle.getStartDate() > (result.getStartDate())) && (subtitle.getStartDate() <= date))) { 760 result = subtitle; 761 } 762 } 763 //return the result. 764 return result; 765 } 766 767 /** 768 * Method magicResynchro. 769 * <br><b>Summary:</b><br> 770 * This method permits to perform a magic resynchro, using the defined anchors. 771 */ 772 public void magicResynchro() { 773 //First is to get the defined anchored delays. 774 ArrayList<Double> xList = new ArrayList<Double>(); 775 ArrayList<Double> yList = new ArrayList<Double>(); 776 for(Subtitle subtitle : subtitleList){ 777 if(subtitle.isAnchored()){ 778 xList.add((double) subtitle.getStartDate()); 779 yList.add((double)(subtitle.getAnchor() - subtitle.getStartDate())); 780 } 781 } 782 //Then create the linear interpolation. 783 double[] x = new double[xList.size()]; 784 double[] y = new double[yList.size()]; 785 for (int i = 0; i < x.length; i++) { 786 x[i] = xList.get(i).doubleValue(); 787 y[i] = yList.get(i).doubleValue(); 788 } 789 getLinearInterpolation(x, y); 790 //Then parse all the subtitles, and apply the delay computed by the linear interpolation. 791 for(Subtitle subtitle : subtitleList){ 792 subtitle.delay((int) linearInterpolation.interpolate(subtitle.getStartDate())); 793 } 794 //magic Interpolation done ! 795 //indicate the file has changed. 796 fileChanged = true; 797 } 798 799 private void getLinearInterpolation(double[] x, double[] y) { 800 if(linearInterpolation==null){ 801 linearInterpolation = new LinearInterpolation(x, y); 802 } 803 } 804 }