public static MergedBin MergeBins(string file, IEnumerable <CueFile> cueFiles, string tempPath) { var mergedBin = new MergedBin(); mergedBin.CueFiles = new List <CueFile>(); Console.WriteLine($"Merging .bins..."); long currentFrame = 0; var mergedFilename = Path.GetFileNameWithoutExtension(file) + " - MERGED.bin"; mergedBin.Path = Path.Combine(tempPath, mergedFilename); var mcueFile = new CueFile() { FileName = mergedFilename, FileType = "BINARY", Tracks = new List <CueTrack>() }; mergedBin.CueFiles.Add(mcueFile); using (var joinedFile = new FileStream(mergedBin.Path, FileMode.Create)) { foreach (var cueFile in cueFiles) { using (var srcStream = new FileStream(Path.Combine(tempPath, cueFile.FileName), FileMode.Open)) { srcStream.CopyTo(joinedFile); foreach (var item in cueFile.Tracks) { var indexes = new List <CueIndex>(); foreach (var idx in item.Indexes) { var newIndex = new CueIndex(); newIndex.Number = idx.Number; newIndex.Position = idx.Position + Helper.PositionFromFrames(currentFrame); indexes.Add(newIndex); } var newTrack = new CueTrack() { DataType = item.DataType, Number = item.Number, Indexes = indexes }; mcueFile.Tracks.Add(newTrack); } var frames = srcStream.Length / 2352; currentFrame += frames; } } } return(mergedBin); }
public static CueFile TOCtoCUE(List <TOCEntry> tocEntries, string dataPath) { var cueFile = new CueFileEntry() { FileName = dataPath, Tracks = new List <CueTrack>(), FileType = "BINARY" }; var audioLeadin = new IndexPosition { Seconds = 2 }; foreach (var track in tocEntries) { var position = new IndexPosition { Minutes = track.Minutes, Seconds = track.Seconds, Frames = track.Frames, }; var indexes = new List <CueIndex>(); if (track.TrackType == TrackTypeEnum.Audio) { indexes.Add(new CueIndex() { Number = 0, Position = position - audioLeadin, }); } indexes.Add(new CueIndex() { Number = 1, Position = position, }); var cueTrack = new CueTrack() { DataType = GetDataType(track.TrackType), Indexes = indexes, Number = track.TrackNo }; cueFile.Tracks.Add(cueTrack); } return(new CueFile(new[] { cueFile })); }
string crushedTrackPath; // Autocalculated // -- public TaskRestoreTrack(CueTrack tr) { name = "Restore"; desc = string.Format("Restoring track {0}", tr.trackNo); track = tr; }// -----------------------------------------
static MergedBin MergeBins(string file, CueFile cueFilex, string tempPath) { var mergedBin = new MergedBin(); mergedBin.CueFile = new CueFile(); var cueFilePath = Path.GetDirectoryName(file); long currentFrame = 0; var mergedFilename = Path.GetFileNameWithoutExtension(file) + " - MERGED.bin"; mergedBin.Path = Path.Combine(tempPath, mergedFilename); var mcueFile = new CueFileEntry() { FileName = mergedFilename, FileType = "BINARY", Tracks = new List <CueTrack>() }; mergedBin.CueFile.FileEntries.Add(mcueFile); using (var joinedFile = new FileStream(mergedBin.Path, FileMode.Create)) { foreach (var cueFileEntry in cueFilex.FileEntries) { var binPath = cueFileEntry.FileName; if (Path.GetDirectoryName(binPath) == "" || Path.GetDirectoryName(binPath).StartsWith("..") || Path.GetDirectoryName(binPath).StartsWith(".")) { binPath = Path.Combine(cueFilePath, cueFileEntry.FileName); } using (var srcStream = new FileStream(binPath, FileMode.Open)) { srcStream.CopyTo(joinedFile); foreach (var item in cueFileEntry.Tracks) { var indexes = new List <CueIndex>(); foreach (var idx in item.Indexes) { var newIndex = new CueIndex { Number = idx.Number, Position = idx.Position + TOCHelper.PositionFromFrames(currentFrame) }; indexes.Add(newIndex); } var newTrack = new CueTrack() { DataType = item.DataType, Number = item.Number, Indexes = indexes }; mcueFile.Tracks.Add(newTrack); } var frames = srcStream.Length / 2352; currentFrame += frames; } } } return(mergedBin); }
public void LoadFromString(string cueString) { TextReader tr = new StringReader(cueString); bool track_has_pregap = false; bool track_has_postgap = false; int last_index_num = -1; CueFile currFile = null; CueTrack currTrack = null; for (; ; ) { string line = tr.ReadLine(); if (line == null) break; line = line.Trim(); if (line == "") continue; var clp = new CueLineParser(line); string key = clp.ReadToken().ToUpper(); switch (key) { case "REM": break; case "FILE": { currTrack = null; currFile = new CueFile(); Files.Add(currFile); currFile.Path = clp.ReadPath().Trim('"'); if (!clp.EOF) { string temp = clp.ReadToken().ToUpper(); switch (temp) { case "BINARY": currFile.FileType = CueFileType.Binary; break; case "WAVE": case "MP3": currFile.FileType = CueFileType.Wave; break; } currFile.StrFileType = temp; } break; } case "TRACK": { if (currFile == null) throw new CueBrokenException("invalid cue structure"); if (clp.EOF) throw new CueBrokenException("invalid cue structure"); string strtracknum = clp.ReadToken(); int tracknum; if (!int.TryParse(strtracknum, out tracknum)) throw new CueBrokenException("malformed track number"); if (clp.EOF) throw new CueBrokenException("invalid cue structure"); if (tracknum < 0 || tracknum > 99) throw new CueBrokenException("`All track numbers must be between 1 and 99 inclusive.`"); string strtracktype = clp.ReadToken().ToUpper(); currTrack = new CueTrack(); switch (strtracktype) { case "MODE1/2352": currTrack.TrackType = ETrackType.Mode1_2352; break; case "MODE1/2048": currTrack.TrackType = ETrackType.Mode1_2048; break; case "MODE2/2352": currTrack.TrackType = ETrackType.Mode2_2352; break; case "AUDIO": currTrack.TrackType = ETrackType.Audio; break; default: throw new CueBrokenException("unhandled track type"); } currTrack.TrackNum = tracknum; currFile.Tracks.Add(currTrack); track_has_pregap = false; track_has_postgap = false; last_index_num = -1; break; } case "INDEX": { if (currTrack == null) throw new CueBrokenException("invalid cue structure"); if (clp.EOF) throw new CueBrokenException("invalid cue structure"); if (track_has_postgap) throw new CueBrokenException("`The POSTGAP command must appear after all INDEX commands for the current track.`"); string strindexnum = clp.ReadToken(); int indexnum; if (!int.TryParse(strindexnum, out indexnum)) throw new CueBrokenException("malformed index number"); if (clp.EOF) throw new CueBrokenException("invalid cue structure (missing index timestamp)"); string str_timestamp = clp.ReadToken(); if (indexnum < 0 || indexnum > 99) throw new CueBrokenException("`All index numbers must be between 0 and 99 inclusive.`"); if (indexnum != 1 && indexnum != last_index_num + 1) throw new CueBrokenException("`The first index must be 0 or 1 with all other indexes being sequential to the first one.`"); last_index_num = indexnum; CueTrackIndex cti = new CueTrackIndex(indexnum) { Timestamp = new Timestamp(str_timestamp), IndexNum = indexnum }; currTrack.Indexes[indexnum] = cti; break; } case "PREGAP": if (track_has_pregap) throw new CueBrokenException("`Only one PREGAP command is allowed per track.`"); if (currTrack.Indexes.Count > 0) throw new CueBrokenException("`The PREGAP command must appear after a TRACK command, but before any INDEX commands.`"); currTrack.PreGap = new Timestamp(clp.ReadToken()); track_has_pregap = true; break; case "POSTGAP": if (track_has_postgap) throw new CueBrokenException("`Only one POSTGAP command is allowed per track.`"); track_has_postgap = true; currTrack.PostGap = new Timestamp(clp.ReadToken()); break; case "CATALOG": case "PERFORMER": case "SONGWRITER": case "TITLE": case "ISRC": //TODO - keep these for later? break; default: throw new CueBrokenException("unsupported cue command: " + key); } } //end cue parsing loop }
public void LoadFromString(string cueString) { TextReader tr = new StringReader(cueString); bool track_has_pregap = false; bool track_has_postgap = false; int last_index_num = -1; CueFile currFile = null; CueTrack currTrack = null; for (; ;) { string line = tr.ReadLine(); if (line == null) { break; } line = line.Trim(); if (line == "") { continue; } var clp = new CueLineParser(line); string key = clp.ReadToken().ToUpper(); switch (key) { case "REM": break; case "FILE": { currTrack = null; currFile = new CueFile(); Files.Add(currFile); currFile.Path = clp.ReadPath().Trim('"'); if (!clp.EOF) { string temp = clp.ReadToken().ToUpper(); switch (temp) { case "BINARY": currFile.FileType = CueFileType.Binary; break; case "WAVE": case "MP3": currFile.FileType = CueFileType.Wave; break; } currFile.StrFileType = temp; } break; } case "TRACK": { if (currFile == null) { throw new CueBrokenException("invalid cue structure"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure"); } string strtracknum = clp.ReadToken(); int tracknum; if (!int.TryParse(strtracknum, out tracknum)) { throw new CueBrokenException("malformed track number"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure"); } if (tracknum < 0 || tracknum > 99) { throw new CueBrokenException("`All track numbers must be between 1 and 99 inclusive.`"); } string strtracktype = clp.ReadToken().ToUpper(); currTrack = new CueTrack(); switch (strtracktype) { case "MODE1/2352": currTrack.TrackType = ETrackType.Mode1_2352; break; case "MODE1/2048": currTrack.TrackType = ETrackType.Mode1_2048; break; case "MODE2/2352": currTrack.TrackType = ETrackType.Mode2_2352; break; case "AUDIO": currTrack.TrackType = ETrackType.Audio; break; default: throw new CueBrokenException("unhandled track type"); } currTrack.TrackNum = tracknum; currFile.Tracks.Add(currTrack); track_has_pregap = false; track_has_postgap = false; last_index_num = -1; break; } case "INDEX": { if (currTrack == null) { throw new CueBrokenException("invalid cue structure"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure"); } if (track_has_postgap) { throw new CueBrokenException("`The POSTGAP command must appear after all INDEX commands for the current track.`"); } string strindexnum = clp.ReadToken(); int indexnum; if (!int.TryParse(strindexnum, out indexnum)) { throw new CueBrokenException("malformed index number"); } if (clp.EOF) { throw new CueBrokenException("invalid cue structure (missing index timestamp)"); } string str_timestamp = clp.ReadToken(); if (indexnum < 0 || indexnum > 99) { throw new CueBrokenException("`All index numbers must be between 0 and 99 inclusive.`"); } if (indexnum != 1 && indexnum != last_index_num + 1) { throw new CueBrokenException("`The first index must be 0 or 1 with all other indexes being sequential to the first one.`"); } last_index_num = indexnum; CueTrackIndex cti = new CueTrackIndex(indexnum) { Timestamp = new Timestamp(str_timestamp), IndexNum = indexnum }; currTrack.Indexes[indexnum] = cti; break; } case "PREGAP": if (track_has_pregap) { throw new CueBrokenException("`Only one PREGAP command is allowed per track.`"); } if (currTrack.Indexes.Count > 0) { throw new CueBrokenException("`The PREGAP command must appear after a TRACK command, but before any INDEX commands.`"); } currTrack.PreGap = new Timestamp(clp.ReadToken()); track_has_pregap = true; break; case "POSTGAP": if (track_has_postgap) { throw new CueBrokenException("`Only one POSTGAP command is allowed per track.`"); } track_has_postgap = true; currTrack.PostGap = new Timestamp(clp.ReadToken()); break; case "CATALOG": case "PERFORMER": case "SONGWRITER": case "TITLE": case "ISRC": //TODO - keep these for later? break; default: throw new CueBrokenException("unsupported cue command: " + key); } } //end cue parsing loop }
string sourceTrackFile; // Temp name, Autocalculated // -- public TaskCompressTrack(CueTrack tr) : base(null, "Encoding") { name = "Compress"; desc = string.Format("Encoding track {0}", tr.trackNo); track = tr; }// -----------------------------------------
public void ExtractMKVSegments(String argMKVFile, List <gMKVSegment> argMKVSegmentsToExtract, String argOutputDirectory, MkvChapterTypes argChapterType, TimecodesExtractionMode argTimecodesExtractionMode, CuesExtractionMode argCueExtractionMode) { _Abort = false; _AbortAll = false; _ErrorBuilder.Length = 0; _MKVExtractOutput.Length = 0; // Analyze the MKV segments and get the initial parameters List <TrackParameter> initialParameters = new List <TrackParameter>(); foreach (gMKVSegment seg in argMKVSegmentsToExtract) { if (_AbortAll) { _ErrorBuilder.AppendLine("User aborted all the processes!"); break; } try { initialParameters.AddRange(GetTrackParameters(seg, argMKVFile, argOutputDirectory, argChapterType, argTimecodesExtractionMode, argCueExtractionMode)); } catch (Exception ex) { Debug.WriteLine(ex); _ErrorBuilder.AppendLine(String.Format("Segment: {0}\r\nException: {1}\r\n", seg, ex.Message)); } } // Group the initial parameters, in order to batch extract the mkv segments List <TrackParameter> finalParameters = new List <TrackParameter>(); foreach (TrackParameter initPar in initialParameters) { TrackParameter currentPar = null; foreach (TrackParameter finalPar in finalParameters) { if (finalPar.ExtractMode == initPar.ExtractMode) { currentPar = finalPar; break; } } if (currentPar != null) { currentPar.TrackOutput = String.Format("{0} {1}", currentPar.TrackOutput, initPar.TrackOutput); } else { finalParameters.Add(initPar); } } // Time to extract the mkv segments foreach (TrackParameter finalPar in finalParameters) { if (_AbortAll) { _ErrorBuilder.AppendLine("User aborted all the processes!"); break; } try { if (finalPar.WriteOutputToFile) { _OutputFileWriter = new StreamWriter(finalPar.OutputFilename, false, new UTF8Encoding(false, true)); } OnMkvExtractTrackUpdated(Enum.GetName(finalPar.ExtractMode.GetType(), finalPar.ExtractMode)); ExtractMkvSegment(argMKVFile, String.Format("{0} {1} \"{2}\" {3}", Enum.GetName(finalPar.ExtractMode.GetType(), finalPar.ExtractMode), finalPar.Options, argMKVFile, finalPar.TrackOutput), finalPar.WriteOutputToFile); } catch (Exception ex) { Debug.WriteLine(ex); _ErrorBuilder.AppendLine(String.Format("Track output: {0}\r\nException: {1}\r\n", finalPar.TrackOutput, ex.Message)); } finally { if (finalPar.WriteOutputToFile) { _OutputFileWriter.Close(); _OutputFileWriter = null; try { // If we have chapters with CUE format, then we read the XML chapters and convert it to CUE if (finalPar.ExtractMode == MkvExtractModes.chapters) { if (finalPar.OutputFilename.EndsWith("cue")) { Chapters c = null; using (StreamReader sr = new StreamReader(finalPar.OutputFilename)) { XmlSerializer serializer = new XmlSerializer(typeof(Chapters)); c = (Chapters)serializer.Deserialize(sr); } Cue cue = new Cue(); cue.File = Path.GetFileName(argMKVFile); cue.FileType = "WAVE"; cue.Title = Path.GetFileName(argMKVFile); cue.Tracks = new List <CueTrack>(); if (c.EditionEntry != null && c.EditionEntry.Length > 0 && c.EditionEntry[0].ChapterAtom != null && c.EditionEntry[0].ChapterAtom.Length > 0) { Int32 currentChapterTrackNumber = 1; foreach (ChapterAtom atom in c.EditionEntry[0].ChapterAtom) { CueTrack tr = new CueTrack(); tr.Number = currentChapterTrackNumber; if (atom.ChapterDisplay != null && atom.ChapterDisplay.Length > 0) { tr.Title = atom.ChapterDisplay[0].ChapterString; } if (!String.IsNullOrEmpty(atom.ChapterTimeStart) && atom.ChapterTimeStart.Contains(":")) { String[] timeElements = atom.ChapterTimeStart.Split(new String[] { ":" }, StringSplitOptions.None); if (timeElements.Length == 3) { // Find cue minutes from hours and minutes Int32 hours = Int32.Parse(timeElements[0]); Int32 minutes = Int32.Parse(timeElements[1]) + 60 * hours; // Convert nanoseconds to frames (each second is 75 frames) Int64 nanoSeconds = 0; Int32 frames = 0; Int32 secondsLength = timeElements[2].Length; if (timeElements[2].Contains(".")) { secondsLength = timeElements[2].IndexOf("."); nanoSeconds = Int64.Parse(timeElements[2].Substring(timeElements[2].IndexOf(".") + 1)); // I take the integer part of the result action in order to get the first frame frames = Convert.ToInt32(Math.Floor(Convert.ToDouble(nanoSeconds) / 1000000000.0 * 75.0)); } tr.Index = String.Format("{0}:{1}:{2}", minutes.ToString("#00") , timeElements[2].Substring(0, secondsLength) , frames.ToString("00") ); } } cue.Tracks.Add(tr); currentChapterTrackNumber++; } } StringBuilder cueBuilder = new StringBuilder(); cueBuilder.AppendFormat("REM GENRE \"\"\r\n"); cueBuilder.AppendFormat("REM DATE \"\"\r\n"); cueBuilder.AppendFormat("PERFORMER \"\"\r\n"); cueBuilder.AppendFormat("TITLE \"{0}\"\r\n", cue.Title); cueBuilder.AppendFormat("FILE \"{0}\" {1}\r\n", cue.File, cue.FileType); foreach (CueTrack tr in cue.Tracks) { cueBuilder.AppendFormat("\tTRACK {0} AUDIO\r\n", tr.Number.ToString("00")); cueBuilder.AppendFormat("\t\tTITLE \"{0}\"\r\n", tr.Title); cueBuilder.AppendFormat("\t\tPERFORMER \"\"\r\n"); cueBuilder.AppendFormat("\t\tINDEX 01 {0}\r\n", tr.Index); } using (StreamWriter sw = new StreamWriter(finalPar.OutputFilename, false, Encoding.UTF8)) { sw.Write(cueBuilder.ToString()); } } } } catch (Exception exc) { Debug.WriteLine(exc); _ErrorBuilder.AppendLine(String.Format("Track output: {0}\r\nException: {1}\r\n", finalPar.TrackOutput, exc.Message)); } } } } // check for errors if (_ErrorBuilder.Length > 0) { throw new Exception(_ErrorBuilder.ToString()); } }