diff --git a/CUETools.CDImage/CDImage.cs b/CUETools.CDImage/CDImage.cs index d1d6c7d..eb5e370 100644 --- a/CUETools.CDImage/CDImage.cs +++ b/CUETools.CDImage/CDImage.cs @@ -81,6 +81,7 @@ namespace CUETools.CDImage _isAudio = isAudio; _indexes = new List(); _indexes.Add(new CDTrackIndex(0, start, 0)); + _indexes.Add(new CDTrackIndex(1, start, length)); } public CDTrack(CDTrack src) @@ -209,8 +210,8 @@ namespace CUETools.CDImage public void AddIndex(CDTrackIndex index) { - if (index.Index == 0) - _indexes[0] = index; + if (index.Index < 2) + _indexes[(int)index.Index] = index; else _indexes.Add(index); } diff --git a/CUETools.Ripper.Console/CUERipper.csproj b/CUETools.Ripper.Console/CUERipper.csproj index c3da207..97254d0 100644 --- a/CUETools.Ripper.Console/CUERipper.csproj +++ b/CUETools.Ripper.Console/CUERipper.csproj @@ -97,6 +97,10 @@ {8CF07381-BEA2-4AFC-B3DD-9B2F21C65A3A} CUETools.Ripper.SCSI + + {E70FA90A-7012-4A52-86B5-362B699D1540} + FLACDotNet + {74C2036B-2C9B-4FC8-B7BD-AE81A8DCE533} MusicBrainz diff --git a/CUETools.Ripper.Console/Program.cs b/CUETools.Ripper.Console/Program.cs index 2e50a4d..37f70c8 100644 --- a/CUETools.Ripper.Console/Program.cs +++ b/CUETools.Ripper.Console/Program.cs @@ -27,6 +27,7 @@ using CUETools.Ripper.SCSI; using CUETools.Codecs; using CUETools.CDImage; using CUETools.AccurateRip; +using FLACDotNet; using MusicBrainz; namespace CUERipper @@ -67,7 +68,7 @@ namespace CUERipper audioSource.Open(driveLetter); audioSource.DriveOffset = 48; - bool toStdout = false; + //bool toStdout = false; AccurateRipVerify arVerify = new AccurateRipVerify(audioSource.TOC); int[,] buff = new int[audioSource.BestBlockSize, audioSource.ChannelCount]; string CDDBId = AccurateRipVerify.CalculateCDDBId(audioSource.TOC); @@ -89,7 +90,7 @@ namespace CUERipper } if (destFile == null || destFile == "") - destFile = (release == null) ? "cdimage.wav" : release.GetArtist() + " - " + release.GetTitle() + ".wav"; + destFile = (release == null) ? "cdimage.flac" : release.GetArtist() + " - " + release.GetTitle() + ".flac"; Console.WriteLine("Drive : {0}", audioSource.Path); Console.WriteLine("Filename : {0}", destFile); @@ -97,7 +98,8 @@ namespace CUERipper Console.WriteLine("AccurateRip : {0}", arVerify.ARStatus == null ? "ok" : arVerify.ARStatus); Console.WriteLine("MusicBrainz : {0}", release == null ? "not found" : release.GetArtist() + " - " + release.GetTitle()); - WAVWriter audioDest = new WAVWriter(destFile, audioSource.BitsPerSample, audioSource.ChannelCount, audioSource.SampleRate, toStdout ? Console.OpenStandardOutput() : null); + IAudioDest audioDest = new FLACWriter(destFile, audioSource.BitsPerSample, audioSource.ChannelCount, audioSource.SampleRate); + //IAudioDest audioDest = new WAVWriter(destFile, audioSource.BitsPerSample, audioSource.ChannelCount, audioSource.SampleRate, toStdout ? Console.OpenStandardOutput() : null); audioDest.FinalSampleCount = (long)audioSource.Length; @@ -131,7 +133,7 @@ namespace CUERipper ); audioDest.Close(); - StreamWriter logWriter = new StreamWriter(Path.ChangeExtension(destFile, ".log")); + StringWriter logWriter = new StringWriter(); logWriter.WriteLine("{0}", programVersion); logWriter.WriteLine(); logWriter.WriteLine("Extraction logfile from {0}", DateTime.Now); @@ -157,20 +159,23 @@ namespace CUERipper arVerify.GenerateFullLog(logWriter, 0); logWriter.WriteLine(); logWriter.WriteLine("End of status report"); - logWriter.Close(); + logWriter.Close(); + StreamWriter logFile = new StreamWriter(Path.ChangeExtension(destFile, ".log")); + logFile.Write(logWriter.ToString()); + logFile.Close(); - StreamWriter cueWriter = new StreamWriter(Path.ChangeExtension(destFile, ".cue")); + StringWriter cueWriter = new StringWriter(); cueWriter.WriteLine("REM DISCID {0}", CDDBId); cueWriter.WriteLine("REM ACCURATERIPID {0}", ArId); cueWriter.WriteLine("REM COMMENT \"{0}\"", programVersion); + if (release != null && release.GetEvents().Count > 0) + cueWriter.WriteLine("REM DATE {0}", release.GetEvents()[0].Date.Substring(0,4)); if (audioSource.TOC.Catalog != null) cueWriter.WriteLine("CATALOG {0}", audioSource.TOC.Catalog); if (release != null) { - if (release.GetEvents().Count > 0) - cueWriter.WriteLine("DATE {0}", release.GetEvents()[0].Date); - cueWriter.WriteLine("PERFORMER {0}", release.GetArtist()); - cueWriter.WriteLine("TITLE {0}", release.GetTitle()); + cueWriter.WriteLine("PERFORMER \"{0}\"", release.GetArtist()); + cueWriter.WriteLine("TITLE \"{0}\"", release.GetTitle()); } cueWriter.WriteLine("FILE \"{0}\" WAVE", destFile); for (int track = 1; track <= audioSource.TOC.TrackCount; track++) @@ -179,8 +184,8 @@ namespace CUERipper cueWriter.WriteLine(" TRACK {0:00} AUDIO", audioSource.TOC[track].Number); if (release != null && release.GetTracks().Count >= audioSource.TOC[track].Number) { - cueWriter.WriteLine(" TITLE {0}", release.GetTracks()[(int)audioSource.TOC[track].Number - 1].GetTitle()); - cueWriter.WriteLine(" PERFORMER {0}", release.GetTracks()[(int)audioSource.TOC[track].Number - 1].GetArtist()); + cueWriter.WriteLine(" TITLE \"{0}\"", release.GetTracks()[(int)audioSource.TOC[track].Number - 1].GetTitle()); + cueWriter.WriteLine(" PERFORMER \"{0}\"", release.GetTracks()[(int)audioSource.TOC[track].Number - 1].GetArtist()); } if (audioSource.TOC[track].ISRC != null) cueWriter.WriteLine(" ISRC {0}", audioSource.TOC[track].ISRC); @@ -188,8 +193,16 @@ namespace CUERipper cueWriter.WriteLine(" INDEX {0:00} {1}", index, audioSource.TOC[track][index].MSF); } cueWriter.Close(); + StreamWriter cueFile = new StreamWriter(Path.ChangeExtension(destFile, ".cue")); + cueFile.Write(cueWriter.ToString()); + cueFile.Close(); audioSource.Close(); + + FLACReader tagger = new FLACReader(destFile, null); + tagger.Tags.Add("CUESHEET", cueWriter.ToString()); + tagger.Tags.Add("LOG", logWriter.ToString()); + tagger.UpdateTags(false); } #if !DEBUG catch (Exception ex) diff --git a/CUETools.Ripper.SCSI/SCSIDrive.cs b/CUETools.Ripper.SCSI/SCSIDrive.cs index 6a1c56d..4b4e6cb 100644 --- a/CUETools.Ripper.SCSI/SCSIDrive.cs +++ b/CUETools.Ripper.SCSI/SCSIDrive.cs @@ -68,15 +68,15 @@ namespace CUETools.Ripper.SCSI { Device.CommandStatus st; - m_info = DeviceInfo.CreateDevice(Drive + ":"); - // Open the base device m_device = new Device(m_logger); if (!m_device.Open(Drive)) - throw new Exception("SCSI error"); + throw new Exception("Open failed: SCSI error"); + // Get device info + m_info = DeviceInfo.CreateDevice(Drive + ":"); if (!m_info.ExtractInfo(m_device)) - throw new Exception("SCSI error"); + throw new Exception("ExtractInfo failed: SCSI error"); //// Open/Initialize the driver //Drive m_drive = new Drive(dev); @@ -100,16 +100,16 @@ namespace CUETools.Ripper.SCSI SpeedDescriptorList speed_list; st = m_device.GetSpeed(out speed_list); if (st != Device.CommandStatus.Success) - throw new Exception("SCSI error"); + throw new Exception("GetSpeed failed: SCSI error"); st = m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, Device.OptimumSpeed, Device.OptimumSpeed); if (st != Device.CommandStatus.Success) - throw new Exception("SCSI error"); + throw new Exception("SetCdSpeed failed: SCSI error"); IList toc; st = m_device.ReadToc((byte)0, false, out toc); if (st != Device.CommandStatus.Success) - throw new Exception("SCSI error"); + throw new Exception("ReadToc failed: SCSI error"); st = m_device.ReadCDText(out cdtext); // new CDTextEncoderDecoder @@ -126,6 +126,8 @@ namespace CUETools.Ripper.SCSI public void Close() { + m_device.Close(); + m_device = null; _toc = null; } @@ -218,7 +220,7 @@ namespace CUETools.Ripper.SCSI { Device.CommandStatus st = m_device.ReadCDAndSubChannel(2, 1, true, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), Sectors2Read * (2352 + 16)); if (st != Device.CommandStatus.Success) - throw new Exception("SCSI error"); + throw new Exception("ReadCDAndSubChannel failed: SCSI error"); } ProcessSubchannel(sector, Sectors2Read); } @@ -226,7 +228,7 @@ namespace CUETools.Ripper.SCSI public unsafe uint Read(int[,] buff, uint sampleCount) { if (_toc == null) - throw new Exception("invalid TOC"); + throw new Exception("Read: invalid TOC"); if (_sampleOffset - _driveOffset >= (uint)Length) return 0; if (_sampleOffset > (uint)Length) @@ -287,7 +289,7 @@ namespace CUETools.Ripper.SCSI } // if (_sampleOffset < PreGapLength && !_overreadIntoPreGap ... ? int firstSector = (int)_sampleOffset / 588; - int lastSector = (int)(_sampleOffset + sampleCount + 577) / 588; + int lastSector = (int)(_sampleOffset + sampleCount + 587) / 588; for (int sector = firstSector; sector < lastSector; sector += NSECTORS) { int Sectors2Read = ((sector + NSECTORS) < lastSector) ? NSECTORS : (lastSector - sector); diff --git a/CUETools/frmBatch.cs b/CUETools/frmBatch.cs index a4eb8f7..123e36f 100644 --- a/CUETools/frmBatch.cs +++ b/CUETools/frmBatch.cs @@ -133,7 +133,7 @@ namespace JDP throw new Exception("Input CUE Sheet not found."); if (!pathIn.EndsWith(new string(Path.DirectorySeparatorChar, 1))) pathIn = pathIn + Path.DirectorySeparatorChar; - cueName = Path.GetFileNameWithoutExtension(Path.GetDirectoryName(pathIn)) + ".cue"; + cueName = Path.GetFileNameWithoutExtension(Path.GetDirectoryName(pathIn) ?? pathIn) + ".cue"; } else cueName = Path.GetFileNameWithoutExtension(pathIn) + ".cue"; @@ -145,7 +145,7 @@ namespace JDP bool pathFound = false; for (int i = 0; i < 20; i++) { - string outDir = Path.Combine(Path.GetDirectoryName(pathIn), "CUEToolsOutput" + (i > 0 ? String.Format("({0})", i) : "")); + string outDir = Path.Combine(Path.GetDirectoryName(pathIn) ?? pathIn, "CUEToolsOutput" + (i > 0 ? String.Format("({0})", i) : "")); if (!Directory.Exists(outDir)) { Directory.CreateDirectory(outDir); @@ -158,7 +158,7 @@ namespace JDP throw new Exception("Could not create a folder."); } else - pathOut = Path.Combine(Path.GetDirectoryName(pathIn), cueName); + pathOut = Path.Combine(Path.GetDirectoryName(pathIn) ?? pathIn, cueName); cueSheet.GenerateFilenames(_audioFormat, pathOut); if (outputAudio) { diff --git a/CUETools/frmCUETools.cs b/CUETools/frmCUETools.cs index a596049..4e0a59c 100644 --- a/CUETools/frmCUETools.cs +++ b/CUETools/frmCUETools.cs @@ -761,7 +761,7 @@ namespace JDP { { if (!pathIn.EndsWith(new string(Path.DirectorySeparatorChar, 1))) pathIn = pathIn + Path.DirectorySeparatorChar; - dir = Path.GetDirectoryName(pathIn); + dir = Path.GetDirectoryName(pathIn) ?? pathIn; file = Path.GetFileNameWithoutExtension(dir); } else diff --git a/CUEToolsLib/CUETools.Processor.csproj b/CUEToolsLib/CUETools.Processor.csproj index 6ad0da6..8cbb8de 100644 --- a/CUEToolsLib/CUETools.Processor.csproj +++ b/CUEToolsLib/CUETools.Processor.csproj @@ -109,6 +109,10 @@ {1DD41038-D885-46C5-8DDE-E0B82F066584} CUETools.CDImage + + {8CF07381-BEA2-4AFC-B3DD-9B2F21C65A3A} + CUETools.Ripper.SCSI + {E70FA90A-7012-4A52-86B5-362B699D1540} FLACDotNet @@ -121,6 +125,10 @@ {8A0426FA-0BC2-4C49-A6E5-1F9A68156F19} CUETools.Codecs.LossyWAV + + {74C2036B-2C9B-4FC8-B7BD-AE81A8DCE533} + MusicBrainz + {8427CAA5-80B8-4952-9A68-5F3DFCFBDF40} UnRarDotNet diff --git a/CUEToolsLib/Main.cs b/CUEToolsLib/Main.cs index 4255a37..28720c6 100644 --- a/CUEToolsLib/Main.cs +++ b/CUEToolsLib/Main.cs @@ -34,6 +34,8 @@ using CUETools.Codecs; using CUETools.Codecs.LossyWAV; using CUETools.CDImage; using CUETools.AccurateRip; +using CUETools.Ripper.SCSI; +using MusicBrainz; #if !MONO using UnRarDotNet; using FLACDotNet; @@ -421,8 +423,9 @@ namespace CUETools.Processor private const int _arOffsetRange = 5 * 588 - 1; private HDCDDotNet.HDCDDotNet hdcdDecoder; private bool _outputLossyWAV = false; - CUEConfig _config; - string _cddbDiscIdTag; + private CUEConfig _config; + private string _cddbDiscIdTag; + private bool _isCD; private bool _isArchive; private List _archiveContents; private string _archiveCUEpath; @@ -440,6 +443,7 @@ namespace CUETools.Processor _progress = new CUEToolsProgressEventArgs(); _attributes = new List(); _tracks = new List(); + _trackFilenames = new List(); _toc = new CDImageLayout(); _sources = new List(); _sourcePaths = new List(); @@ -458,39 +462,86 @@ namespace CUETools.Processor hdcdDecoder = null; _hasEmbeddedCUESheet = false; _isArchive = false; + _isCD = false; } public void Open(string pathIn, bool outputLossyWAV) { - _outputLossyWAV = outputLossyWAV; if (_config.detectHDCD) { try { hdcdDecoder = new HDCDDotNet.HDCDDotNet(2, 44100, ((_outputLossyWAV && _config.decodeHDCDtoLW16) || !_config.decodeHDCDto24bit) ? 20 : 24, _config.decodeHDCD); } catch { } } + _outputLossyWAV = outputLossyWAV; + SourceInfo sourceInfo; + string cueDir = Path.GetDirectoryName(pathIn) ?? pathIn; +#if !MONO + if (cueDir == pathIn) + { + CDDriveReader ripper = new CDDriveReader(); + ripper.Open(pathIn[0]); + // We needed to clone TOC in case we reuse the ripper, because it's going to modify it. + //_toc = (CDImageLayout)ripper.TOC.Clone(); + _toc = (CDImageLayout)ripper.TOC; + sourceInfo.Path = pathIn; + sourceInfo.Offset = 0; + sourceInfo.Length = (uint)ripper.Length; + ripper.Close(); + if (_toc.AudioTracks > 0) + { + _isCD = true; + _sources.Add(sourceInfo); + for (int iTrack = 0; iTrack < _toc.AudioTracks; iTrack++) + { + _trackFilenames.Add(string.Format("{0:00}.wav", iTrack + 1)); + _tracks.Add(new TrackInfo()); + } + _hasTrackFilenames = false; + _accurateRipId = _accurateRipIdActual = AccurateRipVerify.CalculateAccurateRipId(_toc); + _arVerify = new AccurateRipVerify(_toc); - string cueDir, lineStr, command, pathAudio = null, fileType; + Release release; + ReleaseQueryParameters p = new ReleaseQueryParameters(); + p.DiscId = _toc.MusicBrainzId; + Query results = Release.Query(p); + MusicBrainzService.XmlRequest += new EventHandler(MusicBrainz_LookupProgress); + _progress.percentDisk = 0; + try + { + release = results.First(); + General.SetCUELine(_attributes, "PERFORMER", release.GetArtist(), true); + General.SetCUELine(_attributes, "TITLE", release.GetTitle(), true); + General.SetCUELine(_attributes, "REM", "DATE", release.GetEvents()[0].Date.Substring(0, 4), false); + for (int iTrack = 0; iTrack < _toc.AudioTracks; iTrack++) + { + General.SetCUELine(_tracks[iTrack].Attributes, "TITLE", release.GetTracks()[iTrack].GetTitle(), true); + General.SetCUELine(_tracks[iTrack].Attributes, "PERFORMER ", release.GetTracks()[iTrack].GetArtist(), true); + } + } + catch + { + release = null; + } + return; + } + } +#endif + + string lineStr, command, pathAudio = null, fileType; CUELine line; - TrackInfo trackInfo; - int timeRelativeToFileStart, absoluteFileStartTime; - int fileTimeLengthSamples, fileTimeLengthFrames, i; + TrackInfo trackInfo = null; + int timeRelativeToFileStart, absoluteFileStartTime = 0; + int fileTimeLengthSamples = 0, fileTimeLengthFrames = 0, i; int trackNumber = 0; bool seenFirstFileIndex = false, seenDataTrack = false; List indexes = new List(); IndexInfo indexInfo; - SourceInfo sourceInfo; NameValueCollection _trackTags = null; - - cueDir = Path.GetDirectoryName(pathIn); - trackInfo = null; - absoluteFileStartTime = 0; - fileTimeLengthSamples = 0; - fileTimeLengthFrames = 0; TextReader sr; if (Directory.Exists(pathIn)) { - if (cueDir + Path.DirectorySeparatorChar != pathIn) + if (cueDir + Path.DirectorySeparatorChar != pathIn && cueDir != pathIn) throw new Exception("Input directory must end on path separator character."); string cueSheet = null; string[] audioExts = new string[] { "*.wav", "*.flac", "*.wv", "*.ape", "*.m4a" }; @@ -768,7 +819,6 @@ namespace CUETools.Processor _htoaFilename = _hasHTOAFilename ? Path.GetFileName(_sourcePaths[0]) : "01.00.wav"; _hasTrackFilenames = (_sourcePaths.Count == TrackCount) || _hasHTOAFilename; - _trackFilenames = new List(); for (i = 0; i < TrackCount; i++) { _trackFilenames.Add( _hasTrackFilenames ? Path.GetFileName( _sourcePaths[i + (_hasHTOAFilename ? 1 : 0)]) : String.Format("{0:00}.wav", i + 1) ); @@ -892,6 +942,18 @@ namespace CUETools.Processor } #if !MONO + private void MusicBrainz_LookupProgress(object sender, XmlRequestEventArgs e) + { + if (this.CUEToolsProgress == null) + return; + _progress.percentDisk = (1.0 + _progress.percentDisk) / 2; + _progress.percentTrack = 0; + _progress.input = e.Uri.ToString(); + _progress.output = null; + _progress.status = "Looking up album via MusicBrainz"; + this.CUEToolsProgress(this, _progress); + } + private void unrar_ExtractionProgress(object sender, ExtractionProgressEventArgs e) { if (this.CUEToolsProgress == null) @@ -1210,14 +1272,7 @@ namespace CUETools.Processor } private void CalculateMusicBrainzDiscID() { - StringBuilder mbSB = new StringBuilder(); - mbSB.AppendFormat("{0:X2}{1:X2}{2:X8}", 1, TrackCount, _toc.Length + 150); - for (int iTrack = 1; iTrack <= _toc.TrackCount; iTrack++) - mbSB.AppendFormat("{0:X8}", _toc[iTrack].Start + 150); - mbSB.Append(new string('0', (99 - TrackCount) * 8)); - - byte[] hashBytes = (new SHA1CryptoServiceProvider()).ComputeHash(Encoding.ASCII.GetBytes(mbSB.ToString())); - _mbDiscId = Convert.ToBase64String(hashBytes).Replace('+', '.').Replace('/', '_').Replace('=', '-'); + _mbDiscId = _toc.MusicBrainzId; System.Diagnostics.Debug.WriteLine(_mbDiscId); } @@ -1834,7 +1889,8 @@ namespace CUETools.Processor uint currentOffset = 0, previousOffset = 0; uint trackLength = _toc.Pregap * 588; - uint diskLength = _toc.Length * 588, diskOffset = 0; + uint diskLength = 588 * (_toc[_toc.TrackCount].IsAudio ? _toc[_toc.TrackCount].End + 1 : _toc[_toc.TrackCount - 1].End + 1); + uint diskOffset = 0; if (_accurateRip && noOutput) _arVerify.Init(); @@ -1908,11 +1964,12 @@ namespace CUETools.Processor if (trackPercent != lastTrackPercent) ShowProgress(String.Format("{2} track {0:00} ({1:00}%)...", iIndex > 0 ? iTrack + 1 : iTrack, trackPercent, noOutput ? "Verifying" : "Writing"), trackPercent, diskPercent, - audioSource.Path, discardOutput ? null : audioDest.Path); + _isCD ? audioSource.Path + ": " + _tracks[iTrack].Title : audioSource.Path, discardOutput ? null : audioDest.Path); lastTrackPercent = trackPercent; } - audioSource.Read(sampleBuffer, copyCount); + if (audioSource.Read(sampleBuffer, copyCount) != copyCount) + throw new Exception("samples read != samples expected"); if (!discardOutput) { if (!_config.detectHDCD || !_config.decodeHDCD) @@ -2159,6 +2216,13 @@ namespace CUETools.Processor } else { #if !MONO + if (_isCD) + { + CDDriveReader ripper = new CDDriveReader(); + ripper.Open(sourceInfo.Path[0]); + ripper.DriveOffset = 48; + audioSource = ripper; + } else if (_isArchive) { RarStream IO = new RarStream(_archivePath, sourceInfo.Path); diff --git a/LossyWAVSharp/LossyWAVSharp.csproj b/LossyWAVSharp/LossyWAVSharp.csproj index f8e436d..b4b14e2 100644 --- a/LossyWAVSharp/LossyWAVSharp.csproj +++ b/LossyWAVSharp/LossyWAVSharp.csproj @@ -81,11 +81,11 @@ - + {6458A13A-30EF-45A9-9D58-E5031B17BEE2} AudioCodecsDotNet - + {8A0426FA-0BC2-4C49-A6E5-1F9A68156F19} LossyWAVDotNet diff --git a/MAC_SDK/Source/MACLib/Assembly/Assembly.obj b/MAC_SDK/Source/MACLib/Assembly/Assembly.obj index 611066e..e718d81 100644 Binary files a/MAC_SDK/Source/MACLib/Assembly/Assembly.obj and b/MAC_SDK/Source/MACLib/Assembly/Assembly.obj differ