More work on CUERipper

This commit is contained in:
chudov
2008-12-06 05:44:14 +00:00
parent a026df67a9
commit 764f9f2368
9 changed files with 635 additions and 185 deletions

View File

@@ -29,7 +29,7 @@ using System.Diagnostics;
namespace Bwg.Scsi namespace Bwg.Scsi
{ {
class Command : IDisposable public class Command : IDisposable
{ {
[DllImport("ntdll.dll")] [DllImport("ntdll.dll")]
internal static extern void RtlZeroMemory(IntPtr dest, int size); internal static extern void RtlZeroMemory(IntPtr dest, int size);

View File

@@ -23,7 +23,7 @@
namespace Bwg.Scsi namespace Bwg.Scsi
{ {
enum ScsiCommandCode public enum ScsiCommandCode
{ {
TestUnitReady = 0x00, TestUnitReady = 0x00,
RequestSense = 0x03, RequestSense = 0x03,

View File

@@ -939,7 +939,7 @@ namespace Bwg.Scsi
} }
#endregion #endregion
private CommandStatus SendCommand(Command cmd) public CommandStatus SendCommand(Command cmd)
{ {
return (m_ossize == 32) ? SendCommand32(cmd) : SendCommand64(cmd); return (m_ossize == 32) ? SendCommand32(cmd) : SendCommand64(cmd);
} }
@@ -1971,7 +1971,7 @@ namespace Bwg.Scsi
/// <param name="data">The buffer to receive the data</param> /// <param name="data">The buffer to receive the data</param>
/// <param name="size">The size of the buffer given by the data parameter</param> /// <param name="size">The size of the buffer given by the data parameter</param>
/// <returns></returns> /// <returns></returns>
public CommandStatus Read(bool force, bool streaming, uint lba, uint length, IntPtr data, ushort size) public CommandStatus Read(bool force, bool streaming, uint lba, uint length, IntPtr data, int size)
{ {
if (m_logger != null) if (m_logger != null)
{ {
@@ -2134,15 +2134,16 @@ namespace Bwg.Scsi
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="mode">subchannel mode</param> /// <param name="mode">subchannel mode (+16 bytes if equals 2)</param>
/// <param name="exp"></param> /// <param name="c2">report C2 errors (+296 bytes if true)</param>
/// <param name="exp">expected sector type</param>
/// <param name="dap"></param> /// <param name="dap"></param>
/// <param name="start"></param> /// <param name="start"></param>
/// <param name="length"></param> /// <param name="length"></param>
/// <param name="data">the memory area </param> /// <param name="data">the memory area </param>
/// <param name="size">the size of the memory area given by the data parameter</param> /// <param name="size">the size of the memory area given by the data parameter</param>
/// <returns></returns> /// <returns></returns>
public CommandStatus ReadCDAndSubChannel(byte mode, byte exp, bool dap, uint start, uint length, IntPtr data, int size) public CommandStatus ReadCDAndSubChannel(byte mode, bool c2, byte exp, bool dap, uint start, uint length, IntPtr data, int size)
{ {
if (m_logger != null) if (m_logger != null)
{ {
@@ -2161,10 +2162,13 @@ namespace Bwg.Scsi
byte b = (byte)((exp & 0x07) << 2); byte b = (byte)((exp & 0x07) << 2);
if (dap) if (dap)
b |= 0x02; b |= 0x02;
byte byte9 = 0x10;
if (c2)
byte9 |= 0x02;
cmd.SetCDB8(1, b); cmd.SetCDB8(1, b);
cmd.SetCDB32(2, start); cmd.SetCDB32(2, start);
cmd.SetCDB24(6, length); cmd.SetCDB24(6, length);
cmd.SetCDB8(9, 0x10); // User data only cmd.SetCDB8(9, byte9); // User data + possibly c2 errors
cmd.SetCDB8(10, mode); // Subchannel cmd.SetCDB8(10, mode); // Subchannel
CommandStatus st = SendCommand(cmd); CommandStatus st = SendCommand(cmd);

View File

@@ -52,12 +52,13 @@ namespace CUETools.CDImage
public class CDTrack : ICloneable public class CDTrack : ICloneable
{ {
public CDTrack(uint number, uint start, uint length, bool isAudio) public CDTrack(uint number, uint start, uint length, bool isAudio, bool preEmpasis)
{ {
_number = number; _number = number;
_start = start; _start = start;
_length = length; _length = length;
_isAudio = isAudio; _isAudio = isAudio;
_preEmphasis = preEmpasis;
_indexes = new List<CDTrackIndex>(); _indexes = new List<CDTrackIndex>();
_indexes.Add(new CDTrackIndex(0, start)); _indexes.Add(new CDTrackIndex(0, start));
_indexes.Add(new CDTrackIndex(1, start)); _indexes.Add(new CDTrackIndex(1, start));
@@ -69,6 +70,7 @@ namespace CUETools.CDImage
_start = src._start; _start = src._start;
_length = src._length; _length = src._length;
_isAudio = src._isAudio; _isAudio = src._isAudio;
_preEmphasis = src._preEmphasis;
_indexes = new List<CDTrackIndex>(); _indexes = new List<CDTrackIndex>();
for (int i = 0; i < src._indexes.Count; i++) for (int i = 0; i < src._indexes.Count; i++)
_indexes.Add(new CDTrackIndex(src._indexes[i])); _indexes.Add(new CDTrackIndex(src._indexes[i]));
@@ -187,6 +189,18 @@ namespace CUETools.CDImage
} }
} }
public bool PreEmphasis
{
get
{
return _preEmphasis;
}
set
{
_preEmphasis = value;
}
}
public void AddIndex(CDTrackIndex index) public void AddIndex(CDTrackIndex index)
{ {
if (index.Index < 2) if (index.Index < 2)
@@ -198,6 +212,7 @@ namespace CUETools.CDImage
IList<CDTrackIndex> _indexes; IList<CDTrackIndex> _indexes;
string _isrc; string _isrc;
bool _isAudio; bool _isAudio;
bool _preEmphasis;
uint _start; uint _start;
uint _length; uint _length;
uint _number; uint _number;

View File

@@ -27,13 +27,60 @@ using CUETools.Ripper.SCSI;
using CUETools.Codecs; using CUETools.Codecs;
using CUETools.CDImage; using CUETools.CDImage;
using CUETools.AccurateRip; using CUETools.AccurateRip;
using FLACDotNet; //using FLACDotNet;
using MusicBrainz; using MusicBrainz;
namespace CUERipper namespace CUERipper
{ {
class ProgressMeter
{
public DateTime realStart;
public ProgressMeter()
{
realStart = DateTime.Now;
//TimeSpan lastPrint = TimeSpan.FromMilliseconds(0);
}
public void ReadProgress(object sender, ReadProgressArgs e)
{
CDDriveReader audioSource = (CDDriveReader)sender;
int processed = e.Position - e.PassStart;
TimeSpan elapsed = DateTime.Now - e.PassTime;
double speed = elapsed.TotalSeconds > 0 ? processed / elapsed.TotalSeconds / 75 : 1.0;
TimeSpan totalElapsed = DateTime.Now - realStart;
TimeSpan totalEstimated = TimeSpan.FromMilliseconds(totalElapsed.TotalMilliseconds / Math.Max(1, (e.PassStart + (processed + e.Pass * (e.PassEnd - e.PassStart)) / 16)) * audioSource.TOC.AudioLength);
// if ((elapsed - lastPrint).TotalMilliseconds > 60) ;
Console.Write("\r{9} : {0:00}%; {1:00.00}x; {2} ({10:0.00}%) errors; {3:d2}:{4:d2}:{5:d2}/{6:d2}:{7:d2}:{8:d2} ",
100.0 * e.Position / audioSource.TOC.AudioLength,
speed,
e.ErrorsCount,
totalElapsed.Hours, totalElapsed.Minutes, totalElapsed.Seconds,
totalEstimated.Hours, totalEstimated.Minutes, totalEstimated.Seconds,
e.Pass < 1 ? "Progress " : string.Format("Retry {0:00} ", e.Pass),
processed > 0 ? 100.0 * e.ErrorsCount / processed / (4 * 588) : 0
);
//lastPrint = elapsed;
}
}
class Program class Program
{ {
static void Usage()
{
string drives = "";
char[] drivesAvailable = CDDriveReader.DrivesAvailable();
for (int i = 0; i < drivesAvailable.Length; i++)
drives += string.Format("{0}: ", drivesAvailable[i]);
Console.WriteLine("Usage : CUERipper.exe <options>");
Console.WriteLine();
//Console.WriteLine("-S, --secure secure mode, read each block twice;");
Console.WriteLine("-P, --paranoid maximum level of error correction;");
Console.WriteLine("-D, --drive <letter> use a specific CD drive, e.g. {0};", drives);
Console.WriteLine("-O, --offset <samples> use specific drive read offset;");
}
static void Main(string[] args) static void Main(string[] args)
{ {
string programVersion = "CUERipper v1.9.3 Copyright (C) 2008 Gregory S. Chudov"; string programVersion = "CUERipper v1.9.3 Copyright (C) 2008 Gregory S. Chudov";
@@ -42,25 +89,65 @@ namespace CUERipper
Console.WriteLine("This is free software under the GNU GPLv3+ license; There is NO WARRANTY, to"); Console.WriteLine("This is free software under the GNU GPLv3+ license; There is NO WARRANTY, to");
Console.WriteLine("the extent permitted by law. <http://www.gnu.org/licenses/> for details."); Console.WriteLine("the extent permitted by law. <http://www.gnu.org/licenses/> for details.");
char[] drives = CDDriveReader.DrivesAvailable(); int correctionQuality = 1;
if (drives.Length < 1) string driveLetter = null;
int driveOffset = 0;
for (int arg = 0; arg < args.Length; arg++)
{ {
Console.WriteLine("No CD drives found."); bool ok = true;
return; if (args[arg] == "-P" || args[arg] == "--paranoid")
correctionQuality = 4;
//else if (args[arg] == "-B" || args[arg] == "--burst")
// correctionQuality = 1;
else if ((args[arg] == "-D" || args[arg] == "--drive") && ++arg < args.Length)
driveLetter = args[arg];
else if ((args[arg] == "-O" || args[arg] == "--offset") && ++arg < args.Length)
ok = int.TryParse(args[arg], out driveOffset);
else
ok = false;
if (!ok)
{
Usage();
return;
}
} }
char driveLetter = drives[0];
char[] drives;
if (driveLetter == null || driveLetter.Length < 1)
{
drives = CDDriveReader.DrivesAvailable();
if (drives.Length < 1)
{
Console.WriteLine("No CD drives found.");
return;
}
}
else
{
drives = new char[1];
drives[0] = driveLetter[0];
}
#if !DEBUG #if !DEBUG
try try
#endif #endif
{ {
CDDriveReader audioSource = new CDDriveReader(); CDDriveReader audioSource = new CDDriveReader();
audioSource.Open(driveLetter); audioSource.Open(drives[0]);
int driveOffset;
if (!AccurateRipVerify.FindDriveReadOffset(audioSource.ARName, out driveOffset)) if (audioSource.TOC.AudioTracks < 1)
throw new Exception("Failed to find drive read offset for drive" + audioSource.ARName); {
audioSource.DriveOffset = driveOffset; Console.WriteLine("{0}: CD does not contain any audio tracks.", audioSource.Path);
audioSource.Close();
return;
}
if (driveOffset == 0)
if (!AccurateRipVerify.FindDriveReadOffset(audioSource.ARName, out driveOffset))
Console.WriteLine("Unknown read offset for drive {0}!!!", audioSource.Path);
//throw new Exception("Failed to find drive read offset for drive" + audioSource.ARName);
audioSource.DriveOffset = driveOffset;
audioSource.CorrectionQuality = correctionQuality;
//bool toStdout = false;
AccurateRipVerify arVerify = new AccurateRipVerify(audioSource.TOC); AccurateRipVerify arVerify = new AccurateRipVerify(audioSource.TOC);
int[,] buff = new int[audioSource.BestBlockSize, audioSource.ChannelCount]; int[,] buff = new int[audioSource.BestBlockSize, audioSource.ChannelCount];
string CDDBId = AccurateRipVerify.CalculateCDDBId(audioSource.TOC); string CDDBId = AccurateRipVerify.CalculateCDDBId(audioSource.TOC);
@@ -81,7 +168,8 @@ namespace CUERipper
release = null; release = null;
} }
string destFile = (release == null) ? "cdimage.flac" : release.GetArtist() + " - " + release.GetTitle() + ".flac"; //string destFile = (release == null) ? "cdimage.flac" : release.GetArtist() + " - " + release.GetTitle() + ".flac";
string destFile = (release == null) ? "cdimage.wav" : release.GetArtist() + " - " + release.GetTitle() + ".wav";
Console.WriteLine("Drive : {0}", audioSource.Path); Console.WriteLine("Drive : {0}", audioSource.Path);
Console.WriteLine("Read offset : {0}", audioSource.DriveOffset); Console.WriteLine("Read offset : {0}", audioSource.DriveOffset);
@@ -90,48 +178,53 @@ namespace CUERipper
Console.WriteLine("AccurateRip : {0}", arVerify.ARStatus == null ? "ok" : arVerify.ARStatus); Console.WriteLine("AccurateRip : {0}", arVerify.ARStatus == null ? "ok" : arVerify.ARStatus);
Console.WriteLine("MusicBrainz : {0}", release == null ? "not found" : release.GetArtist() + " - " + release.GetTitle()); Console.WriteLine("MusicBrainz : {0}", release == null ? "not found" : release.GetArtist() + " - " + release.GetTitle());
IAudioDest audioDest = new FLACWriter(destFile, audioSource.BitsPerSample, audioSource.ChannelCount, audioSource.SampleRate); //IAudioDest audioDest = new FLACWriter(destFile, audioSource.BitsPerSample, audioSource.ChannelCount, audioSource.SampleRate);
IAudioDest audioDest = new WAVWriter(destFile, audioSource.BitsPerSample, audioSource.ChannelCount, audioSource.SampleRate, null);
audioDest.FinalSampleCount = (long)audioSource.Length; audioDest.FinalSampleCount = (long)audioSource.Length;
ProgressMeter meter = new ProgressMeter();
DateTime start = DateTime.Now; audioSource.ReadProgress += new EventHandler<ReadProgressArgs>(meter.ReadProgress);
TimeSpan lastPrint = TimeSpan.FromMilliseconds(0);
do do
{ {
uint samplesRead = audioSource.Read(buff, Math.Min((uint)buff.GetLength(0), (uint)audioSource.Remaining)); uint toRead = Math.Min((uint)buff.GetLength(0), (uint)audioSource.Remaining);
uint samplesRead = audioSource.Read(buff, toRead);
if (samplesRead == 0) break; if (samplesRead == 0) break;
if (samplesRead != toRead)
throw new Exception("samples read != samples requested");
arVerify.Write(buff, samplesRead); arVerify.Write(buff, samplesRead);
audioDest.Write(buff, samplesRead); audioDest.Write(buff, samplesRead);
TimeSpan elapsed = DateTime.Now - start;
if ((elapsed - lastPrint).TotalMilliseconds > 60)
{
Console.Write("\rProgress : {0:00}%; {1:0.00}x; {2}/{3}",
100.0 * audioSource.Position / audioSource.Length,
audioSource.Position / elapsed.TotalSeconds / audioSource.SampleRate,
elapsed,
TimeSpan.FromMilliseconds(elapsed.TotalMilliseconds / audioSource.Position * audioSource.Length)
);
lastPrint = elapsed;
}
} while (true); } while (true);
TimeSpan totalElapsed = DateTime.Now - start; TimeSpan totalElapsed = DateTime.Now - meter.realStart;
Console.Write("\r \r"); Console.Write("\r \r");
Console.WriteLine("Results : {0:0.00}x; {1}", Console.WriteLine("Results : {0:0.00}x; {1:d5} errors; {2:d2}:{3:d2}:{4:d2}",
audioSource.Length / totalElapsed.TotalSeconds / audioSource.SampleRate, audioSource.Length / totalElapsed.TotalSeconds / audioSource.SampleRate,
totalElapsed audioSource.ErrorsCount,
totalElapsed.Hours, totalElapsed.Minutes, totalElapsed.Seconds
); );
audioDest.Close(); audioDest.Close();
StringWriter logWriter = new StringWriter(); StringWriter logWriter = new StringWriter();
logWriter.WriteLine("{0}", programVersion); logWriter.WriteLine("{0}", programVersion);
logWriter.WriteLine();
logWriter.WriteLine("Extraction logfile from {0}", DateTime.Now); logWriter.WriteLine("Extraction logfile from {0}", DateTime.Now);
logWriter.WriteLine();
logWriter.WriteLine("Used drive : {0}", audioSource.Path); logWriter.WriteLine("Used drive : {0}", audioSource.Path);
logWriter.WriteLine();
logWriter.WriteLine("Read offset correction : {0}", audioSource.DriveOffset); logWriter.WriteLine("Read offset correction : {0}", audioSource.DriveOffset);
bool wereErrors = false;
for (int iTrack = 1; iTrack <= audioSource.TOC.AudioTracks; iTrack++)
for (uint iSector = audioSource.TOC[iTrack].Start; iSector <= audioSource.TOC[iTrack].End; iSector ++)
if (audioSource.Errors[(int)iSector])
{
if (!wereErrors)
{
logWriter.WriteLine();
logWriter.WriteLine("Errors detected");
logWriter.WriteLine();
}
wereErrors = true;
logWriter.WriteLine("Track {0} contains errors", iTrack);
break;
}
logWriter.WriteLine(); logWriter.WriteLine();
logWriter.WriteLine("TOC of the extracted CD"); logWriter.WriteLine("TOC of the extracted CD");
logWriter.WriteLine(); logWriter.WriteLine();
@@ -180,6 +273,8 @@ namespace CUERipper
} }
if (audioSource.TOC[track].ISRC != null) if (audioSource.TOC[track].ISRC != null)
cueWriter.WriteLine(" ISRC {0}", audioSource.TOC[track].ISRC); cueWriter.WriteLine(" ISRC {0}", audioSource.TOC[track].ISRC);
if (audioSource.TOC[track].PreEmphasis)
cueWriter.WriteLine(" FLAGS PRE");
for (int index = audioSource.TOC[track].Pregap > 0 ? 0 : 1; index <= audioSource.TOC[track].LastIndex; index++) for (int index = audioSource.TOC[track].Pregap > 0 ? 0 : 1; index <= audioSource.TOC[track].LastIndex; index++)
cueWriter.WriteLine(" INDEX {0:00} {1}", index, audioSource.TOC[track][index].MSF); cueWriter.WriteLine(" INDEX {0:00} {1}", index, audioSource.TOC[track][index].MSF);
} }
@@ -190,10 +285,10 @@ namespace CUERipper
audioSource.Close(); audioSource.Close();
FLACReader tagger = new FLACReader(destFile, null); //FLACReader tagger = new FLACReader(destFile, null);
tagger.Tags.Add("CUESHEET", cueWriter.ToString()); //tagger.Tags.Add("CUESHEET", cueWriter.ToString());
tagger.Tags.Add("LOG", logWriter.ToString()); //tagger.Tags.Add("LOG", logWriter.ToString());
tagger.UpdateTags(false); //tagger.UpdateTags(false);
} }
#if !DEBUG #if !DEBUG
catch (Exception ex) catch (Exception ex)
@@ -204,5 +299,17 @@ namespace CUERipper
} }
#endif #endif
} }
//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);
//}
} }
} }

View File

@@ -81,6 +81,10 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="CRC16CCITT.cs" />
<Compile Include="Galois.cs" />
<Compile Include="RsDecode.cs" />
<Compile Include="RsEncode.cs" />
<Compile Include="SCSIDrive.cs" /> <Compile Include="SCSIDrive.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>

View File

@@ -21,6 +21,7 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Text; using System.Text;
@@ -40,16 +41,29 @@ namespace CUETools.Ripper.SCSI
byte[] cdtext = null; byte[] cdtext = null;
private Device m_device; private Device m_device;
int _sampleOffset = 0; int _sampleOffset = 0;
uint _samplesInBuffer = 0;
uint _samplesBufferOffset = 0;
uint _samplesBufferSector = 0;
int _driveOffset = 0; int _driveOffset = 0;
const int CB_AUDIO = 588 * 4 + 16; int _correctionQuality = 1;
const int NSECTORS = 32; int _currentStart = -1, _currentEnd = -1, _currentErrorsCount = 0;
const bool DoC2 = true;
const int CB_AUDIO = 588 * 4 + 16 + (DoC2 ? 294 : 0);
//const int REDUNDANCY = 8;
const int NSECTORS = 64; //255 - REDUNDANCY;
const int MSECTORS = 10000000 / CB_AUDIO;
int _currentTrack = -1, _currentIndex = -1, _currentTrackActualStart = -1; int _currentTrack = -1, _currentIndex = -1, _currentTrackActualStart = -1;
Logger m_logger; Logger m_logger;
CDImageLayout _toc; CDImageLayout _toc;
DeviceInfo m_info; DeviceInfo m_info;
Crc16Ccitt _crc;
//RsEncode _rsEncoder;
//RsDecode _rsDecoder;
List<ScanResults> _scanResults;
ScanResults _currentScan;
BitArray _errors;
int _errorsCount;
byte[] _currentData = new byte[MSECTORS * 4 * 588];
int[] valueScore = new int[256];
public event EventHandler<ReadProgressArgs> ReadProgress;
public CDImageLayout TOC public CDImageLayout TOC
{ {
@@ -59,9 +73,28 @@ namespace CUETools.Ripper.SCSI
} }
} }
public BitArray Errors
{
get
{
return _errors;
}
}
public int ErrorsCount
{
get
{
return _errorsCount;
}
}
public CDDriveReader() public CDDriveReader()
{ {
m_logger = new Logger(); m_logger = new Logger();
_crc = new Crc16Ccitt(InitialCrcValue.Zeros);
//_rsEncoder = new RsEncode(REDUNDANCY);
//_rsDecoder = new RsDecode(REDUNDANCY);
} }
public bool Open(char Drive) public bool Open(char Drive)
@@ -97,19 +130,28 @@ namespace CUETools.Ripper.SCSI
//ParseProfileList(f.Data); //ParseProfileList(f.Data);
// } // }
SpeedDescriptorList speed_list; //SpeedDescriptorList speed_list;
st = m_device.GetSpeed(out speed_list); //st = m_device.GetSpeed(out speed_list);
if (st != Device.CommandStatus.Success) //if (st != Device.CommandStatus.Success)
throw new Exception("GetSpeed failed: SCSI error"); // throw new Exception("GetSpeed failed: SCSI error");
st = m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, 32767/*Device.OptimumSpeed*/, Device.OptimumSpeed); //int bytesPerSec = 4 * 588 * 75 * (pass > 8 ? 4 : pass > 4 ? 8 : pass > 0 ? 16 : 32);
if (st != Device.CommandStatus.Success) //Device.CommandStatus st = m_device.SetStreaming(Device.RotationalControl.CLVandNonPureCav, start, end, bytesPerSec, 1, bytesPerSec, 1);
throw new Exception("SetCdSpeed failed: SCSI error"); //if (st != Device.CommandStatus.Success)
// System.Console.WriteLine("SetStreaming: ", (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString()));
//st = m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, (ushort)(bytesPerSec / 1024), (ushort)(bytesPerSec / 1024));
//if (st != Device.CommandStatus.Success)
// System.Console.WriteLine("SetCdSpeed: ", (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString()));
//st = m_device.SetCdSpeed(Device.RotationalControl.CLVandNonPureCav, 32767/*Device.OptimumSpeed*/, Device.OptimumSpeed);
//if (st != Device.CommandStatus.Success)
// throw new Exception("SetCdSpeed failed: SCSI error");
IList<TocEntry> toc; IList<TocEntry> toc;
st = m_device.ReadToc((byte)0, false, out toc); st = m_device.ReadToc((byte)0, false, out toc);
if (st != Device.CommandStatus.Success) if (st != Device.CommandStatus.Success)
throw new Exception("ReadToc failed: SCSI error"); throw new Exception("ReadTOC: " + (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString()));
st = m_device.ReadCDText(out cdtext); st = m_device.ReadCDText(out cdtext);
// new CDTextEncoderDecoder // new CDTextEncoderDecoder
@@ -119,10 +161,13 @@ namespace CUETools.Ripper.SCSI
_toc.AddTrack(new CDTrack((uint)iTrack + 1, _toc.AddTrack(new CDTrack((uint)iTrack + 1,
toc[iTrack].StartSector, toc[iTrack].StartSector,
toc[iTrack + 1].StartSector - toc[iTrack].StartSector - toc[iTrack + 1].StartSector - toc[iTrack].StartSector -
((toc[iTrack + 1].Control == 0 || iTrack + 1 == toc.Count - 1) ? 0U : 152U * 75U), ((toc[iTrack + 1].Control < 4 || iTrack + 1 == toc.Count - 1) ? 0U : 152U * 75U),
toc[iTrack].Control == 0)); toc[iTrack].Control < 4,
(toc[iTrack].Control & 1) == 1));
if (_toc[1].IsAudio) if (_toc[1].IsAudio)
_toc[1][0].Start = 0; _toc[1][0].Start = 0;
if (_toc.AudioLength > 0)
_errors = new BitArray((int)_toc.AudioLength); // !!!
return true; return true;
} }
@@ -137,32 +182,54 @@ namespace CUETools.Ripper.SCSI
{ {
get get
{ {
return Math.Min(m_device.MaximumTransferLength / CB_AUDIO, NSECTORS) * 588; return Math.Min(m_device.MaximumTransferLength / CB_AUDIO - 1, NSECTORS) * 588;
} }
} }
private void ProcessSubchannel(int sector, int Sectors2Read) private void ProcessSubchannel(int sector, int Sectors2Read, bool updateMap)
{ {
for (int iSector = 0; iSector < Sectors2Read; iSector++) for (int iSector = 0; iSector < Sectors2Read; iSector++)
{ {
int q_pos = (iSector + 1) * (588 * 4 + 16) - 16; int q_pos = (sector - _currentStart + iSector + 1) * CB_AUDIO - 16;
int ctl = _sectorBuffer[q_pos + 0] >> 4; int ctl = _currentScan.Data[q_pos + 0] >> 4;
int adr = _sectorBuffer[q_pos + 0] & 7; int adr = _currentScan.Data[q_pos + 0] & 7;
bool preemph = (ctl == 1); bool preemph = (ctl & 1) == 1;
switch (adr) switch (adr)
{ {
case 1: // current position case 1: // current position
{ {
int iTrack = fromBCD(_sectorBuffer[q_pos + 1]); int iTrack = fromBCD(_currentScan.Data[q_pos + 1]);
int iIndex = fromBCD(_sectorBuffer[q_pos + 2]); int iIndex = fromBCD(_currentScan.Data[q_pos + 2]);
int mm = fromBCD(_currentScan.Data[q_pos + 7]);
int ss = fromBCD(_currentScan.Data[q_pos + 8]);
int ff = fromBCD(_currentScan.Data[q_pos + 9]);
int sec = ff + 75 * (ss + 60 * mm) - 150; // sector + iSector;
//if (sec != sector + iSector)
// System.Console.WriteLine("\rLost sync: {0} vs {1} ({2:X} vs {3:X})", CDImageLayout.TimeToString((uint)(sector + iSector)), CDImageLayout.TimeToString((uint)sec), sector + iSector, sec);
//ushort crc = _crc.ComputeChecksum(_currentScan.Data, q_pos, 10);
//crc ^= 0xffff;
//if (_currentScan.Data[q_pos + 10] != 0 && _currentScan.Data[q_pos + 11] != 0 &&
// ((crc & 0xff) != _currentScan.Data[q_pos + 11] ||
// (crc >> 8) != _currentScan.Data[q_pos + 10])
// )
//{
// System.Console.WriteLine("CRC error at {0}", CDImageLayout.TimeToString((uint)(sector + iSector)));
//}
if (iTrack == 110) if (iTrack == 110)
throw new Exception("lead out area encountred"); {
if (sector + iSector + 75 < _toc.AudioLength)
throw new Exception("lead out area encountred");
// make sure that data is zero?
return;
}
if (iTrack == 0) if (iTrack == 0)
throw new Exception("lead in area encountred"); throw new Exception("lead in area encountred");
if (!updateMap)
break;
if (iTrack != _currentTrack) if (iTrack != _currentTrack)
{ {
_currentTrack = iTrack; _currentTrack = iTrack;
_currentTrackActualStart = sector + iSector; _currentTrackActualStart = sec;
_currentIndex = iIndex; _currentIndex = iIndex;
if (_currentIndex != 1 && _currentIndex != 0) if (_currentIndex != 1 && _currentIndex != 0)
throw new Exception("invalid index"); throw new Exception("invalid index");
@@ -174,13 +241,15 @@ namespace CUETools.Ripper.SCSI
_currentIndex = iIndex; _currentIndex = iIndex;
if (_currentIndex == 1) if (_currentIndex == 1)
{ {
uint pregap = (uint) (sector + iSector - _currentTrackActualStart); uint pregap = (uint)(sec - _currentTrackActualStart);
_toc[iTrack][0].Start = _toc[iTrack].Start - pregap; _toc[iTrack][0].Start = _toc[iTrack].Start - pregap;
_currentTrackActualStart = sector + iSector; _currentTrackActualStart = sec;
} else } else
_toc[iTrack].AddIndex(new CDTrackIndex((uint)iIndex, (uint)(_toc[iTrack].Start + sector + iSector - _currentTrackActualStart))); _toc[iTrack].AddIndex(new CDTrackIndex((uint)iIndex, (uint)(_toc[iTrack].Start + sec - _currentTrackActualStart)));
_currentIndex = iIndex; _currentIndex = iIndex;
} }
if (preemph)
_toc[iTrack].PreEmphasis = true;
break; break;
} }
case 2: // catalog case 2: // catalog
@@ -188,7 +257,7 @@ namespace CUETools.Ripper.SCSI
{ {
StringBuilder catalog = new StringBuilder(); StringBuilder catalog = new StringBuilder();
for (int i = 1; i < 8; i++) for (int i = 1; i < 8; i++)
catalog.AppendFormat("{0:x2}", _sectorBuffer[q_pos + i]); catalog.AppendFormat("{0:x2}", _currentScan.Data[q_pos + i]);
_toc.Catalog = catalog.ToString(0, 13); _toc.Catalog = catalog.ToString(0, 13);
} }
break; break;
@@ -197,16 +266,16 @@ namespace CUETools.Ripper.SCSI
{ {
StringBuilder isrc = new StringBuilder(); StringBuilder isrc = new StringBuilder();
char[] ISRC6 = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '#', '#', '#', '#', '#', '#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; char[] ISRC6 = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '#', '#', '#', '#', '#', '#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
isrc.Append(ISRC6[_sectorBuffer[q_pos + 1] >> 2]); isrc.Append(ISRC6[_currentScan.Data[q_pos + 1] >> 2]);
isrc.Append(ISRC6[((_sectorBuffer[q_pos + 1] & 0x3) << 4) + (_sectorBuffer[q_pos + 2] >> 4)]); isrc.Append(ISRC6[((_currentScan.Data[q_pos + 1] & 0x3) << 4) + (_currentScan.Data[q_pos + 2] >> 4)]);
isrc.Append(ISRC6[((_sectorBuffer[q_pos + 2] & 0xf) << 2) + (_sectorBuffer[q_pos + 3] >> 6)]); isrc.Append(ISRC6[((_currentScan.Data[q_pos + 2] & 0xf) << 2) + (_currentScan.Data[q_pos + 3] >> 6)]);
isrc.Append(ISRC6[(_sectorBuffer[q_pos + 3] & 0x3f)]); isrc.Append(ISRC6[(_currentScan.Data[q_pos + 3] & 0x3f)]);
isrc.Append(ISRC6[_sectorBuffer[q_pos + 4] >> 2]); isrc.Append(ISRC6[_currentScan.Data[q_pos + 4] >> 2]);
isrc.Append(ISRC6[((_sectorBuffer[q_pos + 4] & 0x3) << 4) + (_sectorBuffer[q_pos + 5] >> 4)]); isrc.Append(ISRC6[((_currentScan.Data[q_pos + 4] & 0x3) << 4) + (_currentScan.Data[q_pos + 5] >> 4)]);
isrc.AppendFormat("{0:x}", _sectorBuffer[q_pos + 5] & 0xf); isrc.AppendFormat("{0:x}", _currentScan.Data[q_pos + 5] & 0xf);
isrc.AppendFormat("{0:x2}", _sectorBuffer[q_pos + 6]); isrc.AppendFormat("{0:x2}", _currentScan.Data[q_pos + 6]);
isrc.AppendFormat("{0:x2}", _sectorBuffer[q_pos + 7]); isrc.AppendFormat("{0:x2}", _currentScan.Data[q_pos + 7]);
isrc.AppendFormat("{0:x}", _sectorBuffer[q_pos + 8] >> 4); isrc.AppendFormat("{0:x}", _currentScan.Data[q_pos + 8] >> 4);
_toc[_currentTrack].ISRC = isrc.ToString(); _toc[_currentTrack].ISRC = isrc.ToString();
} }
break; break;
@@ -216,117 +285,294 @@ namespace CUETools.Ripper.SCSI
private unsafe void FetchSectors(int sector, int Sectors2Read) private unsafe void FetchSectors(int sector, int Sectors2Read)
{ {
fixed (byte* data = _sectorBuffer) fixed (byte* data = _currentScan.Data)
{ {
Device.CommandStatus st = m_device.ReadCDAndSubChannel(2, 1, true, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)data), Sectors2Read * (2352 + 16)); //Device.CommandStatus st;
//using (Command cmd = new Command(ScsiCommandCode.Read12, 12, (IntPtr)((void*)data), Sectors2Read * 588 * 4, Command.CmdDirection.In, 5 * 60))
//{
// //cmd.SetCDB8(1, 4); // force from media
// cmd.SetCDB32(2, sector);
// cmd.SetCDB32(6, Sectors2Read);
// //cmd.SetCDB8(10, 0x80); // streaming
// st= m_device.SendCommand(cmd);
//}
Device.CommandStatus st = m_device.ReadCDAndSubChannel(2, DoC2, 1, false, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)(data + (sector - _currentStart) * CB_AUDIO)), Sectors2Read * CB_AUDIO);
if (st != Device.CommandStatus.Success) if (st != Device.CommandStatus.Success)
throw new Exception("ReadCDAndSubChannel failed: SCSI error"); {
if (st == Device.CommandStatus.DeviceFailed && m_device.GetSenseAsc() == 0x64 && m_device.GetSenseAscq() == 0x00)
{
int iErrors = 0;
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
st = m_device.ReadCDAndSubChannel(2, DoC2, 1, false, (uint)(sector + iSector), 1U, (IntPtr)((void*)(data + (sector + iSector - _currentStart) * CB_AUDIO)), CB_AUDIO);
if (st != Device.CommandStatus.Success)
{
iErrors ++;
for (int iPos = 0; iPos < CB_AUDIO; iPos++)
data[(sector + iSector - _currentStart) * CB_AUDIO + iPos] = (DoC2 && iPos >= 4 * 588 && iPos < 4 * 588 + 294) ? (byte)255 : (byte)0;
}
}
if (iErrors < Sectors2Read)
return;
}
string status = (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString());
st = m_device.ReadCDAndSubChannel(0, false, 1, false, (uint)sector, (uint)Sectors2Read, (IntPtr)((void*)(data + (sector - _currentStart) * 4 * 588)), Sectors2Read * 4 * 588);
if (st != Device.CommandStatus.Success)
status += "; ReadWithoutSubchannel: " + (st == Device.CommandStatus.DeviceFailed ? Device.LookupSenseError(m_device.GetSenseAsc(), m_device.GetSenseAscq()) : st.ToString());
else
status += "; ReadWithoutSubchannel: might work";
throw new Exception("ReadCD: " + status);
}
//int localC2 = 0;
//if (DoC2)
// for (int iSector = 0; iSector < Sectors2Read; iSector++)
// {
// _currentScan.C2Errors[sector + iSector] = data[(iSector + 1) * CB_AUDIO - 18] != 0;
// localC2 += (data[(iSector + 1) * CB_AUDIO - 18] != 0) ? 1 : 0;
// }
//_currentScan.C2Count += localC2;
} }
ProcessSubchannel(sector, Sectors2Read);
} }
public unsafe uint Read(int[,] buff, uint sampleCount) private unsafe void CorrectSectors(int sector, int Sectors2Read, bool findWorst, bool markErrors)
{
int c2Score = 10;
for (int iSector = 0; iSector < Sectors2Read; iSector++)
{
for (int iPar = 0; iPar < 4 * 588; iPar++)
{
int dataPos = (sector - _currentStart + iSector) * CB_AUDIO + iPar;
int c2Pos = (sector - _currentStart + iSector) * CB_AUDIO + 4 * 588 + iPar / 8;
int c2Bit = 0x80 >> (iPar % 8);
Array.Clear(valueScore, 0, 256);
byte bestValue = _currentScan.Data[dataPos];
valueScore[bestValue] += 1 + (((_currentScan.Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
int totalScore = valueScore[bestValue];
for (int result = 0; result < _scanResults.Count; result++)
{
byte value = _scanResults[result].Data[dataPos];
valueScore[value] += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
totalScore += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
if (valueScore[value] > valueScore[bestValue])
bestValue = value;
}
bool fError = valueScore[bestValue] * 2 <= c2Score + totalScore;
if (fError)
_currentErrorsCount++;
_currentData[(sector - _currentStart + iSector) * 4 * 588 + iPar] = bestValue;
if (findWorst)
{
for (int result = 0; result < _scanResults.Count; result++)
_scanResults[result].Quality += Math.Min(0, valueScore[_scanResults[result].Data[dataPos]] - c2Score - 1);
_currentScan.Quality += Math.Min(0, valueScore[_currentScan.Data[dataPos]] - c2Score - 1);
}
if (markErrors)
{
_errors[sector + iSector] = fError;
_errorsCount += fError ? 1 : 0;
}
}
}
}
//private unsafe int CorrectSectorsTest(int start, int end, int c2Score, byte[] realData, int worstScan)
//{
// int[] valueScore = new int[256];
// int[] scoreErrors = new int[256];
// int realErrors = 0;
// int bestScore = 0;
// int _errorsCaught = 0;
// int _falsePositives = 0;
// for (int iSector = 0; iSector < end - start; iSector++)
// {
// for (int iPar = 0; iPar < 4 * 588; iPar++)
// {
// int dataPos = iSector * CB_AUDIO + iPar;
// int c2Pos = iSector * CB_AUDIO + 4 * 588 + iPar / 8;
// int c2Bit = 0x80 >> (iPar % 8);
// Array.Clear(valueScore, 0, 256);
// byte bestValue = _currentScan.Data[dataPos];
// valueScore[bestValue] += 1 + (((_currentScan.Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
// int totalScore = valueScore[bestValue];
// for (int result = 0; result < _scanResults.Count; result++)
// {
// if (result == worstScan)
// continue;
// byte value = _scanResults[result].Data[dataPos];
// valueScore[value] += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
// totalScore += 1 + (((_scanResults[result].Data[c2Pos] & c2Bit) == 0) ? c2Score : 0);
// if (valueScore[value] > valueScore[bestValue])
// bestValue = value;
// }
// if (valueScore[bestValue] < (1 + c2Score + totalScore) / 2)
// _currentErrorsCount++;
// //_currentData[iSector * 4 * 588 + iPar] = bestValue;
// if (realData[iSector * 4 * 588 + iPar] != bestValue)
// {
// if (valueScore[bestValue] > bestScore)
// scoreErrors[valueScore[bestValue]]++;
// realErrors++;
// if (valueScore[bestValue] * 2 <= c2Score + totalScore)
// _errorsCaught++;
// } else
// if (valueScore[bestValue] * 2 <= c2Score + totalScore)
// _falsePositives++;
// }
// }
// //string s = "";
// //for (int i = 0; i < 256; i++)
// // if (scoreErrors[i] > 0)
// // s += string.Format("[{0}]={1};", i, scoreErrors[i]);
// //System.Console.WriteLine("RE{0:000} EC{1} FP{2}", realErrors, _errorsCaught, _falsePositives);
// return realErrors;
//}
public void PrefetchSector(int iSector)
{
int nPasses = 16 + _correctionQuality * 2;
int nExtraPasses = 8 + _correctionQuality;
if (_currentStart == MSECTORS * (iSector / MSECTORS))
return;
_currentStart = MSECTORS * (iSector / MSECTORS);
_currentEnd = Math.Min(_currentStart + MSECTORS, (int)_toc.AudioLength);
_scanResults = new List<ScanResults>();
//FileStream correctFile = new FileStream("correct.wav", FileMode.Open);
//byte[] realData = new byte[MSECTORS * 4 * 588];
//correctFile.Seek(0x2C + start * 588 * 4, SeekOrigin.Begin);
//if (correctFile.Read(realData, 0, MSECTORS * 4 * 588) != MSECTORS * 4 * 588)
// throw new Exception("read");
//correctFile.Close();
for (int pass = 0; pass <= nPasses + nExtraPasses; pass++)
{
DateTime PassTime = DateTime.Now;
_currentScan = new ScanResults(MSECTORS, CB_AUDIO);
_currentErrorsCount = 0;
int nSectors = Math.Min(NSECTORS, m_device.MaximumTransferLength / CB_AUDIO - 1);
for (int sector = _currentStart; sector < _currentEnd; sector += nSectors)
{
int Sectors2Read = Math.Min(nSectors, _currentEnd - sector);
FetchSectors(sector, Sectors2Read);
CorrectSectors(sector, Sectors2Read, pass > nPasses, pass == nPasses + nExtraPasses);
ProcessSubchannel(sector, Sectors2Read, pass == 0);
if (ReadProgress != null)
ReadProgress(this, new ReadProgressArgs(sector + Sectors2Read, pass, _currentStart, _currentEnd, _currentErrorsCount, PassTime));
}
//System.Console.WriteLine();
//if (CorrectSectorsTest(start, _currentEnd, 10, realData) == 0)
// break;
if (pass == nPasses + nExtraPasses)
break;
if (pass > nPasses)
{
int worstPass = -1;
for (int result = 0; result < _scanResults.Count; result++)
if (_scanResults[result].Quality < (worstPass < 0 ? _currentScan.Quality : _scanResults[worstPass].Quality))
worstPass = result;
//if (worstPass < 0)
// System.Console.WriteLine("bad scan");
//else
// System.Console.WriteLine("{0}->{1}, {2}->{3}", _scanResults[worstPass].Quality, _currentScan.Quality, CorrectSectorsTest(_currentStart, _currentEnd, 10, realData, -1), CorrectSectorsTest(_currentStart, _currentEnd, 10, realData, worstPass));
if (worstPass < 0)
_currentScan = null;
else
_scanResults[worstPass] = _currentScan;
for (int result = 0; result < _scanResults.Count; result++)
_scanResults[result].Quality = 0;
continue;
}
if (_currentErrorsCount == 0 && pass >= _correctionQuality)
{
bool syncOk = true;
//if (pass == 0)
//{
// ScanResults saved = _currentScan;
// _currentScan = new ScanResults(_currentEnd - _currentStart, CB_AUDIO);
// for (int sector = _currentStart; sector < _currentEnd && syncOk; sector += 7)
// {
// FetchSectors(sector, 2);
// for (int i = 0; i < 2 * CB_AUDIO; i++)
// if (_currentScan.Data[(sector - _currentStart) * CB_AUDIO + i] != saved.Data[(sector - _currentStart) * CB_AUDIO + i])
// {
// System.Console.WriteLine("Lost Sync");
// syncOk = false;
// break;
// }
// }
// _currentScan = saved;
//}
if (syncOk)
break;
}
_scanResults.Add(_currentScan);
}
_currentScan = null;
_scanResults = null;
}
public uint Read(int[,] buff, uint sampleCount)
{ {
if (_toc == null) if (_toc == null)
throw new Exception("Read: invalid TOC"); throw new Exception("Read: invalid TOC");
if (_sampleOffset - _driveOffset >= (uint)Length) if (_sampleOffset - _driveOffset >= (uint)Length)
return 0; return 0;
if (_sampleOffset > (uint)Length) if (_sampleOffset > (uint)Length)
{ {
int samplesRead = _sampleOffset - (int)Length; int samplesRead = _sampleOffset - (int)Length;
for (int i = 0; i < samplesRead; i++) for (int i = 0; i < samplesRead; i++)
for (int c = 0; c < ChannelCount; c++) for (int c = 0; c < ChannelCount; c++)
buff[i, c] = 0; buff[i, c] = 0;
_sampleOffset += samplesRead; _sampleOffset += samplesRead;
return 0; return 0;
} }
if ((uint)(_sampleOffset - _driveOffset + sampleCount) > Length) if ((uint)(_sampleOffset - _driveOffset + sampleCount) > Length)
sampleCount = (uint)((int)Length + _driveOffset - _sampleOffset); sampleCount = (uint)((int)Length + _driveOffset - _sampleOffset);
int silenceCount = 0; int silenceCount = 0;
if ((uint)(_sampleOffset + sampleCount) > Length) if ((uint)(_sampleOffset + sampleCount) > Length)
{ {
silenceCount = _sampleOffset + (int)sampleCount - (int)Length; silenceCount = _sampleOffset + (int)sampleCount - (int)Length;
sampleCount -= (uint) silenceCount; sampleCount -= (uint) silenceCount;
} }
uint pos = 0; uint pos = 0;
if (_sampleOffset < 0) if (_sampleOffset < 0)
{ {
uint nullSamplesRead = Math.Min((uint)-_sampleOffset, sampleCount); uint nullSamplesRead = Math.Min((uint)-_sampleOffset, sampleCount);
for (int i = 0; i < nullSamplesRead; i++) for (int i = 0; i < nullSamplesRead; i++)
for (int c = 0; c < ChannelCount; c++) for (int c = 0; c < ChannelCount; c++)
buff[i, c] = 0; buff[i, c] = 0;
pos += nullSamplesRead; pos += nullSamplesRead;
sampleCount -= nullSamplesRead; sampleCount -= nullSamplesRead;
_sampleOffset += (int)nullSamplesRead; _sampleOffset += (int)nullSamplesRead;
if (sampleCount == 0) if (sampleCount == 0)
return pos; return pos;
} }
if (_samplesInBuffer > 0)
{
uint samplesRead = Math.Min(_samplesInBuffer, sampleCount);
AudioSamples.BytesToFLACSamples_16(_sectorBuffer, (int)(_samplesBufferSector * (588 * 4 + 16) + _samplesBufferOffset * 4), buff, (int)pos, samplesRead, 2);
pos += samplesRead;
sampleCount -= samplesRead;
_sampleOffset += (int) samplesRead;
if (sampleCount == 0)
{
_samplesInBuffer -= samplesRead;
_samplesBufferOffset += samplesRead;
if (silenceCount > 0)
{
uint nullSamplesRead = (uint) silenceCount;
for (int i = 0; i < nullSamplesRead; i++)
for (int c = 0; c < ChannelCount; c++)
buff[pos + i, c] = 0;
pos += nullSamplesRead;
_sampleOffset += (int)nullSamplesRead;
}
return pos;
}
_samplesInBuffer = 0;
_samplesBufferOffset = 0;
_samplesBufferSector = 0;
}
// if (_sampleOffset < PreGapLength && !_overreadIntoPreGap ... ?
int firstSector = (int)_sampleOffset / 588; int firstSector = (int)_sampleOffset / 588;
int lastSector = (int)(_sampleOffset + sampleCount + 587) / 588; int lastSector = Math.Min((int)(_sampleOffset + sampleCount + 587)/588, (int)_toc.AudioLength);
for (int sector = firstSector; sector < lastSector; sector += NSECTORS) for (int sector = firstSector; sector < lastSector; sector ++)
{ {
int Sectors2Read = ((sector + NSECTORS) < lastSector) ? NSECTORS : (lastSector - sector); PrefetchSector(sector);
FetchSectors(sector, Sectors2Read); uint samplesRead = (uint) (Math.Min((int)sampleCount, 588 - (_sampleOffset % 588)));
for (int iSector = 0; iSector < Sectors2Read; iSector++) AudioSamples.BytesToFLACSamples_16(_currentData, (sector - _currentStart) * 4 * 588 + ((int)_sampleOffset % 588) * 4, buff, (int)pos, samplesRead, 2);
{ pos += samplesRead;
uint samplesRead = (uint) (Math.Min((int)sampleCount, 588) - (_sampleOffset % 588)); sampleCount -= samplesRead;
AudioSamples.BytesToFLACSamples_16(_sectorBuffer, iSector * (588 * 4 + 16) + ((int)_sampleOffset % 588) * 4, buff, (int)pos, samplesRead, 2); _sampleOffset += (int) samplesRead;
pos += samplesRead;
sampleCount -= samplesRead;
_sampleOffset += (int) samplesRead;
if (sampleCount == 0)
{
_samplesBufferSector = (uint)iSector;
_samplesBufferOffset = samplesRead;
_samplesInBuffer = 588U - samplesRead;
if (silenceCount > 0)
{
uint nullSamplesRead = (uint)silenceCount;
for (int i = 0; i < nullSamplesRead; i++)
for (int c = 0; c < ChannelCount; c++)
buff[pos + i, c] = 0;
pos += nullSamplesRead;
_sampleOffset += (int)nullSamplesRead;
}
return pos;
}
}
} }
if (silenceCount > 0) if (silenceCount > 0)
{ {
uint nullSamplesRead = (uint)silenceCount; uint nullSamplesRead = (uint)silenceCount;
for (int i = 0; i < nullSamplesRead; i++) for (int i = 0; i < nullSamplesRead; i++)
for (int c = 0; c < ChannelCount; c++) for (int c = 0; c < ChannelCount; c++)
buff[pos + i, c] = 0; buff[pos + i, c] = 0;
pos += nullSamplesRead; pos += nullSamplesRead;
_sampleOffset += (int)nullSamplesRead; _sampleOffset += (int)nullSamplesRead;
} }
return pos; return pos;
} }
@@ -430,14 +676,24 @@ namespace CUETools.Ripper.SCSI
} }
} }
public int CorrectionQuality
{
get
{
return _correctionQuality;
}
set
{
_correctionQuality = value;
}
}
public static string RipperVersion() public static string RipperVersion()
{ {
return "CUERipper v1.9.3 Copyright (C) 2008 Gregory S. Chudov"; return "CUERipper v1.9.3 Copyright (C) 2008 Gregory S. Chudov";
// ripper.GetName().Name + " " + ripper.GetName().Version; // ripper.GetName().Name + " " + ripper.GetName().Version;
} }
byte[] _sectorBuffer = new byte[CB_AUDIO * NSECTORS];
private int fromBCD(byte hex) private int fromBCD(byte hex)
{ {
return (hex >> 4) * 10 + (hex & 15); return (hex >> 4) * 10 + (hex & 15);
@@ -452,4 +708,35 @@ namespace CUETools.Ripper.SCSI
return result.ToArray(); return result.ToArray();
} }
} }
internal class ScanResults
{
public byte[] Data;
public long Quality;
public ScanResults(int msector, int cbaudio)
{
Data = new byte[msector * cbaudio];
Quality = 0;
}
}
public sealed class ReadProgressArgs: EventArgs
{
public int Position;
public int Pass;
public int PassStart, PassEnd;
public int ErrorsCount;
public DateTime PassTime;
public ReadProgressArgs(int position, int pass, int passStart, int passEnd, int errorsCount, DateTime passTime)
{
Position = position;
Pass = pass;
PassStart = passStart;
PassEnd = passEnd;
ErrorsCount = errorsCount;
PassTime = passTime;
}
}
} }

