public void Can_Parse_Nonstandard_Noncompliant_CueSheet_File()
        {
            using (Stream stream = GetNonstandardNoncompliantCueSheet())
            {
                var      parser   = new CueSheetParser(stream);
                CueSheet cueSheet = parser.Parse();

                Assert.IsNotNull(cueSheet);
            }
        }
        public void Can_Parse_Standard_CueSheet_File()
        {
            using (Stream stream = GetStandardCueSheet())
            {
                var      parser   = new CueSheetParser(stream);
                CueSheet cueSheet = parser.Parse();

                Assert.IsNotNull(cueSheet);
            }
        }
        public void Can_Parse_Nonstandard_Noncompliant_CueSheet_Track_Level_Commands()
        {
            using (Stream stream = GetNonstandardNoncompliantCueSheet())
            {
                var      parser   = new CueSheetParser(stream);
                CueSheet cueSheet = parser.Parse();

                File file1 = cueSheet.Files[0];

                Assert.AreEqual(1, file1.Tracks.Count);

                Track track1 = file1.Tracks[0];

                Assert.AreEqual(1, track1.TrackNum);
                Assert.AreEqual("AUDIO", track1.TrackType);
                Assert.AreEqual("Punish My Heaven", track1.Title);
                Assert.AreEqual("Dark Tranquillity", track1.Performer);
                Assert.AreEqual("DCP", track1.Flags);

                Assert.AreEqual(1, track1.Indexes.Count);

                Index index1 = track1.Indexes[0];

                Assert.AreEqual(1, index1.IndexNum);
                Assert.AreEqual(0, index1.IndexTime.Minutes);
                Assert.AreEqual(0, index1.IndexTime.Seconds);
                Assert.AreEqual(0, index1.IndexTime.Frames);

                File file9 = cueSheet.Files[9];

                track1 = file9.Tracks[0];

                Assert.AreEqual(10, track1.TrackNum);
                Assert.AreEqual("AUDIO", track1.TrackType);
                Assert.AreEqual("Mine Is The Grandeur...", track1.Title);
                Assert.AreEqual("Dark Tranquillity", track1.Performer);
                Assert.AreEqual("DCP", track1.Flags);

                Assert.AreEqual(2, track1.Indexes.Count);

                Index index0 = track1.Indexes[0];

                Assert.AreEqual(0, index0.IndexNum);
                Assert.AreEqual(5, index0.IndexTime.Minutes);
                Assert.AreEqual(41, index0.IndexTime.Seconds);
                Assert.AreEqual(65, index0.IndexTime.Frames);

                index1 = track1.Indexes[1];

                Assert.AreEqual(1, index1.IndexNum);
                Assert.AreEqual(0, index1.IndexTime.Minutes);
                Assert.AreEqual(0, index1.IndexTime.Seconds);
                Assert.AreEqual(0, index1.IndexTime.Frames);
            }
        }
        public void Can_Parse_Standard_CueSheet_Track_Level_Commands()
        {
            using (Stream stream = GetStandardCueSheet())
            {
                var      parser   = new CueSheetParser(stream);
                CueSheet cueSheet = parser.Parse();

                File file = cueSheet.Files[0];

                Assert.AreEqual(4, file.Tracks.Count);

                Track track1 = file.Tracks[0];

                Assert.AreEqual(1, track1.TrackNum);
                Assert.AreEqual("AUDIO", track1.TrackType);
                Assert.AreEqual("Black winter day", track1.Title);
                Assert.AreEqual("AMORPHIS", track1.Performer);

                Assert.AreEqual(1, track1.Indexes.Count);

                Index index1 = track1.Indexes[0];

                Assert.AreEqual(1, index1.IndexNum);
                Assert.AreEqual(0, index1.IndexTime.Minutes);
                Assert.AreEqual(0, index1.IndexTime.Seconds);
                Assert.AreEqual(0, index1.IndexTime.Frames);

                Track track4 = file.Tracks[3];

                Assert.AreEqual(4, track4.TrackNum);
                Assert.AreEqual("AUDIO", track4.TrackType);
                Assert.AreEqual("Moon and sun Part II: North's son", track4.Title);
                Assert.AreEqual("AMORPHIS", track4.Performer);

                Assert.AreEqual(2, track4.Indexes.Count);

                Index index0 = track4.Indexes[0];

                Assert.AreEqual(0, index0.IndexNum);
                Assert.AreEqual(8, index0.IndexTime.Minutes);
                Assert.AreEqual(45, index0.IndexTime.Seconds);
                Assert.AreEqual(55, index0.IndexTime.Frames);

                index1 = track4.Indexes[1];

                Assert.AreEqual(1, index1.IndexNum);
                Assert.AreEqual(8, index1.IndexTime.Minutes);
                Assert.AreEqual(47, index1.IndexTime.Seconds);
                Assert.AreEqual(0, index1.IndexTime.Frames);
            }
        }
        public void Can_Parse_Standard_CueSheet_File_Level_Commands()
        {
            using (Stream stream = GetStandardCueSheet())
            {
                var      parser   = new CueSheetParser(stream);
                CueSheet cueSheet = parser.Parse();

                Assert.AreEqual("AMORPHIS", cueSheet.Performer);
                Assert.AreEqual("Black winter day", cueSheet.Title);

                Assert.AreEqual(1, cueSheet.Files.Count);

                Assert.AreEqual("AMORPHIS - Black winter day.flac", cueSheet.Files[0].FileName);
                Assert.AreEqual("WAVE", cueSheet.Files[0].FileType);
            }
        }
        public void Can_Parse_Nonstandard_CueSheet_Track_Level_Commands()
        {
            using (Stream stream = GetNonstandardCueSheet())
            {
                var      parser   = new CueSheetParser(stream);
                CueSheet cueSheet = parser.Parse();

                File file1 = cueSheet.Files[0];

                Assert.AreEqual(1, file1.Tracks.Count);

                Track track1 = file1.Tracks[0];

                Assert.AreEqual(1, track1.TrackNum);
                Assert.AreEqual("AUDIO", track1.TrackType);
                Assert.AreEqual("Mass Hypnosis", track1.Title);
                Assert.AreEqual("Nasum", track1.Performer);

                Assert.AreEqual(1, track1.Indexes.Count);

                Index index1 = track1.Indexes[0];

                Assert.AreEqual(1, index1.IndexNum);
                Assert.AreEqual(0, index1.IndexTime.Minutes);
                Assert.AreEqual(0, index1.IndexTime.Seconds);
                Assert.AreEqual(0, index1.IndexTime.Frames);

                File file25 = cueSheet.Files[24];

                track1 = file25.Tracks[0];

                Assert.AreEqual(25, track1.TrackNum);
                Assert.AreEqual("AUDIO", track1.TrackType);
                Assert.AreEqual("Sometimes Dead is Better", track1.Title);
                Assert.AreEqual("Nasum", track1.Performer);

                Assert.AreEqual(1, track1.Indexes.Count);

                index1 = track1.Indexes[0];

                Assert.AreEqual(1, index1.IndexNum);
                Assert.AreEqual(0, index1.IndexTime.Minutes);
                Assert.AreEqual(0, index1.IndexTime.Seconds);
                Assert.AreEqual(0, index1.IndexTime.Frames);
            }
        }
        public void Can_Parse_Nonstandard_Noncompliant_CueSheet_File_Level_Commands()
        {
            using (Stream stream = GetNonstandardNoncompliantCueSheet())
            {
                var      parser   = new CueSheetParser(stream);
                CueSheet cueSheet = parser.Parse();

                Assert.AreEqual("Dark Tranquillity", cueSheet.Performer);
                Assert.AreEqual("The Gallery", cueSheet.Title);

                Assert.AreEqual(11, cueSheet.Files.Count);

                Assert.AreEqual("01 Punish My Heaven.flac", cueSheet.Files[0].FileName);
                Assert.AreEqual("WAVE", cueSheet.Files[0].FileType);

                Assert.AreEqual("11 ...Of Melancholy Burning.flac", cueSheet.Files[10].FileName);
                Assert.AreEqual("WAVE", cueSheet.Files[10].FileType);
            }
        }
        public void Can_Parse_Nonstandard_CueSheet_File_Level_Commands()
        {
            using (Stream stream = GetNonstandardCueSheet())
            {
                var      parser   = new CueSheetParser(stream);
                CueSheet cueSheet = parser.Parse();

                Assert.AreEqual("Nasum", cueSheet.Performer);
                Assert.AreEqual("Human 2.0", cueSheet.Title);

                Assert.AreEqual(25, cueSheet.Files.Count);

                Assert.AreEqual("01 Nasum - Mass Hypnosis.flac", cueSheet.Files[0].FileName);
                Assert.AreEqual("WAVE", cueSheet.Files[0].FileType);

                Assert.AreEqual("25 Nasum - Sometimes Dead is Better.flac", cueSheet.Files[24].FileName);
                Assert.AreEqual("WAVE", cueSheet.Files[24].FileType);
            }
        }
        public void Can_Parse_Standard_CueSheet_Comment_Commands()
        {
            using (Stream stream = GetStandardCueSheet())
            {
                var      parser   = new CueSheetParser(stream);
                CueSheet cueSheet = parser.Parse();

                Assert.AreEqual(4, cueSheet.Comments.Count);

                Assert.AreEqual("GENRE", cueSheet.Comments[0].Item1);
                Assert.AreEqual("Death Metal", cueSheet.Comments[0].Item2);

                Assert.AreEqual("DATE", cueSheet.Comments[1].Item1);
                Assert.AreEqual("1994", cueSheet.Comments[1].Item2);

                Assert.AreEqual("DISCID", cueSheet.Comments[2].Item1);
                Assert.AreEqual("1F034504", cueSheet.Comments[2].Item2);

                Assert.AreEqual("COMMENT", cueSheet.Comments[3].Item1);
                Assert.AreEqual("ExactAudioCopy v1.0b3", cueSheet.Comments[3].Item2);
            }
        }
        public void Can_Parse_Nonstandard__Noncompliant_CueSheet_Comment_Commands()
        {
            using (Stream stream = GetNonstandardNoncompliantCueSheet())
            {
                var      parser   = new CueSheetParser(stream);
                CueSheet cueSheet = parser.Parse();

                Assert.AreEqual(4, cueSheet.Comments.Count);

                Assert.AreEqual("GENRE", cueSheet.Comments[0].Item1);
                Assert.AreEqual("Death Metal", cueSheet.Comments[0].Item2);

                Assert.AreEqual("DATE", cueSheet.Comments[1].Item1);
                Assert.AreEqual("1995", cueSheet.Comments[1].Item2);

                Assert.AreEqual("DISCID", cueSheet.Comments[2].Item1);
                Assert.AreEqual("870B3B0B", cueSheet.Comments[2].Item2);

                Assert.AreEqual("COMMENT", cueSheet.Comments[3].Item1);
                Assert.AreEqual("ExactAudioCopy v0.99pb4", cueSheet.Comments[3].Item2);
            }
        }
        public void Can_Parse_Nonstandard_CueSheet_Comment_Commands()
        {
            using (Stream stream = GetNonstandardCueSheet())
            {
                var      parser   = new CueSheetParser(stream);
                CueSheet cueSheet = parser.Parse();

                Assert.AreEqual(4, cueSheet.Comments.Count);

                Assert.AreEqual("GENRE", cueSheet.Comments[0].Item1);
                Assert.AreEqual("Metal", cueSheet.Comments[0].Item2);

                Assert.AreEqual("DATE", cueSheet.Comments[1].Item1);
                Assert.AreEqual("2000", cueSheet.Comments[1].Item2);

                Assert.AreEqual("DISCID", cueSheet.Comments[2].Item1);
                Assert.AreEqual("55090619", cueSheet.Comments[2].Item2);

                Assert.AreEqual("COMMENT", cueSheet.Comments[3].Item1);
                Assert.AreEqual("ExactAudioCopy v0.95b4", cueSheet.Comments[3].Item2);
            }
        }
        static void Main(string[] args)
        {
            var optionValues = new OptionValues();
            var options      = new OptionSet
            {
                { "h|help", "Show this message and exit.", s => optionValues.ShowHelp = s != null },
                {
                    "e={:}|encoder={:}",
                    new StringBuilder().AppendLine("The {0:ENCODER TYPE} and {1:QUALITY} value to use. ")
                    .AppendLine()
                    .AppendLine("Valid {0:ENCODER TYPE} values are: ")
                    .AppendLine()
                    .AppendLine("fhgaacenc")
                    .AppendLine("lame")
                    .AppendLine("nero")
                    .AppendLine("oggvorbis")
                    .AppendLine("qaac")
                    .AppendLine("qaac64")
                    .AppendLine()
                    .Append("Numeric {1:QUALITY} values are specific to each encoder ")
                    .AppendLine("(fhgaacenc uses a value between 1 and 6, qaac uses 0 to 127, etc.) ")
                    .AppendLine()
                    .AppendLine("VBR mode is always used.")
                    .AppendLine()
                    .Append("These encoders are not distributed with this program. ")
                    .Append("They must be installed separately and copied to the executable directory ")
                    .Append("or made accessible via the System PATH environment variable. ")
                    .ToString(),
                    (string encStr, decimal encQual) =>
                    {
                        optionValues.EncoderString  = encStr;
                        optionValues.EncoderQuality = encQual;
                    }
                },
                {
                    "i=|input=",
                    new StringBuilder().AppendLine("The {PATH} to the cue sheet file.")
                    .AppendLine()
                    .Append("FLAC, WavPack and Monkey's Audio* files can be split. ")
                    .Append("Decoders for these files are not distributed with this program. ")
                    .Append("They must be installed separately and copied to the executable directory ")
                    .Append("or made accessible via the System PATH environment variable. ")
                    .AppendLine()
                    .AppendLine()
                    .Append("*Ensure the FLAC decoder is installed if splitting Monkey's Audio files. ")
                    .Append("MAC.exe does not provide any splitting functionality so a transcode to FLAC is required.")
                    .ToString(),
                    s => optionValues.CueFilePath = s
                },
                { "o=|output=", "The output {PATH}.", s => optionValues.OutputPath = s },
                { "c=|cover=", "The {PATH} to a front cover image.", s => optionValues.CoverPath = s }
            };

            List <string> extra;

            try
            {
                extra = options.Parse(args);

                if (optionValues.ShowHelp)
                {
                    options.WriteOptionDescriptions(Console.Out);
                    return;
                }

                optionValues.Validate();

                var cueSheetParser = new CueSheetParser(optionValues.CueFilePath);

                CueSheet cueSheet = cueSheetParser.Parse().ToTitleCase();

                var splitterFactory = new SplitterFactory(cueSheet, optionValues.CueFilePath);
                var encoderFactory  = new EncoderFactory(optionValues.EncoderQuality);
                var taggerFactory   = new TaggerFactory(cueSheet, optionValues.CoverPath);
                var stopwatch       = new Stopwatch();

                IEncoder encoder = encoderFactory.Build(optionValues.EncoderType);
                ITagger  tagger  = taggerFactory.Build(optionValues.EncoderType);
                using (ISplitter splitter = splitterFactory.Build())
                {
                    Console.WriteLine("Starting...");
                    Console.WriteLine("Splitting {0} cue sheet into WAV files...", BuildCuesheetTypeStr(cueSheet));

                    stopwatch.Start();

                    splitter.Split();

                    DirectoryInfo encodedOutputDirInfo = Directory.CreateDirectory(Path.Combine(optionValues.OutputPath, encoder.FileType));

                    int trackCountWidth = cueSheet.IsStandard
                        ? cueSheet.Files[0].Tracks.Count.ToString().Length
                        : cueSheet.Files.Count.ToString().Length;

                    Parallel.ForEach(
                        splitter.Results,
                        trackWavPair =>
                    {
                        Track track        = trackWavPair.Track;
                        string wavFilePath = trackWavPair.FilePath;
                        string title       = track.Title.Trim();

                        Console.WriteLine(
                            "Encoding '{0}' to {1} (Thread {2})...",
                            title,
                            encoder.FileType,
                            Thread.CurrentThread.ManagedThreadId);

                        string tempEncodedFilePath = encoder.Encode(wavFilePath, track, tagger);

                        string encodedOutputPath = BuildEncodedFileOutputPath(
                            encodedOutputDirInfo.FullName,
                            title,
                            track.TrackNum,
                            trackCountWidth,
                            encoder.FileExtension);

                        IOUtils.FileMove(tempEncodedFilePath, encodedOutputPath);
                    });
                }

                Console.WriteLine("Copying original files to output directory...");

                CopyOriginalsToOutputPath(optionValues.OutputPath, optionValues.CueFilePath, cueSheet, optionValues.CoverPath);

                stopwatch.Stop();

                Console.WriteLine("Done. Time elapsed: {0}", stopwatch.Elapsed);
            }
            catch (OptionException e)
            {
                Console.WriteLine("{0} {1}", e.OptionName, e.Message);
                Console.WriteLine("Try '--help' for more information.");
            }

            if (Debugger.IsAttached)
            {
                Console.WriteLine("Click any key to exit.");
                Console.ReadKey();
            }
        }