View File

@@ -20,6 +20,7 @@
// **************************************************************************** // ****************************************************************************
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Text; using System.Text;
@@ -436,6 +437,7 @@ namespace CUETools.Processor
private bool _isCD; private bool _isCD;
private string _driveName; private string _driveName;
private int _driveOffset; private int _driveOffset;
private BitArray _cdErrors;
private bool _isArchive; private bool _isArchive;
private List<string> _archiveContents; private List<string> _archiveContents;
private string _archiveCUEpath; private string _archiveCUEpath;
@@ -696,7 +698,7 @@ namespace CUETools.Processor
} }
trackInfo = new TrackInfo(); trackInfo = new TrackInfo();
_tracks.Add(trackInfo); _tracks.Add(trackInfo);
_toc.AddTrack(new CDTrack((uint)trackNumber, 0, 0, true)); _toc.AddTrack(new CDTrack((uint)trackNumber, 0, 0, true, false));
} }
} }
else if (seenDataTrack) { else if (seenDataTrack) {
@@ -892,7 +894,7 @@ namespace CUETools.Processor
uint trNo, trStart, trEnd; uint trNo, trStart, trEnd;
if (n.Length == 5 && uint.TryParse(n[0], out trNo) && uint.TryParse(n[3], out trStart) && uint.TryParse(n[4], out trEnd)) if (n.Length == 5 && uint.TryParse(n[0], out trNo) && uint.TryParse(n[3], out trStart) && uint.TryParse(n[4], out trEnd))
tocFromLog.AddTrack(new CDTrack(trNo, trStart, trEnd + 1 - trStart, tocFromLog.AddTrack(new CDTrack(trNo, trStart, trEnd + 1 - trStart,
tocFromLog.TrackCount < _toc.TrackCount || trStart != tocFromLog[tocFromLog.TrackCount].End + 1U + 152U * 75U)); tocFromLog.TrackCount < _toc.TrackCount || trStart != tocFromLog[tocFromLog.TrackCount].End + 1U + 152U * 75U, false));
} else } else
if (lineStr.StartsWith("TOC of the extracted CD") if (lineStr.StartsWith("TOC of the extracted CD")
|| lineStr.StartsWith("Exact Audio Copy") || lineStr.StartsWith("Exact Audio Copy")
@@ -906,7 +908,7 @@ namespace CUETools.Processor
if (_accurateRipId == null && _dataTrackLength != null) if (_accurateRipId == null && _dataTrackLength != null)
{ {
CDImageLayout toc2 = new CDImageLayout(_toc); CDImageLayout toc2 = new CDImageLayout(_toc);
toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152U * 75U, _dataTrackLength.Value, false)); toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152U * 75U, _dataTrackLength.Value, false, false));
_accurateRipId = AccurateRipVerify.CalculateAccurateRipId(toc2); _accurateRipId = AccurateRipVerify.CalculateAccurateRipId(toc2);
} }
@@ -1216,18 +1218,39 @@ namespace CUETools.Processor
_toc[track].LengthMSF, _toc[track].LengthMSF,
_toc[track].Start, _toc[track].Start,
_toc[track].End); _toc[track].End);
logWriter.WriteLine(); bool wereErrors = false;
if (_accurateRipMode == AccurateRipMode.VerifyThenConvert) for (int iTrack = 0; iTrack < _toc.AudioTracks; iTrack++)
for (int iTrack = 0; iTrack < _toc.AudioTracks; iTrack++) {
if (_arVerify.BackupCRC(iTrack) != _arVerify.CRC(iTrack)) int cdErrors = 0;
logWriter.WriteLine("Track {0} CRC mismatch: test {1:X8} vs copy {2:X8}", iTrack + 1, _arVerify.BackupCRC(iTrack), _arVerify.CRC(iTrack)); bool crcMismatch = _accurateRipMode == AccurateRipMode.VerifyThenConvert &&
_arVerify.BackupCRC(iTrack) != _arVerify.CRC(iTrack);
for (uint iSector = _toc[iTrack + 1].Start; iSector <= _toc[iTrack + 1].End; iSector++)
if (_cdErrors[(int)iSector])
cdErrors++;
if (crcMismatch || cdErrors != 0)
{
if (!wereErrors)
{
logWriter.WriteLine();
logWriter.WriteLine("Errors detected");
logWriter.WriteLine();
}
wereErrors = true;
if (crcMismatch)
logWriter.WriteLine("Track {0} contains {1} errors, CRC mismatch: test {2:X8} vs copy {3:X8}", iTrack + 1, cdErrors, _arVerify.BackupCRC(iTrack), _arVerify.CRC(iTrack));
else
logWriter.WriteLine("Track {0} contains {1} errors", iTrack + 1, cdErrors);
}
}
if (_accurateRipMode != AccurateRipMode.None) if (_accurateRipMode != AccurateRipMode.None)
{ {
logWriter.WriteLine();
logWriter.WriteLine("AccurateRip summary"); logWriter.WriteLine("AccurateRip summary");
logWriter.WriteLine(); logWriter.WriteLine();
_arVerify.GenerateFullLog(logWriter, 0); _arVerify.GenerateFullLog(logWriter, 0);
logWriter.WriteLine(); logWriter.WriteLine();
} }
logWriter.WriteLine();
logWriter.WriteLine("End of status report"); logWriter.WriteLine("End of status report");
logWriter.Close(); logWriter.Close();
return logWriter.ToString(); return logWriter.ToString();
@@ -1494,7 +1517,7 @@ namespace CUETools.Processor
{ {
uint minDTL = _minDataTrackLength.Value; uint minDTL = _minDataTrackLength.Value;
CDImageLayout toc2 = new CDImageLayout(_toc); CDImageLayout toc2 = new CDImageLayout(_toc);
toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152 * 75, minDTL, false)); toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152 * 75, minDTL, false, false));
for (uint dtl = minDTL; dtl < minDTL + 75; dtl++) for (uint dtl = minDTL; dtl < minDTL + 75; dtl++)
{ {
toc2[toc2.TrackCount].Length = dtl; toc2[toc2.TrackCount].Length = dtl;
@@ -1608,7 +1631,7 @@ namespace CUETools.Processor
{ {
WriteText(_cuePath, cueContents); WriteText(_cuePath, cueContents);
#if !MONO #if !MONO
if (needNewCRCs) if (needNewCRCs && style != CUEStyle.SingleFile)
{ {
for (int iTrack = 0; iTrack < TrackCount; iTrack++) for (int iTrack = 0; iTrack < TrackCount; iTrack++)
{ {
@@ -1972,7 +1995,10 @@ namespace CUETools.Processor
{ {
#if !MONO #if !MONO
if (_isCD && audioSource != null && audioSource is CDDriveReader) if (_isCD && audioSource != null && audioSource is CDDriveReader)
{
updatedTOC = ((CDDriveReader)audioSource).TOC; updatedTOC = ((CDDriveReader)audioSource).TOC;
_cdErrors = ((CDDriveReader)audioSource).Errors;
}
#endif #endif
if (audioSource != null) audioSource.Close(); if (audioSource != null) audioSource.Close();
audioSource = GetAudioSource(++iSource); audioSource = GetAudioSource(++iSource);
@@ -2057,15 +2083,22 @@ namespace CUETools.Processor
#if !MONO #if !MONO
if (_isCD && audioSource != null && audioSource is CDDriveReader) if (_isCD && audioSource != null && audioSource is CDDriveReader)
{
updatedTOC = ((CDDriveReader)audioSource).TOC; updatedTOC = ((CDDriveReader)audioSource).TOC;
_cdErrors = ((CDDriveReader)audioSource).Errors;
}
if (updatedTOC != null) if (updatedTOC != null)
{ {
_toc = updatedTOC; _toc = updatedTOC;
if (_toc.Catalog != null) if (_toc.Catalog != null)
General.SetCUELine(_attributes, "CATALOG", _toc.Catalog, false); General.SetCUELine(_attributes, "CATALOG", _toc.Catalog, false);
for (iTrack = 1; iTrack <= _toc.TrackCount; iTrack++) for (iTrack = 1; iTrack <= _toc.TrackCount; iTrack++)
{
if (_toc[iTrack].IsAudio && _toc[iTrack].ISRC != null) if (_toc[iTrack].IsAudio && _toc[iTrack].ISRC != null)
General.SetCUELine(_tracks[iTrack - 1].Attributes, "ISRC", _toc[iTrack].ISRC, false); General.SetCUELine(_tracks[iTrack - 1].Attributes, "ISRC", _toc[iTrack].ISRC, false);
if (_toc[iTrack].IsAudio && _toc[iTrack].PreEmphasis)
General.SetCUELine(_tracks[iTrack - 1].Attributes, "FLAGS", "PRE", false);
}
} }
#endif #endif
@@ -2401,7 +2434,7 @@ namespace CUETools.Processor
{ {
_dataTrackLength = dtl; _dataTrackLength = dtl;
CDImageLayout toc2 = new CDImageLayout(_toc); CDImageLayout toc2 = new CDImageLayout(_toc);
toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152 * 75, dtl, false)); toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152 * 75, dtl, false, false));
_accurateRipIdActual = _accurateRipId = AccurateRipVerify.CalculateAccurateRipId(toc2); _accurateRipIdActual = _accurateRipId = AccurateRipVerify.CalculateAccurateRipId(toc2);
} }
} }