diff --git a/ArCueDotNet/Program.cs b/ArCueDotNet/Program.cs
index e2e7cf7..0e7d282 100644
--- a/ArCueDotNet/Program.cs
+++ b/ArCueDotNet/Program.cs
@@ -29,8 +29,8 @@ namespace ArCueDotNet
try
{
CUESheet cueSheet = new CUESheet(config);
- cueSheet.Open(pathIn, false);
- cueSheet.GenerateFilenames(OutputAudioFormat.NoAudio, pathIn);
+ cueSheet.Open(pathIn);
+ cueSheet.GenerateFilenames(OutputAudioFormat.NoAudio, false, pathIn);
cueSheet.AccurateRip = AccurateRipMode.Verify;
cueSheet.WriteAudioFiles(Path.GetDirectoryName(pathIn), CUEStyle.SingleFile);
cueSheet.GenerateAccurateRipLog(sw);
diff --git a/CUERipper/CUERipper.csproj b/CUERipper/CUERipper.csproj
index a35354e..e1b2462 100644
--- a/CUERipper/CUERipper.csproj
+++ b/CUERipper/CUERipper.csproj
@@ -125,6 +125,10 @@
{6458A13A-30EF-45A9-9D58-E5031B17BEE2}
CUETools.Codecs
+
+ {4911BD82-49EF-4858-8B51-5394F86739A4}
+ CUETools.Processor
+
{8CF07381-BEA2-4AFC-B3DD-9B2F21C65A3A}
CUETools.Ripper.SCSI
diff --git a/CUERipper/frmCUERipper.Designer.cs b/CUERipper/frmCUERipper.Designer.cs
index f1bc0cf..475c78a 100644
--- a/CUERipper/frmCUERipper.Designer.cs
+++ b/CUERipper/frmCUERipper.Designer.cs
@@ -136,34 +136,34 @@ namespace CUERipper
// comboLossless
//
this.comboLossless.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
- resources.ApplyResources(this.comboLossless, "comboLossless");
this.comboLossless.FormattingEnabled = true;
this.comboLossless.Items.AddRange(new object[] {
resources.GetString("comboLossless.Items"),
resources.GetString("comboLossless.Items1"),
resources.GetString("comboLossless.Items2")});
+ resources.ApplyResources(this.comboLossless, "comboLossless");
this.comboLossless.Name = "comboLossless";
//
// comboCodec
//
this.comboCodec.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
- resources.ApplyResources(this.comboCodec, "comboCodec");
this.comboCodec.FormattingEnabled = true;
this.comboCodec.Items.AddRange(new object[] {
resources.GetString("comboCodec.Items"),
resources.GetString("comboCodec.Items1"),
resources.GetString("comboCodec.Items2"),
resources.GetString("comboCodec.Items3")});
+ resources.ApplyResources(this.comboCodec, "comboCodec");
this.comboCodec.Name = "comboCodec";
//
// comboImage
//
this.comboImage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
- resources.ApplyResources(this.comboImage, "comboImage");
this.comboImage.FormattingEnabled = true;
this.comboImage.Items.AddRange(new object[] {
resources.GetString("comboImage.Items"),
resources.GetString("comboImage.Items1")});
+ resources.ApplyResources(this.comboImage, "comboImage");
this.comboImage.Name = "comboImage";
//
// buttonAbort
diff --git a/CUERipper/frmCUERipper.cs b/CUERipper/frmCUERipper.cs
index 1e57be6..2571ef3 100644
--- a/CUERipper/frmCUERipper.cs
+++ b/CUERipper/frmCUERipper.cs
@@ -7,21 +7,29 @@ using System.Drawing;
using System.Text;
using System.Threading;
using System.Windows.Forms;
-using CUETools.Ripper.SCSI;
+using CUETools.AccurateRip;
using CUETools.CDImage;
+using CUETools.Codecs;
+using CUETools.Processor;
+using CUETools.Ripper.SCSI;
using MusicBrainz;
namespace CUERipper
{
public partial class frmCUERipper : Form
{
- private CDDriveReader _reader = null;
private Thread _workThread = null;
+ private CDDriveReader _reader = null;
private StartStop _startStop;
+ private CUEConfig _config;
+ private OutputAudioFormat _format;
+ private CUEStyle _style;
+ private CUESheet _cueSheet;
public frmCUERipper()
{
InitializeComponent();
+ _config = new CUEConfig();
_startStop = new StartStop();
}
@@ -44,9 +52,12 @@ namespace CUERipper
private void SetupControls ()
{
bool running = _workThread != null;
- listTracks.Enabled = !running;
- comboDrives.Enabled = !running;
- comboRelease.Enabled = !running;
+ listTracks.Enabled =
+ comboDrives.Enabled =
+ comboRelease.Enabled =
+ comboCodec.Enabled =
+ comboImage.Enabled =
+ comboLossless.Enabled = !running;
buttonPause.Visible = buttonPause.Enabled = buttonAbort.Visible = buttonAbort.Enabled = running;
buttonGo.Visible = buttonGo.Enabled = !running;
toolStripStatusLabel1.Text = String.Empty;
@@ -95,26 +106,57 @@ namespace CUERipper
CDDriveReader audioSource = (CDDriveReader)o;
audioSource.ReadProgress += new EventHandler(CDReadProgress);
int[,] buff = new int[audioSource.BestBlockSize, audioSource.ChannelCount];
+ AccurateRipVerify arVerify = new AccurateRipVerify(audioSource.TOC);
+ string ArId = AccurateRipVerify.CalculateAccurateRipId(audioSource.TOC);
+
+ arVerify.ContactAccurateRip(ArId);
+
+ IAudioDest audioDest = null;
try
{
audioSource.Position = 0;
- do
+ if (_style == CUEStyle.SingleFile || _style == CUEStyle.SingleFileWithCUE)
+ audioDest = AudioReadWrite.GetAudioDest(_cueSheet.SingleFilename, (long)audioSource.Length, _config);
+
+ for (int iTrack = 0; iTrack <= audioSource.TOC.AudioTracks; iTrack++)
{
- uint toRead = Math.Min((uint)buff.GetLength(0), (uint)audioSource.Remaining);
- uint samplesRead = audioSource.Read(buff, toRead);
- if (samplesRead == 0) break;
- if (samplesRead != toRead)
- throw new Exception("samples read != samples requested");
- //arVerify.Write(buff, samplesRead);
- //audioDest.Write(buff, samplesRead);
- } while (true);
+ uint samplesRemTrack = (iTrack > 0 ? audioSource.TOC[iTrack].Length : audioSource.TOC.Pregap) * 588;
+ if (_style == CUEStyle.GapsAppended)
+ {
+ if (audioDest != null)
+ audioDest.Close();
+ audioDest = AudioReadWrite.GetAudioDest(iTrack > 0 ? _cueSheet.TrackFilenames[iTrack - 1] : _cueSheet.HTOAFilename, (long)samplesRemTrack, _config);
+ }
+ while (samplesRemTrack > 0)
+ {
+ uint toRead = Math.Min((uint)buff.GetLength(0), (uint)samplesRemTrack);
+ uint samplesRead = audioSource.Read(buff, toRead);
+ if (samplesRead != toRead)
+ throw new Exception("samples read != samples requested");
+ arVerify.Write(buff, samplesRead);
+ audioDest.Write(buff, samplesRead);
+ samplesRemTrack -= samplesRead;
+ }
+ }
+
+ if (audioDest != null)
+ audioDest.Close();
+ audioDest = null;
}
catch (StopException)
{
+ if (audioDest != null)
+ try { audioDest.Close(); }
+ catch { };
+ audioDest = null;
}
catch (Exception ex)
{
+ if (audioDest != null)
+ try { audioDest.Close(); }
+ catch { };
+ audioDest = null;
this.Invoke((MethodInvoker)delegate()
{
string message = "Exception";
@@ -134,6 +176,18 @@ namespace CUERipper
{
if (_reader == null)
return;
+
+ _config.lossyWAVHybrid = comboLossless.SelectedIndex == 1; // _cueSheet.Config?
+ _config.singleFilenameFormat = "%D - %C.cue";
+ _format = (string)comboCodec.SelectedItem == "wav" ? OutputAudioFormat.WAV :
+ (string)comboCodec.SelectedItem == "flac" ? OutputAudioFormat.FLAC :
+ (string)comboCodec.SelectedItem == "wv" ? OutputAudioFormat.WavPack :
+ (string)comboCodec.SelectedItem == "ape" ? OutputAudioFormat.APE :
+ OutputAudioFormat.NoAudio;
+ _style = comboImage.SelectedIndex == 0 ? CUEStyle.SingleFileWithCUE :
+ CUEStyle.GapsAppended;
+ _cueSheet.GenerateFilenames(_format, comboLossless.SelectedIndex != 0, ".");
+
_workThread = new Thread(Rip);
_workThread.Priority = ThreadPriority.BelowNormal;
_workThread.IsBackground = true;
@@ -174,26 +228,22 @@ namespace CUERipper
{
if (e.ListItem is string)
return;
- ReadOnlyCollection events = ((Release)e.ListItem).GetEvents();
- string year = events.Count > 0 ? events[0].Date.Substring(0, 4) + ": " : "";
- e.Value = string.Format("{0}{1} - {2}", year, ((Release)e.ListItem).GetArtist(), ((Release)e.ListItem).GetTitle());
+ CUELine date = General.FindCUELine(((CUESheet)e.ListItem).Attributes, "REM", "DATE");
+ e.Value = string.Format("{0}{1} - {2}", date != null ? date.Params[2] + ": " : "", ((CUESheet)e.ListItem).Artist, ((CUESheet)e.ListItem).Title);
}
private void comboRelease_SelectedIndexChanged(object sender, EventArgs e)
{
listTracks.Items.Clear();
if (comboRelease.SelectedItem == null || comboRelease.SelectedItem is string)
- {
- for (int i = 1; i <= _reader.TOC.AudioTracks; i++)
- listTracks.Items.Add(new ListViewItem(new string[] { "Track " + _reader.TOC[i].Number.ToString(), _reader.TOC[i].Number.ToString(), _reader.TOC[i].StartMSF, _reader.TOC[i].LengthMSF }));
return;
- }
- Release release = (Release) comboRelease.SelectedItem;
+ _cueSheet = (CUESheet)comboRelease.SelectedItem;
for (int i = 1; i <= _reader.TOC.AudioTracks; i++)
- {
- Track track = release.GetTracks()[(int)_reader.TOC[i].Number - 1];
- listTracks.Items.Add(new ListViewItem(new string[] { track.GetTitle(), _reader.TOC[i].Number.ToString(), _reader.TOC[i].StartMSF, _reader.TOC[i].LengthMSF }));
- }
+ listTracks.Items.Add(new ListViewItem(new string[] {
+ _cueSheet.Tracks[i-1].Title,
+ _reader.TOC[i].Number.ToString(),
+ _reader.TOC[i].StartMSF,
+ _reader.TOC[i].LengthMSF }));
}
private void MusicBrainz_LookupProgress(object sender, XmlRequestEventArgs e)
@@ -230,23 +280,41 @@ namespace CUERipper
CDDriveReader audioSource = (CDDriveReader)o;
ReleaseQueryParameters p = new ReleaseQueryParameters();
- p.DiscId = _reader.TOC.MusicBrainzId;
+ p.DiscId = audioSource.TOC.MusicBrainzId;
Query results = Release.Query(p);
MusicBrainzService.XmlRequest += new EventHandler(MusicBrainz_LookupProgress);
foreach (Release release in results)
{
release.GetEvents();
release.GetTracks();
+
this.BeginInvoke((MethodInvoker)delegate()
{
- comboRelease.Items.Add(release);
+ CUESheet cueSheet = new CUESheet(_config);
+ cueSheet.OpenTOC(audioSource.TOC);
+ cueSheet.Artist = release.GetArtist();
+ cueSheet.Title = release.GetTitle();
+ if (release.GetEvents().Count > 0)
+ General.SetCUELine(cueSheet.Attributes, "REM", "DATE", release.GetEvents()[0].Date.Substring(0, 4), false);
+ for (int i = 1; i <= audioSource.TOC.AudioTracks; i++)
+ {
+ Track track = release.GetTracks()[(int)audioSource.TOC[i].Number - 1];
+ cueSheet.Tracks[i - 1].Title = track.GetTitle();
+ cueSheet.Tracks[i - 1].Artist = track.GetArtist();
+ }
+ comboRelease.Items.Add(cueSheet);
});
}
MusicBrainzService.XmlRequest -= new EventHandler(MusicBrainz_LookupProgress);
this.BeginInvoke((MethodInvoker)delegate()
{
if (comboRelease.Items.Count == 0)
- comboRelease.Items.Add("MusicBrainz: not found");
+ {
+ CUESheet cueSheet = new CUESheet(_config);
+ cueSheet.OpenTOC(audioSource.TOC);
+ comboRelease.Items.Add(cueSheet);
+ // cueSheet.Tracks[i - 1].Title = "Track " + _reader.TOC[i].Number.ToString();
+ }
});
_workThread = null;
this.BeginInvoke((MethodInvoker)delegate()
diff --git a/CUERipper/frmCUERipper.resx b/CUERipper/frmCUERipper.resx
index c675e9c..f7d7834 100644
--- a/CUERipper/frmCUERipper.resx
+++ b/CUERipper/frmCUERipper.resx
@@ -270,9 +270,6 @@
6
-
- False
-
lossless
@@ -303,14 +300,11 @@
5
-
- False
-
flac
- WavPack
+ wv
wav
@@ -339,9 +333,6 @@
4
-
- False
-
image
diff --git a/CUETools.Codecs.APE/CUETools.Codecs.APE.cpp b/CUETools.Codecs.APE/CUETools.Codecs.APE.cpp
new file mode 100644
index 0000000..6542b91
--- /dev/null
+++ b/CUETools.Codecs.APE/CUETools.Codecs.APE.cpp
@@ -0,0 +1,558 @@
+// This is the main DLL file.
+
+using namespace System;
+using namespace System::Text;
+using namespace System::Collections::Generic;
+using namespace System::Collections::Specialized;
+using namespace System::Runtime::InteropServices;
+using namespace System::IO;
+using namespace APETagsDotNet;
+using namespace CUETools::Codecs;
+
+#ifndef _WAVEFORMATEX_
+#define _WAVEFORMATEX_
+
+#define BOOL int
+#define TRUE 1
+#define FALSE 0
+#define HWND long
+
+/*
+ * extended waveform format structure used for all non-PCM formats. this
+ * structure is common to all non-PCM formats.
+ */
+typedef struct tWAVEFORMATEX
+{
+ Int16 wFormatTag; /* format type */
+ Int16 nChannels; /* number of channels (i.e. mono, stereo...) */
+ Int32 nSamplesPerSec; /* sample rate */
+ Int32 nAvgBytesPerSec; /* for buffer estimation */
+ Int16 nBlockAlign; /* block size of data */
+ Int16 wBitsPerSample; /* number of bits per sample of mono data */
+ Int16 cbSize; /* the count in bytes of the size of */
+ /* extra information (after cbSize) */
+} WAVEFORMATEX, *PWAVEFORMATEX, *NPWAVEFORMATEX, *LPWAVEFORMATEX;
+
+#endif /* _WAVEFORMATEX_ */
+
+#include "All.h"
+#include "MACLib.h"
+#include "IO.h"
+
+namespace CUETools { namespace Codecs { namespace APE {
+
+ class CWinFileIO : public CIO
+ {
+ public:
+
+ // construction / destruction
+ CWinFileIO(GCHandle gchIO, GCHandle gchBuffer)
+ {
+ _gchIO = gchIO;
+ _gchBuffer = gchBuffer;
+ }
+ ~CWinFileIO()
+ {
+ }
+
+ // open / close
+ int Open(const wchar_t * pName)
+ {
+ throw gcnew Exception("CIO::Open Unsupported.");
+ }
+ int Close()
+ {
+ throw gcnew Exception("CIO::Close Unsupported.");
+ }
+
+ // read / write
+ int Read(void * pBuffer, unsigned int nBytesToRead, unsigned int * pBytesRead);
+ int Write(const void * pBuffer, unsigned int nBytesToWrite, unsigned int * pBytesWritten);
+
+ // seek
+ int Seek(int nDistance, unsigned int nMoveMode);
+
+ // other functions
+ int SetEOF()
+ {
+ throw gcnew Exception("CIO::SetEOF unsupported.");
+ }
+
+ // creation / destruction
+ int Create(const wchar_t * pName)
+ {
+ throw gcnew Exception("CIO::Create unsupported.");
+ }
+
+ int Delete()
+ {
+ throw gcnew Exception("CIO::Delete unsupported.");
+ }
+
+ // attributes
+ int GetPosition();
+ int GetSize();
+
+ int GetName(wchar_t * pBuffer)
+ {
+ throw gcnew Exception("CIO::GetName unsupported.");
+ }
+
+ private:
+ GCHandle _gchIO;
+ GCHandle _gchBuffer;
+ };
+
+ public ref class APEReader : public IAudioSource
+ {
+ public:
+ APEReader(String^ path, Stream^ IO)
+ {
+ pAPEDecompress = NULL;
+ _sampleOffset = 0;
+ _bufferOffset = 0;
+ _bufferLength = 0;
+ _path = path;
+
+ int nRetVal = 0;
+
+ _IO = (IO != nullptr) ? IO : gcnew FileStream (path, FileMode::Open, FileAccess::Read, FileShare::Read);
+ _readBuffer = gcnew array(0x4000);
+ _gchIO = GCHandle::Alloc(_IO);
+ _gchReadBuffer = GCHandle::Alloc(_readBuffer);
+ _winFileIO = new CWinFileIO(_gchIO, _gchReadBuffer);
+ pAPEDecompress = CreateIAPEDecompressEx (_winFileIO, &nRetVal);
+ if (!pAPEDecompress) {
+ throw gcnew Exception("Unable to open file.");
+ }
+
+ _sampleRate = pAPEDecompress->GetInfo (APE_INFO_SAMPLE_RATE, 0, 0);
+ _bitsPerSample = pAPEDecompress->GetInfo (APE_INFO_BITS_PER_SAMPLE, 0, 0);
+ _channelCount = pAPEDecompress->GetInfo (APE_INFO_CHANNELS, 0, 0);
+
+ // make a buffer to hold 16384 blocks of audio data
+ nBlockAlign = pAPEDecompress->GetInfo (APE_INFO_BLOCK_ALIGN, 0, 0);
+ _samplesBuffer = gcnew array (16384 * nBlockAlign);
+
+ // loop through the whole file
+ _sampleCount = pAPEDecompress->GetInfo (APE_DECOMPRESS_TOTAL_BLOCKS, 0, 0); // * ?
+ }
+
+ ~APEReader ()
+ {
+ if (_winFileIO)
+ delete _winFileIO;
+ if (_gchIO.IsAllocated)
+ _gchIO.Free();
+ if (_gchReadBuffer.IsAllocated)
+ _gchReadBuffer.Free();
+ }
+
+ virtual property Int32 BitsPerSample {
+ Int32 get() {
+ return _bitsPerSample;
+ }
+ }
+
+ virtual property Int32 ChannelCount {
+ Int32 get() {
+ return _channelCount;
+ }
+ }
+
+ virtual property Int32 SampleRate {
+ Int32 get() {
+ return _sampleRate;
+ }
+ }
+
+ virtual property UInt64 Length {
+ UInt64 get() {
+ return _sampleCount;
+ }
+ }
+
+ virtual property UInt64 Position
+ {
+ UInt64 get() {
+ return _sampleOffset - SamplesInBuffer;
+ }
+ void set(UInt64 offset) {
+ _sampleOffset = offset;
+ _bufferOffset = 0;
+ _bufferLength = 0;
+ if (pAPEDecompress->Seek ((int) offset /*? */))
+ throw gcnew Exception("Unable to seek.");
+ }
+ }
+
+ virtual property UInt64 Remaining {
+ UInt64 get() {
+ return _sampleCount - _sampleOffset + SamplesInBuffer;
+ }
+ }
+
+ virtual void Close()
+ {
+ if (pAPEDecompress)
+ {
+ delete pAPEDecompress;
+ pAPEDecompress = NULL;
+ }
+ if (_IO != nullptr)
+ {
+ _IO->Close ();
+ _IO = nullptr;
+ }
+ }
+
+ virtual property String^ Path {
+ String^ get() {
+ return _path;
+ }
+ }
+
+ virtual property NameValueCollection^ Tags {
+ NameValueCollection^ get ()
+ {
+ if (!_tags)
+ {
+ APETagDotNet^ apeTag = gcnew APETagDotNet (_IO, true);
+ _tags = apeTag->GetStringTags (true);
+ apeTag->Close ();
+ }
+ return _tags;
+ }
+ void set (NameValueCollection ^tags)
+ {
+ _tags = tags;
+ }
+ }
+
+ virtual bool UpdateTags(bool preserveTime)
+ {
+ Close ();
+ APETagDotNet^ apeTag = gcnew APETagDotNet (_path, true, false);
+ apeTag->SetStringTags (_tags, true);
+ apeTag->Save();
+ apeTag->Close();
+ return true;
+ }
+
+ virtual UInt32 Read([Out] array^ buff, UInt32 sampleCount)
+ {
+ UInt32 buffOffset = 0;
+ UInt32 samplesNeeded = sampleCount;
+
+ while (samplesNeeded != 0)
+ {
+ if (SamplesInBuffer == 0)
+ {
+ int nBlocksRetrieved;
+ pin_ptr pSampleBuffer = &_samplesBuffer[0];
+ if (pAPEDecompress->GetData ((char *) pSampleBuffer, 16384, &nBlocksRetrieved))
+ throw gcnew Exception("An error occurred while decoding.");
+ _bufferOffset = 0;
+ _bufferLength = nBlocksRetrieved;
+ _sampleOffset += nBlocksRetrieved;
+ }
+ UInt32 copyCount = Math::Min(samplesNeeded, SamplesInBuffer);
+ AudioSamples::BytesToFLACSamples_16(_samplesBuffer, _bufferOffset*nBlockAlign, buff, buffOffset, copyCount, _channelCount);
+ samplesNeeded -= copyCount;
+ buffOffset += copyCount;
+ _bufferOffset += copyCount;
+ }
+ return sampleCount;
+ }
+
+ private:
+ IAPEDecompress * pAPEDecompress;
+
+ NameValueCollection^ _tags;
+ Int64 _sampleCount, _sampleOffset;
+ Int32 _bitsPerSample, _channelCount, _sampleRate;
+ UInt32 _bufferOffset, _bufferLength;
+ int nBlockAlign;
+ array^ _samplesBuffer;
+ String^ _path;
+ Stream^ _IO;
+ array^ _readBuffer;
+ CWinFileIO* _winFileIO;
+ GCHandle _gchIO, _gchReadBuffer;
+
+ property UInt32 SamplesInBuffer
+ {
+ UInt32 get ()
+ {
+ return (UInt32) (_bufferLength - _bufferOffset);
+ }
+ }
+ };
+
+ public ref class APEWriter : IAudioDest
+ {
+ public:
+ APEWriter(String^ path, Int32 bitsPerSample, Int32 channelCount, Int32 sampleRate)
+ {
+ if (channelCount != 1 && channelCount != 2)
+ throw gcnew Exception("Only stereo and mono audio formats are allowed.");
+ if (bitsPerSample != 16 && bitsPerSample != 24)
+ throw gcnew Exception("Monkey's Audio doesn't support selected bits per sample value.");
+
+ _path = path;
+ _tags = gcnew NameValueCollection();
+ _winFileIO = NULL;
+
+ _compressionLevel = COMPRESSION_LEVEL_NORMAL;
+
+ _bitsPerSample = bitsPerSample;
+ _channelCount = channelCount;
+ _sampleRate = sampleRate;
+ _blockAlign = _channelCount * ((_bitsPerSample + 7) / 8);
+
+ int nRetVal;
+ pAPECompress = CreateIAPECompress (&nRetVal);
+ if (!pAPECompress)
+ throw gcnew Exception("Unable to open APE compressor.");
+ }
+
+ ~APEWriter()
+ {
+ if (_winFileIO)
+ delete _winFileIO;
+ if (_gchIO.IsAllocated)
+ _gchIO.Free();
+ if (_gchBuffer.IsAllocated)
+ _gchBuffer.Free();
+ }
+
+ virtual void Close()
+ {
+ if (pAPECompress)
+ {
+ pAPECompress->Finish (NULL, 0, 0);
+ delete pAPECompress;
+ pAPECompress = NULL;
+ }
+
+ if ((_finalSampleCount != 0) && (_samplesWritten != _finalSampleCount)) {
+ throw gcnew Exception("Samples written differs from the expected sample count.");
+ }
+
+ if (_tags->Count > 0)
+ {
+ APETagDotNet^ apeTag = gcnew APETagDotNet (_IO, true);
+ apeTag->SetStringTags (_tags, true);
+ apeTag->Save();
+ apeTag->Close();
+ _tags->Clear ();
+ }
+
+ if (_IO != nullptr)
+ {
+ _IO->Close ();
+ _IO = nullptr;
+ }
+ }
+
+ virtual void Delete()
+ {
+ try { Close (); } catch (Exception^) {}
+ File::Delete(_path);
+ }
+
+ virtual property Int64 FinalSampleCount
+ {
+ Int64 get()
+ {
+ return _finalSampleCount;
+ }
+ void set(Int64 value)
+ {
+ if (value < 0)
+ throw gcnew Exception("Invalid final sample count.");
+ if (_initialized)
+ throw gcnew Exception("Final sample count cannot be changed after encoding begins.");
+ _finalSampleCount = value;
+ }
+ }
+
+ virtual property Int64 BlockSize
+ {
+ void set(Int64 value)
+ {
+ }
+ }
+
+ virtual property int BitsPerSample
+ {
+ int get() { return _bitsPerSample; }
+ }
+
+ virtual void Write(array^ buff, UInt32 sampleCount)
+ {
+ if (_sampleBuffer == nullptr || _sampleBuffer.Length < sampleCount * _blockAlign)
+ _sampleBuffer = gcnew array(sampleCount * _blockAlign);
+ AudioSamples::FLACSamplesToBytes(buff, 0, _sampleBuffer, 0, sampleCount, _channelCount, _bitsPerSample);
+ if (!_initialized) Initialize();
+ pin_ptr pSampleBuffer = &_sampleBuffer[0];
+ if (pAPECompress->AddData (pSampleBuffer, sampleCount * _blockAlign))
+ throw gcnew Exception("An error occurred while encoding.");
+ _samplesWritten += sampleCount;
+ }
+
+ virtual property String^ Path
+ {
+ String^ get() {
+ return _path;
+ }
+ }
+
+ virtual bool SetTags (NameValueCollection^ tags)
+ {
+ _tags = tags;
+ return true;
+ }
+
+ property Int32 CompressionLevel {
+ Int32 get() {
+ return _compressionLevel;
+ }
+ void set(Int32 value) {
+ if ((value < 1) || (value > 5)) {
+ throw gcnew Exception("Invalid compression mode.");
+ }
+ _compressionLevel = value * 1000;
+ }
+ }
+
+ private:
+ IAPECompress * pAPECompress;
+ bool _initialized;
+ Int32 _finalSampleCount, _samplesWritten;
+ Int32 _bitsPerSample, _channelCount, _sampleRate, _blockAlign;
+ Int32 _compressionLevel;
+ NameValueCollection^ _tags;
+ String^ _path;
+ Stream^ _IO;
+ GCHandle _gchIO, _gchBuffer;
+ CWinFileIO* _winFileIO;
+ array^ _writeBuffer;
+ array^ _sampleBuffer;
+
+ void Initialize() {
+ _IO = gcnew FileStream (_path, FileMode::Create, FileAccess::ReadWrite, FileShare::Read);
+ _writeBuffer = gcnew array(0x4000);
+
+ _gchIO = GCHandle::Alloc(_IO);
+ _gchBuffer = GCHandle::Alloc(_writeBuffer);
+ _winFileIO = new CWinFileIO(_gchIO, _gchBuffer);
+
+ WAVEFORMATEX waveFormat;
+ FillWaveFormatEx (&waveFormat, _sampleRate, _bitsPerSample, _channelCount);
+
+ int res = pAPECompress->StartEx (_winFileIO,
+ &waveFormat,
+ (_finalSampleCount == 0) ? MAX_AUDIO_BYTES_UNKNOWN : _finalSampleCount * _blockAlign,
+ _compressionLevel,
+ NULL,
+ CREATE_WAV_HEADER_ON_DECOMPRESSION);
+ if (res)
+ throw gcnew Exception("Unable to create the encoder.");
+
+ _initialized = true;
+ }
+ };
+
+ int CWinFileIO::Read(void * pBuffer, unsigned int nBytesToRead, unsigned int * pBytesRead)
+ {
+ array^ buff = (array^) _gchBuffer.Target;
+ if (buff->Length < nBytesToRead)
+ {
+ Array::Resize (buff, nBytesToRead);
+ _gchBuffer.Target = buff;
+ }
+ int len = ((Stream^)_gchIO.Target)->Read (buff, 0, nBytesToRead);
+ if (len) Marshal::Copy (buff, 0, (IntPtr)pBuffer, len);
+ *pBytesRead = len;
+ return 0;
+ }
+
+ int CWinFileIO::Write(const void * pBuffer, unsigned int nBytesToWrite, unsigned int * pBytesWritten)
+ {
+ array^ buff = (array^) _gchBuffer.Target;
+ if (buff->Length < nBytesToWrite)
+ {
+ Array::Resize (buff, nBytesToWrite);
+ _gchBuffer.Target = buff;
+ }
+ Marshal::Copy ((IntPtr)(void*)pBuffer, buff, 0, nBytesToWrite);
+ ((Stream^)_gchIO.Target)->Write (buff, 0, nBytesToWrite);
+ *pBytesWritten = nBytesToWrite;
+ return 0;
+ }
+
+ int CWinFileIO::GetPosition()
+ {
+ return ((Stream^)_gchIO.Target)->Position;
+ }
+
+ int CWinFileIO::GetSize()
+ {
+ return ((Stream^)_gchIO.Target)->Length;
+ }
+
+ int CWinFileIO::Seek(int delta, unsigned int mode)
+ {
+ switch (mode)
+ {
+ case FILE_BEGIN:
+ ((Stream^)_gchIO.Target)->Seek (delta, System::IO::SeekOrigin::Begin);
+ break;
+ case FILE_END:
+ ((Stream^)_gchIO.Target)->Seek (delta, System::IO::SeekOrigin::End);
+ break;
+ case FILE_CURRENT:
+ ((Stream^)_gchIO.Target)->Seek (delta, System::IO::SeekOrigin::Current);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+ }
+
+#if 0
+extern "C"
+{
+ BOOL GetMMXAvailable();
+ int CalculateDotProduct8(const short* pA, const short* pB, int nOrder);
+};
+
+ public ref class SSE2Functions
+ {
+ public:
+ SSE2Functions ()
+ {
+ _haveSSE2 = GetMMXAvailable();
+ }
+ int SumInts (short*a, short*b, int count)
+ {
+ if (_haveSSE2 && count == 8)
+ return CalculateDotProduct8(a, b, count);
+ int sum = 0;
+ for (int j = 0; j < count; j++)
+ sum += a[j] * b[j];
+ return sum;
+ }
+ int SumInts (int*a, short*b, int count)
+ {
+ int sum = 0;
+ for (int j = 0; j < count; j++)
+ sum += a[j] * b[j];
+ return sum;
+ }
+ private:
+ bool _haveSSE2;
+ };
+#endif
+}}}
diff --git a/CUETools.Codecs.FLAC/AssemblyInfo.cpp b/CUETools.Codecs.FLAC/AssemblyInfo.cpp
index ff2a0c9..b4a2e61 100644
--- a/CUETools.Codecs.FLAC/AssemblyInfo.cpp
+++ b/CUETools.Codecs.FLAC/AssemblyInfo.cpp
@@ -10,7 +10,7 @@ using namespace System::Security::Permissions;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
-[assembly:AssemblyTitleAttribute("FLACDotNet")];
+[assembly:AssemblyTitleAttribute("CUETools.Codecs.FLAC")];
[assembly:AssemblyDescriptionAttribute("")];
[assembly:AssemblyConfigurationAttribute("")];
[assembly:AssemblyCompanyAttribute("")];
diff --git a/CUETools.Codecs.LossyWAV/LossyWAV.cs b/CUETools.Codecs.LossyWAV/LossyWAV.cs
index a075711..0784e74 100644
--- a/CUETools.Codecs.LossyWAV/LossyWAV.cs
+++ b/CUETools.Codecs.LossyWAV/LossyWAV.cs
@@ -85,8 +85,10 @@ namespace CUETools.Codecs.LossyWAV
process_this_codec_block();
}
}
+ if (_lwcdfDest != null)
+ try { _lwcdfDest.Close(); }
+ catch { }
if (_audioDest != null) _audioDest.Close();
- if (_lwcdfDest != null) _lwcdfDest.Close();
}
public void Write(int[,] buff, uint sampleCount)
diff --git a/CUETools.Codecs.WavPack/CUETools.Codecs.WavPack.cpp b/CUETools.Codecs.WavPack/CUETools.Codecs.WavPack.cpp
new file mode 100644
index 0000000..5d6434c
--- /dev/null
+++ b/CUETools.Codecs.WavPack/CUETools.Codecs.WavPack.cpp
@@ -0,0 +1,561 @@
+// ****************************************************************************
+//
+// Copyright (c) 2006-2007 Moitah (moitah@yahoo.com)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the author nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// ****************************************************************************
+
+using namespace System;
+using namespace System::Runtime::InteropServices;
+using namespace System::Collections::Specialized;
+using namespace System::Security::Cryptography;
+using namespace System::IO;
+using namespace APETagsDotNet;
+using namespace CUETools::Codecs;
+
+#include
+#include
+#include "wavpack.h"
+#include
+
+namespace CUETools { namespace Codecs { namespace WavPack {
+ int write_block(void *id, void *data, int32_t length);
+
+ [UnmanagedFunctionPointer(CallingConvention::Cdecl)]
+ public delegate int32_t DecoderReadDelegate(void *id, void *data, int32_t bcount);
+ [UnmanagedFunctionPointer(CallingConvention::Cdecl)]
+ public delegate uint32_t DecoderTellDelegate(void *id);
+ [UnmanagedFunctionPointer(CallingConvention::Cdecl)]
+ public delegate int DecoderSeekDelegate(void *id, uint32_t pos);
+ [UnmanagedFunctionPointer(CallingConvention::Cdecl)]
+ public delegate int DecoderSeekRelativeDelegate(void *id, int32_t delta, int mode);
+ [UnmanagedFunctionPointer(CallingConvention::Cdecl)]
+ public delegate int DecoderPushBackDelegate(void *id, int c);
+ [UnmanagedFunctionPointer(CallingConvention::Cdecl)]
+ public delegate uint32_t DecoderLengthDelegate(void *id);
+ [UnmanagedFunctionPointer(CallingConvention::Cdecl)]
+ public delegate int DecoderCanSeekDelegate(void *id);
+
+ public ref class WavPackReader : public IAudioSource
+ {
+ public:
+ WavPackReader(String^ path, Stream^ IO, Stream^ IO_WVC)
+ {
+ char errorMessage[256];
+
+ _readDel = gcnew DecoderReadDelegate (this, &WavPackReader::ReadCallback);
+ _tellDel = gcnew DecoderTellDelegate (this, &WavPackReader::TellCallback);
+ _seekDel = gcnew DecoderSeekDelegate (this, &WavPackReader::SeekCallback);
+ _seekRelDel = gcnew DecoderSeekRelativeDelegate (this, &WavPackReader::SeekRelCallback);
+ _pushBackDel = gcnew DecoderPushBackDelegate (this, &WavPackReader::PushBackCallback);
+ _lengthDel = gcnew DecoderLengthDelegate (this, &WavPackReader::LengthCallback);
+ _canSeekDel = gcnew DecoderCanSeekDelegate (this, &WavPackReader::CanSeekCallback);
+
+ ioReader = new WavpackStreamReader;
+ ioReader->read_bytes = (int32_t (*)(void *, void *, int32_t)) Marshal::GetFunctionPointerForDelegate(_readDel).ToPointer();
+ ioReader->get_pos = (uint32_t (*)(void *)) Marshal::GetFunctionPointerForDelegate(_tellDel).ToPointer();
+ ioReader->set_pos_abs = (int (*)(void *, uint32_t)) Marshal::GetFunctionPointerForDelegate(_seekDel).ToPointer();
+ ioReader->set_pos_rel = (int (*)(void *, int32_t, int)) Marshal::GetFunctionPointerForDelegate(_seekRelDel).ToPointer();
+ ioReader->push_back_byte = (int (*)(void *, int)) Marshal::GetFunctionPointerForDelegate(_pushBackDel).ToPointer();
+ ioReader->get_length = (uint32_t (*)(void *)) Marshal::GetFunctionPointerForDelegate(_lengthDel).ToPointer();
+ ioReader->can_seek = (int (*)(void *)) Marshal::GetFunctionPointerForDelegate(_canSeekDel).ToPointer();
+ ioReader->write_bytes = NULL;
+
+ _IO_ungetc = _IO_WVC_ungetc = -1;
+
+ _path = path;
+
+ _IO = (IO != nullptr) ? IO : gcnew FileStream (path, FileMode::Open, FileAccess::Read, FileShare::Read);
+ _IO_WVC = (IO != nullptr) ? IO_WVC : System::IO::File::Exists (path+"c") ? gcnew FileStream (path+"c", FileMode::Open, FileAccess::Read, FileShare::Read) : nullptr;
+
+ _wpc = WavpackOpenFileInputEx (ioReader, "v", _IO_WVC != nullptr ? "c" : NULL, errorMessage, OPEN_WVC, 0);
+ if (_wpc == NULL) {
+ throw gcnew Exception("Unable to initialize the decoder.");
+ }
+
+ _bitsPerSample = WavpackGetBitsPerSample(_wpc);
+ _channelCount = WavpackGetNumChannels(_wpc);
+ _sampleRate = WavpackGetSampleRate(_wpc);
+ _sampleCount = WavpackGetNumSamples(_wpc);
+ _sampleOffset = 0;
+ }
+
+ ~WavPackReader()
+ {
+ delete ioReader;
+ }
+
+ virtual property Int32 BitsPerSample {
+ Int32 get() {
+ return _bitsPerSample;
+ }
+ }
+
+ virtual property Int32 ChannelCount {
+ Int32 get() {
+ return _channelCount;
+ }
+ }
+
+ virtual property Int32 SampleRate {
+ Int32 get() {
+ return _sampleRate;
+ }
+ }
+
+ virtual property UInt64 Length {
+ UInt64 get() {
+ return _sampleCount;
+ }
+ }
+
+ virtual property UInt64 Position {
+ UInt64 get() {
+ return _sampleOffset;
+ }
+ void set(UInt64 offset) {
+ _sampleOffset = offset;
+ if (!WavpackSeekSample(_wpc, offset)) {
+ throw gcnew Exception("Unable to seek.");
+ }
+ }
+ }
+
+ virtual property UInt64 Remaining {
+ UInt64 get() {
+ return _sampleCount - _sampleOffset;
+ }
+ }
+
+ virtual property String^ Path {
+ String^ get() {
+ return _path;
+ }
+ }
+
+ virtual property NameValueCollection^ Tags {
+ NameValueCollection^ get () {
+ if (!_tags)
+ {
+ APETagDotNet^ apeTag = gcnew APETagDotNet (_IO, true);
+ _tags = apeTag->GetStringTags (true);
+ apeTag->Close ();
+ }
+ return _tags;
+ }
+ void set (NameValueCollection ^tags) {
+ _tags = tags;
+ }
+ }
+
+ virtual bool UpdateTags(bool preserveTime)
+ {
+ Close ();
+ APETagDotNet^ apeTag = gcnew APETagDotNet (_path, true, false);
+ apeTag->SetStringTags (_tags, true);
+ apeTag->Save();
+ apeTag->Close();
+ return true;
+ }
+
+ virtual void Close()
+ {
+ if (_wpc != NULL)
+ _wpc = WavpackCloseFile(_wpc);
+ if (_IO != nullptr)
+ {
+ _IO->Close ();
+ _IO = nullptr;
+ }
+ if (_IO_WVC != nullptr)
+ {
+ _IO_WVC->Close ();
+ _IO_WVC = nullptr;
+ }
+ }
+
+ virtual UInt32 Read(array^ sampleBuffer, UInt32 sampleCount)
+ {
+ pin_ptr pSampleBuffer = &sampleBuffer[0, 0];
+ int samplesRead = WavpackUnpackSamples(_wpc, pSampleBuffer, sampleCount);
+ _sampleOffset += samplesRead;
+ if (samplesRead != sampleCount)
+ throw gcnew Exception("Decoder returned a different number of samples than requested.");
+ return sampleCount;
+ }
+
+ private:
+ WavpackContext *_wpc;
+ NameValueCollection^ _tags;
+ Int32 _sampleCount, _sampleOffset;
+ Int32 _bitsPerSample, _channelCount, _sampleRate;
+ String^ _path;
+ Stream^ _IO;
+ Stream^ _IO_WVC;
+ DecoderReadDelegate^ _readDel;
+ DecoderTellDelegate^ _tellDel;
+ DecoderSeekDelegate^ _seekDel;
+ DecoderSeekRelativeDelegate^ _seekRelDel;
+ DecoderPushBackDelegate^ _pushBackDel;
+ DecoderLengthDelegate^ _lengthDel;
+ DecoderCanSeekDelegate^ _canSeekDel;
+ array^ _readBuffer;
+ int _IO_ungetc, _IO_WVC_ungetc;
+ WavpackStreamReader* ioReader;
+
+ int32_t ReadCallback (void *id, void *data, int32_t bcount)
+ {
+ Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
+ int IO_ungetc = (*(char*)id=='c') ? _IO_WVC_ungetc : _IO_ungetc;
+ int unget_len = 0;
+
+ if (IO_ungetc != -1)
+ {
+ *(unsigned char*)data = (unsigned char) IO_ungetc;
+ if (IO == _IO)
+ _IO_ungetc = -1;
+ else
+ _IO_WVC_ungetc = -1;
+ bcount --;
+ if (!bcount)
+ return 1;
+ data = 1 + (unsigned char*)data;
+ unget_len = 1;
+ }
+
+ if (_readBuffer == nullptr || _readBuffer->Length < bcount)
+ _readBuffer = gcnew array(bcount < 0x4000 ? 0x4000 : bcount);
+ int len = IO->Read (_readBuffer, 0, bcount);
+ if (len) Marshal::Copy (_readBuffer, 0, (IntPtr)data, len);
+ return len + unget_len;
+ }
+
+ uint32_t TellCallback(void *id)
+ {
+ Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
+ return IO->Position;
+ }
+
+ int SeekCallback (void *id, uint32_t pos)
+ {
+ Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
+ IO->Position = pos;
+ return 0;
+ }
+
+ int SeekRelCallback (void *id, int32_t delta, int mode)
+ {
+ Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
+ switch (mode)
+ {
+ case SEEK_SET:
+ IO->Seek (delta, System::IO::SeekOrigin::Begin);
+ break;
+ case SEEK_END:
+ IO->Seek (delta, System::IO::SeekOrigin::End);
+ break;
+ case SEEK_CUR:
+ IO->Seek (delta, System::IO::SeekOrigin::Current);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+ }
+
+ int PushBackCallback (void *id, int c)
+ {
+ Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
+ if (IO == _IO)
+ {
+ if (_IO_ungetc != -1)
+ throw gcnew Exception("Double PushBackCallback unsupported.");
+ _IO_ungetc = c;
+ } else
+ {
+ if (_IO_WVC_ungetc != -1)
+ throw gcnew Exception("Double PushBackCallback unsupported.");
+ _IO_WVC_ungetc = c;
+ }
+ }
+
+ uint32_t LengthCallback (void *id)
+ {
+ Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
+ return IO->Length;
+ }
+
+ int CanSeekCallback(void *id)
+ {
+ Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
+ return IO->CanSeek;
+ }
+ };
+
+ public ref class WavPackWriter : IAudioDest
+ {
+ public:
+ WavPackWriter(String^ path, Int32 bitsPerSample, Int32 channelCount, Int32 sampleRate)
+ {
+ IntPtr pathChars;
+
+ if (channelCount != 1 && channelCount != 2)
+ throw gcnew Exception("Only stereo and mono audio formats are allowed.");
+ if (bitsPerSample < 16 || bitsPerSample > 24)
+ throw gcnew Exception("Bits per sample must be 16..24.");
+
+ _path = path;
+ _tags = gcnew NameValueCollection();
+
+ _compressionMode = 1;
+ _extraMode = 0;
+ _blockSize = 0;
+
+ _bitsPerSample = bitsPerSample;
+ _channelCount = channelCount;
+ _sampleRate = sampleRate;
+ _blockAlign = _channelCount * ((_bitsPerSample + 7) / 8);
+
+ pathChars = Marshal::StringToHGlobalUni(path);
+ _hFile = _wfopen((const wchar_t*)pathChars.ToPointer(), L"w+b");
+ Marshal::FreeHGlobal(pathChars);
+ if (!_hFile) {
+ throw gcnew Exception("Unable to open file.");
+ }
+ }
+
+ virtual void Close()
+ {
+ if (_md5Sum)
+ {
+ _md5hasher->TransformFinalBlock (gcnew array(1), 0, 0);
+ pin_ptr md5_digest = &_md5hasher->Hash[0];
+ WavpackStoreMD5Sum (_wpc, md5_digest);
+ }
+
+ WavpackFlushSamples(_wpc);
+ _wpc = WavpackCloseFile(_wpc);
+ fclose(_hFile);
+
+ if ((_finalSampleCount != 0) && (_samplesWritten != _finalSampleCount)) {
+ throw gcnew Exception("Samples written differs from the expected sample count.");
+ }
+
+ if (_tags->Count > 0)
+ {
+ APETagDotNet^ apeTag = gcnew APETagDotNet (_path, true, false);
+ apeTag->SetStringTags (_tags, true);
+ apeTag->Save();
+ apeTag->Close();
+ _tags->Clear ();
+ }
+ }
+
+ virtual void Delete()
+ {
+ try { Close (); } catch (Exception^) {}
+ File::Delete(_path);
+ }
+
+ virtual property Int64 FinalSampleCount
+ {
+ Int64 get()
+ {
+ return _finalSampleCount;
+ }
+ void set(Int64 value)
+ {
+ if (value < 0)
+ throw gcnew Exception("Invalid final sample count.");
+ if (_initialized)
+ throw gcnew Exception("Final sample count cannot be changed after encoding begins.");
+ _finalSampleCount = value;
+ }
+ }
+
+ virtual property Int64 BlockSize
+ {
+ void set(Int64 value)
+ {
+ _blockSize = value;
+ }
+ }
+
+ virtual property int BitsPerSample
+ {
+ int get() { return _bitsPerSample; }
+ }
+
+ virtual void Write(array^ sampleBuffer, UInt32 sampleCount)
+ {
+ if (!_initialized)
+ Initialize();
+
+ if (MD5Sum)
+ {
+ if (_sampleBuffer == nullptr || _sampleBuffer.Length < sampleCount * _blockAlign)
+ _sampleBuffer = gcnew array(sampleCount * _blockAlign);
+ AudioSamples::FLACSamplesToBytes(sampleBuffer, 0, _sampleBuffer, 0, sampleCount, _channelCount, _bitsPerSample);
+ UpdateHash(_sampleBuffer, (int) sampleCount * _blockAlign);
+ }
+
+ if ((_bitsPerSample & 7) != 0)
+ {
+ if (_shiftedSampleBuffer == nullptr || _shiftedSampleBuffer.GetLength(0) < sampleCount)
+ _shiftedSampleBuffer = gcnew array(sampleCount, _channelCount);
+ for (int i = 0; i < sampleCount; i++)
+ for (int c = 0; c < _channelCount; c++)
+ _shiftedSampleBuffer[i,c] = sampleBuffer[i,c] << 8 - (_bitsPerSample & 7);
+ pin_ptr pSampleBuffer = &_shiftedSampleBuffer[0, 0];
+ if (!WavpackPackSamples(_wpc, (int32_t*)pSampleBuffer, sampleCount))
+ throw gcnew Exception("An error occurred while encoding.");
+ } else
+ {
+ pin_ptr pSampleBuffer = &sampleBuffer[0, 0];
+ if (!WavpackPackSamples(_wpc, (int32_t*)pSampleBuffer, sampleCount))
+ throw gcnew Exception("An error occurred while encoding.");
+ }
+
+ _samplesWritten += sampleCount;
+ }
+
+ virtual property String^ Path
+ {
+ String^ get() {
+ return _path;
+ }
+ }
+
+ virtual bool SetTags (NameValueCollection^ tags)
+ {
+ _tags = tags;
+ return true;
+ }
+
+ property Int32 CompressionMode {
+ Int32 get() {
+ return _compressionMode;
+ }
+ void set(Int32 value) {
+ if ((value < 0) || (value > 3)) {
+ throw gcnew Exception("Invalid compression mode.");
+ }
+ _compressionMode = value;
+ }
+ }
+
+ property Int32 ExtraMode {
+ Int32 get() {
+ return _extraMode;
+ }
+ void set(Int32 value) {
+ if ((value < 0) || (value > 6)) {
+ throw gcnew Exception("Invalid extra mode.");
+ }
+ _extraMode = value;
+ }
+ }
+
+ property bool MD5Sum {
+ bool get() {
+ return _md5Sum;
+ }
+ void set(bool value) {
+ _md5Sum = value;
+ }
+ }
+
+ void UpdateHash(array^ buff, Int32 len)
+ {
+ if (!_initialized) Initialize();
+
+ if (!_md5Sum || !_md5hasher)
+ throw gcnew Exception("MD5 not enabled.");
+ _md5hasher->TransformBlock (buff, 0, len, buff, 0);
+ }
+
+ private:
+ FILE *_hFile;
+ bool _initialized;
+ WavpackContext *_wpc;
+ Int32 _finalSampleCount, _samplesWritten;
+ Int32 _bitsPerSample, _channelCount, _sampleRate, _blockAlign;
+ Int32 _compressionMode, _extraMode, _blockSize;
+ NameValueCollection^ _tags;
+ String^ _path;
+ bool _md5Sum;
+ MD5^ _md5hasher;
+ array^ _sampleBuffer;
+ array^ _shiftedSampleBuffer;
+
+ void Initialize() {
+ WavpackConfig config;
+
+ _wpc = WavpackOpenFileOutput(write_block, _hFile, NULL);
+ if (!_wpc) {
+ throw gcnew Exception("Unable to create the encoder.");
+ }
+
+ memset(&config, 0, sizeof(WavpackConfig));
+ config.bits_per_sample = _bitsPerSample;
+ config.bytes_per_sample = (_bitsPerSample + 7) / 8;
+ config.num_channels = _channelCount;
+ config.channel_mask = 5 - _channelCount;
+ config.sample_rate = _sampleRate;
+ if (_compressionMode == 0) config.flags |= CONFIG_FAST_FLAG;
+ if (_compressionMode == 2) config.flags |= CONFIG_HIGH_FLAG;
+ if (_compressionMode == 3) config.flags |= CONFIG_HIGH_FLAG | CONFIG_VERY_HIGH_FLAG;
+ if (_extraMode != 0) {
+ config.flags |= CONFIG_EXTRA_MODE;
+ config.xmode = _extraMode;
+ }
+ if (_md5Sum)
+ {
+ _md5hasher = gcnew MD5CryptoServiceProvider ();
+ config.flags |= CONFIG_MD5_CHECKSUM;
+ }
+ config.block_samples = (int)_blockSize;
+ if (_blockSize > 0 && _blockSize < 2048)
+ config.flags |= CONFIG_MERGE_BLOCKS;
+
+ if (!WavpackSetConfiguration(_wpc, &config, (_finalSampleCount == 0) ? -1 : _finalSampleCount)) {
+ throw gcnew Exception("Invalid configuration setting.");
+ }
+
+ if (!WavpackPackInit(_wpc)) {
+ throw gcnew Exception("Unable to initialize the encoder.");
+ }
+
+ _initialized = true;
+ }
+ };
+
+#pragma unmanaged
+ int write_block(void *id, void *data, int32_t length) {
+ return (fwrite(data, 1, length, (FILE*)id) == length);
+ }
+}}}
diff --git a/CUETools.Processor/AudioReadWrite.cs b/CUETools.Processor/AudioReadWrite.cs
index a363fab..45e6a7d 100644
--- a/CUETools.Processor/AudioReadWrite.cs
+++ b/CUETools.Processor/AudioReadWrite.cs
@@ -1,10 +1,10 @@
using System;
using System.IO;
-using FLACDotNet;
-using WavPackDotNet;
-using APEDotNet;
using CUETools.Codecs;
using CUETools.Codecs.ALAC;
+using CUETools.Codecs.FLAC;
+using CUETools.Codecs.WavPack;
+using CUETools.Codecs.APE;
using CUETools.Codecs.LossyWAV;
using System.Collections.Generic;
using System.Collections.Specialized;
@@ -95,7 +95,7 @@ namespace CUETools.Processor
int destBitsPerSample = (config.detectHDCD && config.decodeHDCD) ? ((!config.decodeHDCDtoLW16 && config.decodeHDCDto24bit) ? 24 : 20) : 16;
int lossyBitsPerSample = (config.detectHDCD && config.decodeHDCD && !config.decodeHDCDtoLW16) ? 24 : 16;
IAudioDest lossyDest = GetAudioDest(path, lossyBitsPerSample, 2, 44100, finalSampleCount, extension, config);
- IAudioDest lwcdfDest = GetAudioDest(lwcdfPath, destBitsPerSample, 2, 44100, finalSampleCount, extension, config);
+ IAudioDest lwcdfDest = config.lossyWAVHybrid ? GetAudioDest(lwcdfPath, destBitsPerSample, 2, 44100, finalSampleCount, extension, config) : null;
return new LossyWAVWriter(lossyDest, lwcdfDest, destBitsPerSample, 2, 44100, config.lossyWAVQuality);
}
}
diff --git a/CUETools.Processor/CUETools.Processor.csproj b/CUETools.Processor/CUETools.Processor.csproj
index bf5a9c0..58586db 100644
--- a/CUETools.Processor/CUETools.Processor.csproj
+++ b/CUETools.Processor/CUETools.Processor.csproj
@@ -84,7 +84,7 @@
-
+
diff --git a/CUETools.Processor/Main.cs b/CUETools.Processor/Processor.cs
similarity index 69%
rename from CUETools.Processor/Main.cs
rename to CUETools.Processor/Processor.cs
index 176118d..853f4b9 100644
--- a/CUETools.Processor/Main.cs
+++ b/CUETools.Processor/Processor.cs
@@ -20,6 +20,7 @@
// ****************************************************************************
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
@@ -34,14 +35,15 @@ 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;
+using CUETools.Codecs.FLAC;
#endif
namespace CUETools.Processor
{
-
public enum OutputAudioFormat
{
WAV,
@@ -51,6 +53,23 @@ namespace CUETools.Processor
NoAudio
}
+ public enum AccurateRipMode
+ {
+ None,
+ Verify,
+ VerifyThenConvert,
+ VerifyAndConvert
+ }
+
+ public enum CUEStyle
+ {
+ SingleFileWithCUE,
+ SingleFile,
+ GapsPrepended,
+ GapsAppended,
+ GapsLeftOut
+ }
+
public static class General {
public static string FormatExtension(OutputAudioFormat value)
{
@@ -96,7 +115,7 @@ namespace CUETools.Processor
{
line = new CUELine();
line.Params.Add(command); line.IsQuoted.Add(false);
- line.Params.Add(value); line.IsQuoted.Add(true);
+ line.Params.Add(value); line.IsQuoted.Add(quoted);
list.Add(line);
}
else
@@ -114,7 +133,7 @@ namespace CUETools.Processor
line = new CUELine();
line.Params.Add(command); line.IsQuoted.Add(false);
line.Params.Add(command2); line.IsQuoted.Add(false);
- line.Params.Add(value); line.IsQuoted.Add(true);
+ line.Params.Add(value); line.IsQuoted.Add(quoted);
list.Add(line);
}
else
@@ -171,14 +190,6 @@ namespace CUETools.Processor
}
}
- public enum CUEStyle {
- SingleFileWithCUE,
- SingleFile,
- GapsPrepended,
- GapsAppended,
- GapsLeftOut
- }
-
public class CUEConfig {
public uint fixWhenConfidence;
public uint fixWhenPercent;
@@ -217,6 +228,7 @@ namespace CUETools.Processor
public bool createCUEFileWhenEmbedded;
public bool truncate4608ExtraSamples;
public int lossyWAVQuality;
+ public bool lossyWAVHybrid;
public bool decodeHDCDtoLW16;
public bool decodeHDCDto24bit;
@@ -247,7 +259,7 @@ namespace CUETools.Processor
singleFilenameFormat = "%F";
removeSpecial = false;
specialExceptions = "-()";
- replaceSpaces = true;
+ replaceSpaces = false;
embedLog = true;
fillUpCUE = true;
filenamesANSISafe = true;
@@ -260,6 +272,7 @@ namespace CUETools.Processor
createCUEFileWhenEmbedded = false;
truncate4608ExtraSamples = true;
lossyWAVQuality = 5;
+ lossyWAVHybrid = true;
decodeHDCDtoLW16 = false;
decodeHDCDto24bit = true;
}
@@ -304,6 +317,7 @@ namespace CUETools.Processor
sw.Save("CreateCUEFileWhenEmbedded", createCUEFileWhenEmbedded);
sw.Save("Truncate4608ExtraSamples", truncate4608ExtraSamples);
sw.Save("LossyWAVQuality", lossyWAVQuality);
+ sw.Save("LossyWAVHybrid", lossyWAVHybrid);
sw.Save("DecodeHDCDToLossyWAV16", decodeHDCDtoLW16);
sw.Save("DecodeHDCDTo24bit", decodeHDCDto24bit);
}
@@ -335,7 +349,7 @@ namespace CUETools.Processor
trackFilenameFormat = sr.Load("TrackFilenameFormat") ?? "%N-%A-%T";
removeSpecial = sr.LoadBoolean("RemoveSpecialCharacters") ?? false;
specialExceptions = sr.Load("SpecialCharactersExceptions") ?? "-()";
- replaceSpaces = sr.LoadBoolean("ReplaceSpaces") ?? true;
+ replaceSpaces = sr.LoadBoolean("ReplaceSpaces") ?? false;
embedLog = sr.LoadBoolean("EmbedLog") ?? true;
fillUpCUE = sr.LoadBoolean("FillUpCUE") ?? true;
filenamesANSISafe = sr.LoadBoolean("FilenamesANSISafe") ?? true;
@@ -348,6 +362,7 @@ namespace CUETools.Processor
createCUEFileWhenEmbedded = sr.LoadBoolean("CreateCUEFileWhenEmbedded") ?? false;
truncate4608ExtraSamples = sr.LoadBoolean("Truncate4608ExtraSamples") ?? true;
lossyWAVQuality = sr.LoadInt32("LossyWAVQuality", 0, 10) ?? 5;
+ lossyWAVHybrid = sr.LoadBoolean("LossyWAVHybrid") ?? true;
decodeHDCDtoLW16 = sr.LoadBoolean("DecodeHDCDToLossyWAV16") ?? false;
decodeHDCDto24bit = sr.LoadBoolean("DecodeHDCDTo24bit") ?? true;
}
@@ -382,7 +397,7 @@ namespace CUETools.Processor
public class CUEToolsProgressEventArgs
{
public string status = string.Empty;
- public uint percentTrack = 0;
+ public double percentTrck = 0;
public double percentDisk = 0.0;
public string input = string.Empty;
public string output = string.Empty;
@@ -404,11 +419,11 @@ namespace CUETools.Processor
private List _sources;
private List _sourcePaths, _trackFilenames;
private string _htoaFilename, _singleFilename;
- private bool _hasHTOAFilename, _hasTrackFilenames, _hasSingleFilename, _appliedWriteOffset;
+ private bool _hasHTOAFilename = false, _hasTrackFilenames = false, _hasSingleFilename = false, _appliedWriteOffset;
private bool _hasEmbeddedCUESheet;
private bool _paddedToFrame, _truncated4608, _usePregapForFirstTrackInSingleFile;
private int _writeOffset;
- private bool _accurateRip, _accurateOffset;
+ private AccurateRipMode _accurateRipMode;
private uint? _dataTrackLength;
private uint? _minDataTrackLength;
private string _accurateRipId;
@@ -421,8 +436,12 @@ 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 string _driveName;
+ private int _driveOffset;
+ private BitArray _cdErrors;
private bool _isArchive;
private List _archiveContents;
private string _archiveCUEpath;
@@ -430,6 +449,7 @@ namespace CUETools.Processor
private string _archivePassword;
private CUEToolsProgressEventArgs _progress;
private AccurateRipVerify _arVerify;
+ private CDImageLayout _toc;
public event ArchivePasswordRequiredHandler PasswordRequired;
public event CUEToolsProgressHandler CUEToolsProgress;
@@ -440,7 +460,8 @@ namespace CUETools.Processor
_progress = new CUEToolsProgressEventArgs();
_attributes = new List();
_tracks = new List();
- _toc = new CDImageLayout(0);
+ _trackFilenames = new List();
+ _toc = new CDImageLayout();
_sources = new List();
_sourcePaths = new List();
_albumTags = new NameValueCollection();
@@ -450,47 +471,104 @@ namespace CUETools.Processor
_paddedToFrame = false;
_truncated4608 = false;
_usePregapForFirstTrackInSingleFile = false;
- _accurateRip = false;
- _accurateOffset = false;
+ _accurateRipMode = AccurateRipMode.None;
_appliedWriteOffset = false;
_dataTrackLength = null;
_minDataTrackLength = null;
hdcdDecoder = null;
_hasEmbeddedCUESheet = false;
_isArchive = false;
+ _isCD = false;
}
- public void Open(string pathIn, bool outputLossyWAV)
+ public void OpenTOC(CDImageLayout toc)
{
- _outputLossyWAV = outputLossyWAV;
- if (_config.detectHDCD)
+ _toc = toc;
+ for (int iTrack = 0; iTrack < _toc.AudioTracks; iTrack++)
{
- try { hdcdDecoder = new HDCDDotNet.HDCDDotNet(2, 44100, ((_outputLossyWAV && _config.decodeHDCDtoLW16) || !_config.decodeHDCDto24bit) ? 20 : 24, _config.decodeHDCD); }
- catch { }
+ _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;
+ public void Open(string pathIn)
+ {
+ string cueDir = Path.GetDirectoryName(pathIn) ?? pathIn;
+#if !MONO
+ if (cueDir == pathIn)
+ {
+ CDDriveReader ripper = new CDDriveReader();
+ ripper.Open(pathIn[0]);
+ _toc = ripper.TOC;
+ _driveName = ripper.ARName;
+ ripper.Close();
+ if (_toc.AudioTracks > 0)
+ {
+ if (!AccurateRipVerify.FindDriveReadOffset(_driveName, out _driveOffset))
+ throw new Exception("Failed to find drive read offset for drive" + _driveName);
+ _isCD = true;
+ SourceInfo cdInfo;
+ cdInfo.Path = pathIn;
+ cdInfo.Offset = 0;
+ cdInfo.Length = _toc.AudioLength * 588;
+ _sources.Add(cdInfo);
+ 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);
+
+ 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, "REM", "DISCID", AccurateRipVerify.CalculateCDDBId(_toc), false);
+ General.SetCUELine(_attributes, "REM", "COMMENT", CDDriveReader.RipperVersion(), true);
+ General.SetCUELine(_attributes, "REM", "DATE", release.GetEvents()[0].Date.Substring(0, 4), false);
+ General.SetCUELine(_attributes, "PERFORMER", release.GetArtist(), true);
+ General.SetCUELine(_attributes, "TITLE", release.GetTitle(), true);
+ 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
+
+ SourceInfo sourceInfo;
+ 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" };
@@ -632,7 +710,7 @@ namespace CUETools.Processor
}
trackInfo = new 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) {
@@ -745,18 +823,21 @@ namespace CUETools.Processor
// Calculate the length of each index
for (i = 0; i < indexes.Count - 1; i++)
{
- int length = indexes[i + 1].Time - indexes[i].Time;
- if (length < 0)
+ if (indexes[i + 1].Time - indexes[i].Time < 0)
throw new Exception("Indexes must be in chronological order.");
- _toc[indexes[i].Track].AddIndex(new CDTrackIndex((uint)indexes[i].Index, (uint)indexes[i].Time, (uint)length));
+ if ((indexes[i+1].Track != indexes[i].Track || indexes[i+1].Index != indexes[i].Index + 1) &&
+ (indexes[i + 1].Track != indexes[i].Track + 1 || indexes[i].Index < 1 || indexes[i + 1].Index > 1))
+ throw new Exception("Indexes must be in chronological order.");
+ if (indexes[i].Index == 1 && (i == 0 || indexes[i - 1].Index != 0))
+ _toc[indexes[i].Track].AddIndex(new CDTrackIndex(0U, (uint)indexes[i].Time));
+ _toc[indexes[i].Track].AddIndex(new CDTrackIndex((uint)indexes[i].Index, (uint)indexes[i].Time));
}
- _toc.Length = (uint) indexes[indexes.Count - 1].Time;
- for (i = 1; i <= TrackCount; i++)
+
+ // Calculate the length of each track
+ for (int iTrack = 1; iTrack <= TrackCount; iTrack++)
{
- if (_toc[i].LastIndex < 1)
- throw new Exception("Track must have an INDEX 01.");
- _toc[i].Start = _toc[i][1].Start;
- _toc[i].Length = (i == TrackCount ? _toc.Length - _toc[i].Start : _toc[i+1][1].Start - _toc[i].Start);
+ _toc[iTrack].Start = _toc[iTrack][1].Start;
+ _toc[iTrack].Length = (iTrack == TrackCount ? (uint)indexes[indexes.Count - 1].Time - _toc[iTrack].Start : _toc[iTrack + 1][1].Start - _toc[iTrack].Start);
}
// Store the audio filenames, generating generic names if necessary
@@ -768,7 +849,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) );
@@ -805,53 +885,58 @@ namespace CUETools.Processor
if (General.FindCUELine(_attributes, "REM", "GENRE") == null && GetCommonTag("GENRE") != null)
General.SetCUELine(_attributes, "REM", "GENRE", GetCommonTag("GENRE"), true);
}
+
+ CUELine cddbDiscIdLine = General.FindCUELine(_attributes, "REM", "DISCID");
+ _cddbDiscIdTag = cddbDiscIdLine != null && cddbDiscIdLine.Params.Count == 3 ? cddbDiscIdLine.Params[2] : null;
+ if (_cddbDiscIdTag == null) _cddbDiscIdTag = GetCommonTag("DISCID");
+
if (_accurateRipId == null)
_accurateRipId = GetCommonTag("ACCURATERIPID");
if (_accurateRipId == null && _dataTrackLength == null && _eacLog != null)
{
sr = new StringReader(_eacLog);
- int lastAudioSector = -1;
bool isEACLog = false;
+ CDImageLayout tocFromLog = new CDImageLayout();
while ((lineStr = sr.ReadLine()) != null)
{
- if (!isEACLog)
+ if (isEACLog)
{
- if (!lineStr.StartsWith("Exact Audio Copy"))
- break;
- isEACLog = true;
- }
- string[] n = lineStr.Split('|');
- if (n.Length == 5)
- try
- {
- int trNo = Int32.Parse(n[0]);
- int trStart = Int32.Parse(n[3]);
- int trEnd = Int32.Parse(n[4]);
- if (trNo == TrackCount && trEnd > 0)
- lastAudioSector = trEnd;
- if (trNo == TrackCount + 1 && lastAudioSector != -1 && trEnd > lastAudioSector + (90 + 60) * 75 + 150)
- {
- _dataTrackLength = (uint)(trEnd - lastAudioSector - (90 + 60) * 75 - 150);
- break;
- }
- }
- catch { }
+ string[] n = lineStr.Split('|');
+ 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))
+ tocFromLog.AddTrack(new CDTrack(trNo, trStart, trEnd + 1 - trStart,
+ tocFromLog.TrackCount < _toc.TrackCount || trStart != tocFromLog[tocFromLog.TrackCount].End + 1U + 152U * 75U, false));
+ } else
+ if (lineStr.StartsWith("TOC of the extracted CD")
+ || lineStr.StartsWith("Exact Audio Copy")
+ || lineStr.StartsWith("CUERipper"))
+ isEACLog = true;
+ }
+ if (tocFromLog.TrackCount == _toc.TrackCount + 1 && !tocFromLog[tocFromLog.TrackCount].IsAudio)
+ _accurateRipId = AccurateRipVerify.CalculateAccurateRipId(tocFromLog);
+ }
+
+ if (_accurateRipId == null && _dataTrackLength != null)
+ {
+ CDImageLayout toc2 = new CDImageLayout(_toc);
+ toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152U * 75U, _dataTrackLength.Value, false, false));
+ _accurateRipId = AccurateRipVerify.CalculateAccurateRipId(toc2);
+ }
+
+ if (_dataTrackLength == null && _cddbDiscIdTag != null)
+ {
+ uint cddbDiscIdNum;
+ if (uint.TryParse(_cddbDiscIdTag, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out cddbDiscIdNum) && (cddbDiscIdNum & 0xff) == TrackCount + 1)
+ {
+ uint lengthFromTag = ((cddbDiscIdNum >> 8) & 0xffff);
+ _minDataTrackLength = ((lengthFromTag + _toc[1].Start / 75) - 152) * 75 - _toc.Length;
}
}
- CUELine cddbDiscIdLine = General.FindCUELine(_attributes, "REM", "DISCID");
- _cddbDiscIdTag = cddbDiscIdLine != null && cddbDiscIdLine.Params.Count == 3 ? cddbDiscIdLine.Params[2] : null;
- if (_cddbDiscIdTag == null) _cddbDiscIdTag = GetCommonTag("DISCID");
-
- if (_dataTrackLength != null)
- _accurateRipIdActual = _accurateRipId = CalculateAccurateRipId();
- else
- {
- _accurateRipIdActual = CalculateAccurateRipId();
- if (_accurateRipId == null)
- _accurateRipId = _accurateRipIdActual;
- }
+ _accurateRipIdActual = AccurateRipVerify.CalculateAccurateRipId(_toc);
+ if (_accurateRipId == null)
+ _accurateRipId = _accurateRipIdActual;
_arVerify = new AccurateRipVerify(_toc);
@@ -874,12 +959,12 @@ namespace CUETools.Processor
}
}
- private void ShowProgress(string status, uint percentTrack, double percentDisk, string input, string output)
+ private void ShowProgress(string status, double percentTrack, double percentDisk, string input, string output)
{
if (this.CUEToolsProgress == null)
return;
_progress.status = status;
- _progress.percentTrack = percentTrack;
+ _progress.percentTrck = percentTrack;
_progress.percentDisk = percentDisk;
_progress.input = input;
_progress.output = output;
@@ -887,11 +972,47 @@ namespace CUETools.Processor
}
#if !MONO
+ private void CDReadProgress(object sender, ReadProgressArgs e)
+ {
+ lock (this)
+ {
+ if (_stop)
+ throw new StopException();
+ if (_pause)
+ {
+ ShowProgress("Paused...", 0, 0, null, null);
+ Monitor.Wait(this);
+ }
+ }
+ if (this.CUEToolsProgress == null)
+ return;
+ 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;
+ _progress.percentDisk = (double)(e.PassStart + (processed + e.Pass * (e.PassEnd - e.PassStart)) / (audioSource.CorrectionQuality + 1)) / audioSource.TOC.AudioLength;
+ _progress.percentTrck = (double) (e.Position - e.PassStart) / (e.PassEnd - e.PassStart);
+ _progress.status = string.Format("Ripping @{0:00.00}x {1}", speed, e.Pass > 0 ? " (Retry " + e.Pass.ToString() + ")" : "");
+ this.CUEToolsProgress(this, _progress);
+ }
+
+ private void MusicBrainz_LookupProgress(object sender, XmlRequestEventArgs e)
+ {
+ if (this.CUEToolsProgress == null)
+ return;
+ _progress.percentDisk = (1.0 + _progress.percentDisk) / 2;
+ _progress.percentTrck = 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)
return;
- _progress.percentTrack = (uint)Math.Round(e.PercentComplete);
+ _progress.percentTrck = e.PercentComplete/100;
this.CUEToolsProgress(this, _progress);
}
@@ -971,8 +1092,9 @@ namespace CUETools.Processor
return null;
}
- public void GenerateFilenames (OutputAudioFormat format, string outputPath)
+ public void GenerateFilenames(OutputAudioFormat format, bool outputLossyWAV, string outputPath)
{
+ _outputLossyWAV = outputLossyWAV;
_cuePath = outputPath;
string extension = General.FormatExtension(format);
@@ -1064,7 +1186,7 @@ namespace CUETools.Processor
{
IAudioSource audioSource;
- ShowProgress("Analyzing input file...", 0, 0.0, path, null);
+ ShowProgress("Analyzing input file...", 0.0, 0.0, path, null);
#if !MONO
if (_isArchive)
{
@@ -1090,81 +1212,136 @@ namespace CUETools.Processor
return (int)audioSource.Length;
}
- public void WriteM3U(string path, CUEStyle style)
+ public void WriteText(string path, string text)
{
- StringWriter sw = new StringWriter();
- WriteM3U(sw, style);
- sw.Close();
- bool utf8Required = CUESheet.Encoding.GetString(CUESheet.Encoding.GetBytes(sw.ToString())) != sw.ToString();
+ bool utf8Required = CUESheet.Encoding.GetString(CUESheet.Encoding.GetBytes(text)) != text;
StreamWriter sw1 = new StreamWriter(path, false, utf8Required ? Encoding.UTF8 : CUESheet.Encoding);
- sw1.Write(sw.ToString());
+ sw1.Write(text);
sw1.Close();
}
- public void WriteM3U(TextWriter sw, CUEStyle style)
+ public string LOGContents()
{
- int iTrack;
- bool htoaToFile = ((style == CUEStyle.GapsAppended) && _config.preserveHTOA &&
- (_toc.Pregap != 0));
+ if (!_isCD)
+ return null;
+#if !MONO
+ StringWriter logWriter = new StringWriter();
+ logWriter.WriteLine("{0}", CDDriveReader.RipperVersion());
+ logWriter.WriteLine("Extraction logfile from : {0}", DateTime.Now);
+ logWriter.WriteLine("Used drive : {0}", _driveName);
+ logWriter.WriteLine("Read offset correction : {0}", _driveOffset);
+ //logWriter.WriteLine("Read command : {0}", );
+ //logWriter.WriteLine("Secure mode : {0}", );
+ if (hdcdDecoder != null && hdcdDecoder.Detected)
+ {
+ hdcd_decoder_statistics stats;
+ hdcdDecoder.GetStatistics(out stats);
+ logWriter.WriteLine("HDCD : peak extend: {0}, transient filter: {1}, gain: {2}",
+ (stats.enabled_peak_extend ? (stats.disabled_peak_extend ? "some" : "yes") : "none"),
+ (stats.enabled_transient_filter ? (stats.disabled_transient_filter ? "some" : "yes") : "none"),
+ stats.min_gain_adjustment == stats.max_gain_adjustment ?
+ (stats.min_gain_adjustment == 1.0 ? "none" : String.Format("{0:0.0}dB", (Math.Log10(stats.min_gain_adjustment) * 20))) :
+ String.Format("{0:0.0}dB..{1:0.0}dB", (Math.Log10(stats.min_gain_adjustment) * 20), (Math.Log10(stats.max_gain_adjustment) * 20))
+ );
+ logWriter.WriteLine();
+ }
+ logWriter.WriteLine();
+ logWriter.WriteLine("TOC of the extracted CD");
+ logWriter.WriteLine();
+ logWriter.WriteLine(" Track | Start | Length | Start sector | End sector");
+ logWriter.WriteLine(" ---------------------------------------------------------");
+ for (int track = 1; track <= _toc.TrackCount; track++)
+ logWriter.WriteLine("{0,9} | {1,8} | {2,8} | {3,9} | {4,9}",
+ _toc[track].Number,
+ _toc[track].StartMSF,
+ _toc[track].LengthMSF,
+ _toc[track].Start,
+ _toc[track].End);
+ bool wereErrors = false;
+ for (int iTrack = 0; iTrack < _toc.AudioTracks; iTrack++)
+ {
+ int cdErrors = 0;
+ 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)
+ {
+ logWriter.WriteLine();
+ logWriter.WriteLine("AccurateRip summary");
+ logWriter.WriteLine();
+ _arVerify.GenerateFullLog(logWriter, 0);
+ logWriter.WriteLine();
+ }
+ logWriter.WriteLine();
+ logWriter.WriteLine("End of status report");
+ logWriter.Close();
+ return logWriter.ToString();
+#else
+ return null;
+#endif
+ }
- if (htoaToFile) {
+ public string M3UContents(CUEStyle style)
+ {
+ StringWriter sw = new StringWriter();
+ if (style == CUEStyle.GapsAppended && _config.preserveHTOA && _toc.Pregap != 0)
WriteLine(sw, 0, _htoaFilename);
- }
- for (iTrack = 0; iTrack < TrackCount; iTrack++) {
+ for (int iTrack = 0; iTrack < TrackCount; iTrack++)
WriteLine(sw, 0, _trackFilenames[iTrack]);
- }
- }
-
- public void WriteTOC(string path)
- {
- StreamWriter sw = new StreamWriter(path, false, CUESheet.Encoding);
- WriteTOC(sw);
sw.Close();
+ return sw.ToString();
}
- public void WriteTOC(TextWriter sw)
+ public string TOCContents()
{
+ StringWriter sw = new StringWriter();
for (int iTrack = 0; iTrack < TrackCount; iTrack++)
WriteLine(sw, 0, "\t" + _toc[iTrack+1].Start + 150);
- }
-
- public void Write(string path, CUEStyle style) {
- StringWriter sw = new StringWriter();
- Write(sw, style);
sw.Close();
- bool utf8Required = CUESheet.Encoding.GetString(CUESheet.Encoding.GetBytes(sw.ToString())) != sw.ToString();
- StreamWriter sw1 = new StreamWriter(path, false, utf8Required?Encoding.UTF8:CUESheet.Encoding);
- sw1.Write(sw.ToString());
- sw1.Close();
+ return sw.ToString();
}
- public void Write(TextWriter sw, CUEStyle style) {
+ public string CUESheetContents(CUEStyle style)
+ {
+ StringWriter sw = new StringWriter();
int i, iTrack, iIndex;
- TrackInfo track;
- bool htoaToFile = ((style == CUEStyle.GapsAppended) && _config.preserveHTOA &&
- (_toc.Pregap != 0));
+ bool htoaToFile = (style == CUEStyle.GapsAppended && _config.preserveHTOA && _toc.Pregap != 0);
uint timeRelativeToFileStart = 0;
- using (sw) {
- if (_accurateRipId != null && _config.writeArTagsOnConvert)
- WriteLine(sw, 0, "REM ACCURATERIPID " +
- _accurateRipId);
+ using (sw)
+ {
+ if (_config.writeArTagsOnConvert)
+ WriteLine(sw, 0, "REM ACCURATERIPID " + _accurateRipId);
- for (i = 0; i < _attributes.Count; i++) {
+ for (i = 0; i < _attributes.Count; i++)
WriteLine(sw, 0, _attributes[i]);
- }
- if (style == CUEStyle.SingleFile || style == CUEStyle.SingleFileWithCUE) {
+ if (style == CUEStyle.SingleFile || style == CUEStyle.SingleFileWithCUE)
WriteLine(sw, 0, String.Format("FILE \"{0}\" WAVE", _singleFilename));
- }
- if (htoaToFile) {
+
+ if (htoaToFile)
WriteLine(sw, 0, String.Format("FILE \"{0}\" WAVE", _htoaFilename));
- }
-
- for (iTrack = 0; iTrack < TrackCount; iTrack++) {
- track = _tracks[iTrack];
+ for (iTrack = 0; iTrack < TrackCount; iTrack++)
+ {
if ((style == CUEStyle.GapsPrepended) ||
(style == CUEStyle.GapsLeftOut) ||
((style == CUEStyle.GapsAppended) &&
@@ -1175,201 +1352,41 @@ namespace CUETools.Processor
}
WriteLine(sw, 1, String.Format("TRACK {0:00} AUDIO", iTrack + 1));
- for (i = 0; i < track.Attributes.Count; i++) {
- WriteLine(sw, 2, track.Attributes[i]);
- }
+ for (i = 0; i < _tracks[iTrack].Attributes.Count; i++)
+ WriteLine(sw, 2, _tracks[iTrack].Attributes[i]);
- for (iIndex = 0; iIndex <= _toc[iTrack+1].LastIndex; iIndex++) {
- if (_toc[iTrack+1][iIndex].Length != 0) {
- if ((iIndex == 0) &&
- ((style == CUEStyle.GapsLeftOut) ||
- ((style == CUEStyle.GapsAppended) && (iTrack == 0) && !htoaToFile) ||
- ((style == CUEStyle.SingleFile || style == CUEStyle.SingleFileWithCUE) && (iTrack == 0) && _usePregapForFirstTrackInSingleFile)))
+ if (_toc[iTrack + 1].Pregap != 0)
+ {
+ if (((style == CUEStyle.GapsLeftOut) ||
+ ((style == CUEStyle.GapsAppended) && (iTrack == 0) && !htoaToFile) ||
+ ((style == CUEStyle.SingleFile || style == CUEStyle.SingleFileWithCUE) && (iTrack == 0) && _usePregapForFirstTrackInSingleFile)))
+ WriteLine(sw, 2, "PREGAP " + CDImageLayout.TimeToString(_toc[iTrack + 1].Pregap));
+ else
+ {
+ WriteLine(sw, 2, String.Format("INDEX 00 {0}", CDImageLayout.TimeToString(timeRelativeToFileStart)));
+ timeRelativeToFileStart += _toc[iTrack + 1].Pregap;
+ if (style == CUEStyle.GapsAppended)
{
- WriteLine(sw, 2, "PREGAP " + CDImageLayout.TimeToString(_toc[iTrack + 1][iIndex].Length));
- }
- else {
- WriteLine(sw, 2, String.Format( "INDEX {0:00} {1}", iIndex,
- CDImageLayout.TimeToString(timeRelativeToFileStart)));
- timeRelativeToFileStart += _toc[iTrack + 1][iIndex].Length;
-
- if ((style == CUEStyle.GapsAppended) && (iIndex == 0)) {
- WriteLine(sw, 0, String.Format("FILE \"{0}\" WAVE", _trackFilenames[iTrack]));
- timeRelativeToFileStart = 0;
- }
+ WriteLine(sw, 0, String.Format("FILE \"{0}\" WAVE", _trackFilenames[iTrack]));
+ timeRelativeToFileStart = 0;
}
}
}
- }
- }
- }
-
- private uint sumDigits(uint n)
- {
- uint r = 0;
- while (n > 0)
- {
- r = r + (n % 10);
- n = n / 10;
- }
- return r;
- }
-
- private string CalculateAccurateRipId ()
- {
- // Calculate the three disc ids used by AR
- uint discId1 = 0;
- uint discId2 = 0;
- uint cddbDiscId = 0;
-
- for (int iTrack = 1; iTrack <= _toc.TrackCount; iTrack++)
- {
- discId1 += _toc[iTrack].Start;
- discId2 += (_toc[iTrack].Start == 0 ? 1 : _toc[iTrack].Start) * ((uint)iTrack);
- cddbDiscId += sumDigits(_toc[iTrack].Start / 75 + 2);
- }
- uint trackOffset = _toc.Length;
- if (_dataTrackLength.HasValue)
- {
- trackOffset += ((90 + 60) * 75) + 150; // 90 second lead-out, 60 second lead-in, 150 sector gap
- cddbDiscId += sumDigits((uint)(trackOffset / 75) + 2);
- trackOffset += _dataTrackLength.Value;
- }
- discId1 += trackOffset;
- discId2 += (trackOffset == 0 ? 1 : trackOffset) * ((uint)TrackCount + 1);
-
- if (!_dataTrackLength.HasValue && _cddbDiscIdTag != null)
- {
- uint cddbDiscIdNum = UInt32.Parse(_cddbDiscIdTag, NumberStyles.HexNumber);
- if ((cddbDiscIdNum & 0xff) == TrackCount + 1)
- {
- uint lengthFromTag = ((cddbDiscIdNum >> 8) & 0xffff);
- _minDataTrackLength = ((lengthFromTag + _toc[1].Start / 75) - 152) * 75 - trackOffset;
- }
- }
-
- cddbDiscId = ((cddbDiscId % 255) << 24) +
- ((trackOffset / 75 - _toc[1].Start / 75) << 8) +
- (uint)(TrackCount + (_dataTrackLength.HasValue ? 1 : 0));
-
- discId1 &= 0xFFFFFFFF;
- discId2 &= 0xFFFFFFFF;
- cddbDiscId &= 0xFFFFFFFF;
-
- return String.Format("{0:x8}-{1:x8}-{2:x8}", discId1, discId2, cddbDiscId);
- }
-
- 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('=', '-');
- System.Diagnostics.Debug.WriteLine(_mbDiscId);
- }
-
- private void GetMetadataFromMusicBrainz() {
- if (_mbDiscId == null) return;
-
- using (Stream respStream = HttpGetToStream(
- "http://musicbrainz.org/ws/1/release/?type=xml&limit=1&discid=" + _mbDiscId))
- {
- XmlDocument xd = GetXmlDocument(respStream);
- XmlNode xn;
-
- xn = xd.SelectSingleNode("/metadata/release-list/release");
- if (xn != null)
- _mbReleaseId = xn.Attributes["id"].InnerText;
- }
-
- if (_mbReleaseId == null) return;
-
- using (Stream respStream = HttpGetToStream(String.Format(
- "http://musicbrainz.org/ws/1/release/{0}?type=xml&inc=artist+tracks", _mbReleaseId)))
- {
- string discArtist = null;
- string discTitle = null;
- XmlDocument xd = GetXmlDocument(respStream);
- XmlNode xn;
-
- XmlNode xnRelease = xd.DocumentElement.SelectSingleNode("/metadata/release");
- if (xnRelease == null) return;
-
- XmlNodeList xnlTracks = xnRelease.SelectNodes("track-list/track");
- if (xnlTracks.Count != TrackCount) return;
-
- xn = xnRelease.SelectSingleNode("title");
- if (xn != null)
- discTitle = xn.InnerText;
-
- xn = xnRelease.SelectSingleNode("artist/name");
- if (xn != null)
- discArtist = xn.InnerText;
-
- Artist = discArtist;
- Title = discTitle;
-
- for (int iTrack = 0; iTrack < TrackCount; iTrack++) {
- string trackArtist = null;
- string trackTitle = null;
- XmlNode xnTrack = xnlTracks[iTrack];
- TrackInfo trackInfo = Tracks[iTrack];
-
- xn = xnTrack.SelectSingleNode("title");
- if (xn != null)
- trackTitle = xn.InnerText;
-
- xn = xnTrack.SelectSingleNode("artist/name");
- if (xn != null)
- trackArtist = xn.InnerText;
-
- trackInfo.Artist = trackArtist ?? discArtist;
- trackInfo.Title = trackTitle;
- }
- }
- }
-
- private XmlDocument GetXmlDocument(Stream stream) {
- XmlDocument xd = new XmlDocument();
-
- xd.Load(stream);
-
- if (xd.DocumentElement.NamespaceURI.Length > 0) {
- // Strip namespace to simplify xpath expressions
- XmlDocument xdNew = new XmlDocument();
- xd.DocumentElement.SetAttribute("xmlns", String.Empty);
- xdNew.LoadXml(xd.OuterXml);
- xd = xdNew;
- }
-
- return xd;
- }
-
- private Stream HttpGetToStream(string url) {
- HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
- req.UserAgent = "CUE Tools";
- try {
- HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
- return resp.GetResponseStream();
- }
- catch (WebException ex) {
- if (ex.Status == WebExceptionStatus.ProtocolError) {
- HttpStatusCode code = ((HttpWebResponse)ex.Response).StatusCode;
- if (code == HttpStatusCode.NotFound) {
- throw new HttpNotFoundException();
+ for (iIndex = 1; iIndex <= _toc[iTrack+1].LastIndex; iIndex++)
+ {
+ WriteLine(sw, 2, String.Format( "INDEX {0:00} {1}", iIndex, CDImageLayout.TimeToString(timeRelativeToFileStart)));
+ timeRelativeToFileStart += _toc.IndexLength(iTrack + 1, iIndex);
}
}
- throw;
}
+ sw.Close();
+ return sw.ToString();
}
public void GenerateAccurateRipLog(TextWriter sw)
{
- int iTrack;
- sw.WriteLine (String.Format("[Disc ID: {0}]", _accurateRipId));
+ sw.WriteLine("[Verification date: {0}]", DateTime.Now);
+ sw.WriteLine("[Disc ID: {0}]", _accurateRipId);
if (_dataTrackLength.HasValue)
sw.WriteLine("Assuming a data track was present, length {0}.", CDImageLayout.TimeToString(_dataTrackLength.Value));
else
@@ -1399,46 +1416,9 @@ namespace CUETools.Processor
);
}
- if (_arVerify.AccResult == HttpStatusCode.NotFound)
- {
- sw.WriteLine("Disk not present in database.");
- //for (iTrack = 0; iTrack < TrackCount; iTrack++)
- // sw.WriteLine(String.Format(" {0:00}\t[{1:x8}] Disk not present in database", iTrack + 1, _tracks[iTrack].CRC));
- }
- else if (_arVerify.AccResult != HttpStatusCode.OK)
- {
- sw.WriteLine("Database access error: " + _arVerify.AccResult.ToString());
- //for (iTrack = 0; iTrack < TrackCount; iTrack++)
- // sw.WriteLine(String.Format(" {0:00}\t[{1:x8}] Database access error {2}", iTrack + 1, _tracks[iTrack].CRC, accResult.ToString()));
- }
- else
- {
- if (0 != _writeOffset)
- sw.WriteLine(String.Format("Offset applied: {0}", _writeOffset));
- int offsetApplied = _accurateOffset ? _writeOffset : 0;
- sw.WriteLine(String.Format("Track\t[ CRC ] Status"));
- _arVerify.GenerateAccurateRipLog(sw, offsetApplied);
- uint offsets_match = 0;
- for (int oi = -_arOffsetRange; oi <= _arOffsetRange; oi++)
- {
- uint matches = 0;
- for (iTrack = 0; iTrack < TrackCount; iTrack++)
- for (int di = 0; di < (int)_arVerify.AccDisks.Count; di++)
- if ((_arVerify.CRC(iTrack, oi) == _arVerify.AccDisks[di].tracks[iTrack].CRC && _arVerify.AccDisks[di].tracks[iTrack].CRC != 0) ||
- (_arVerify.CRC450(iTrack, oi) == _arVerify.AccDisks[di].tracks[iTrack].Frame450CRC && _arVerify.AccDisks[di].tracks[iTrack].Frame450CRC != 0))
- matches++;
- if (matches != 0 && oi != offsetApplied)
- {
- if (offsets_match++ > 10)
- {
- sw.WriteLine("More than 10 offsets match!");
- break;
- }
- sw.WriteLine(String.Format("Offsetted by {0}:", oi));
- _arVerify.GenerateAccurateRipLog(sw, oi);
- }
- }
- }
+ if (0 != _writeOffset)
+ sw.WriteLine("Offset applied: {0}", _writeOffset);
+ _arVerify.GenerateFullLog(sw, 0);
}
public void GenerateAccurateRipTagsForTrack(NameValueCollection tags, int offset, int bestOffset, int iTrack, string prefix)
@@ -1539,9 +1519,11 @@ namespace CUETools.Processor
bool htoaToFile = ((style == CUEStyle.GapsAppended) && _config.preserveHTOA &&
(_toc.Pregap != 0));
- if (_usePregapForFirstTrackInSingleFile) {
+ if (_isCD && (style == CUEStyle.GapsLeftOut || style == CUEStyle.GapsPrepended) && (_accurateRipMode == AccurateRipMode.None || _accurateRipMode == AccurateRipMode.VerifyAndConvert))
+ throw new Exception("When ripping a CD, gaps Left Out/Gaps prepended modes can only be used in verify-then-convert mode");
+
+ if (_usePregapForFirstTrackInSingleFile)
throw new Exception("UsePregapForFirstTrackInSingleFile is not supported for writing audio files.");
- }
if (style == CUEStyle.SingleFile || style == CUEStyle.SingleFileWithCUE) {
destPaths = new string[1];
@@ -1557,31 +1539,34 @@ namespace CUETools.Processor
}
}
- if ( !_accurateRip || _accurateOffset )
- for (int i = 0; i < destPaths.Length; i++) {
- for (int j = 0; j < _sourcePaths.Count; j++) {
- if (destPaths[i].ToLower() == _sourcePaths[j].ToLower()) {
- throw new Exception("Source and destination audio file paths cannot be the same.");
- }
- }
- }
+ if (_accurateRipMode != AccurateRipMode.Verify)
+ for (int i = 0; i < destPaths.Length; i++)
+ for (int j = 0; j < _sourcePaths.Count; j++)
+ if (destPaths[i].ToLower() == _sourcePaths[j].ToLower())
+ throw new Exception("Source and destination audio file paths cannot be the same.");
destLengths = CalculateAudioFileLengths(style);
bool SkipOutput = false;
- if (_accurateRip) {
+ if (_accurateRipMode != AccurateRipMode.None)
+ {
ShowProgress((string)"Contacting AccurateRip database...", 0, 0, null, null);
if (!_dataTrackLength.HasValue && _minDataTrackLength.HasValue && _accurateRipId == _accurateRipIdActual && _config.bruteForceDTL)
{
uint minDTL = _minDataTrackLength.Value;
+ CDImageLayout toc2 = new CDImageLayout(_toc);
+ toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152 * 75, minDTL, false, false));
for (uint dtl = minDTL; dtl < minDTL + 75; dtl++)
{
- _dataTrackLength = dtl;
- _accurateRipId = CalculateAccurateRipId();
+ toc2[toc2.TrackCount].Length = dtl;
+ _accurateRipId = AccurateRipVerify.CalculateAccurateRipId(toc2);
_arVerify.ContactAccurateRip(_accurateRipId);
if (_arVerify.AccResult != HttpStatusCode.NotFound)
+ {
+ _dataTrackLength = dtl;
break;
+ }
ShowProgress((string)"Contacting AccurateRip database...", 0, (dtl - minDTL) / 75.0, null, null);
lock (this) {
if (_stop)
@@ -1597,7 +1582,6 @@ namespace CUETools.Processor
}
if (_arVerify.AccResult != HttpStatusCode.OK)
{
- _dataTrackLength = null;
_accurateRipId = _accurateRipIdActual;
}
} else
@@ -1605,10 +1589,10 @@ namespace CUETools.Processor
if (_arVerify.AccResult != HttpStatusCode.OK)
{
- if (!_accurateOffset || _config.noUnverifiedOutput)
+ if (_accurateRipMode == AccurateRipMode.Verify || _config.noUnverifiedOutput)
{
- if ((_accurateOffset && _config.writeArLogOnConvert) ||
- (!_accurateOffset && _config.writeArLogOnVerify))
+ if ((_accurateRipMode != AccurateRipMode.Verify && _config.writeArLogOnConvert) ||
+ (_accurateRipMode == AccurateRipMode.Verify && _config.writeArLogOnVerify))
{
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
@@ -1621,12 +1605,18 @@ namespace CUETools.Processor
{
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
- WriteTOC(Path.ChangeExtension(_cuePath, ".toc"));
+ WriteText(Path.ChangeExtension(_cuePath, ".toc"), TOCContents());
}
return;
}
+ if (_accurateRipMode == AccurateRipMode.VerifyThenConvert && _isCD)
+ {
+ _writeOffset = 0;
+ WriteAudioFilesPass(dir, style, destPaths, destLengths, htoaToFile, true);
+ _arVerify.CreateBackup(_writeOffset);
+ }
}
- else if (_accurateOffset)
+ else if (_accurateRipMode == AccurateRipMode.VerifyThenConvert)
{
_writeOffset = 0;
WriteAudioFilesPass(dir, style, destPaths, destLengths, htoaToFile, true);
@@ -1647,30 +1637,87 @@ namespace CUETools.Processor
if (tracksMatch * 100 >= _config.fixWhenPercent * TrackCount)
_writeOffset = bestOffset;
}
+ _arVerify.CreateBackup(_writeOffset);
}
}
if (!SkipOutput)
{
- bool verifyOnly = _accurateRip && !_accurateOffset;
- if (!verifyOnly)
+ if (_accurateRipMode != AccurateRipMode.Verify)
{
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
- if (style != CUEStyle.SingleFileWithCUE)
- Write(_cuePath, style);
- else if (_config.createCUEFileWhenEmbedded)
- Write(Path.ChangeExtension(_cuePath, ".cue"), style);
- if (style != CUEStyle.SingleFileWithCUE && style != CUEStyle.SingleFile && _config.createM3U)
- WriteM3U(Path.ChangeExtension(_cuePath, ".m3u"), style);
}
- WriteAudioFilesPass(dir, style, destPaths, destLengths, htoaToFile, verifyOnly);
+ if (_isCD)
+ destLengths = CalculateAudioFileLengths(style); // need to recalc, might have changed after scanning the CD
+ WriteAudioFilesPass(dir, style, destPaths, destLengths, htoaToFile, _accurateRipMode == AccurateRipMode.Verify);
+ if (_accurateRipMode != AccurateRipMode.Verify)
+ {
+ string logContents = LOGContents();
+ string cueContents = CUESheetContents(style);
+ bool needNewCRCs = _accurateRipMode != AccurateRipMode.None &&
+ (_accurateRipMode == AccurateRipMode.VerifyAndConvert || _isCD) &&
+ _config.writeArTagsOnConvert &&
+ _arVerify.AccResult == HttpStatusCode.OK;
+ uint tracksMatch = 0;
+ int bestOffset = 0;
+ if (needNewCRCs)
+ FindBestOffset(1, true, out tracksMatch, out bestOffset);
+
+ if (logContents != null)
+ WriteText(Path.ChangeExtension(_cuePath, ".log"), logContents);
+ if (style != CUEStyle.SingleFileWithCUE)
+ {
+ WriteText(_cuePath, cueContents);
+#if !MONO
+ if (needNewCRCs && style != CUEStyle.SingleFile)
+ {
+ for (int iTrack = 0; iTrack < TrackCount; iTrack++)
+ {
+ IAudioSource audioSource = AudioReadWrite.GetAudioSource(destPaths[iTrack + (htoaToFile ? 1 : 0)], null);
+ CleanupTags(audioSource.Tags, "ACCURATERIP");
+ GenerateAccurateRipTags(audioSource.Tags, 0, bestOffset, iTrack);
+ audioSource.UpdateTags(false);
+ audioSource.Close();
+ audioSource = null;
+ }
+ }
+#endif
+ }
+ else
+ {
+ if (_config.createCUEFileWhenEmbedded)
+ WriteText(Path.ChangeExtension(_cuePath, ".cue"), cueContents);
+#if !MONO
+ if (needNewCRCs || _isCD)
+ {
+ IAudioSource audioSource = AudioReadWrite.GetAudioSource(destPaths[0], null);
+ if (_isCD)
+ {
+ if (_accurateRipMode != AccurateRipMode.VerifyThenConvert)
+ audioSource.Tags.Add("CUESHEET", cueContents);
+ audioSource.Tags.Add("LOG", logContents);
+ }
+ if (needNewCRCs)
+ {
+ CleanupTags(audioSource.Tags, "ACCURATERIP");
+ GenerateAccurateRipTags(audioSource.Tags, 0, bestOffset, -1);
+ }
+ audioSource.UpdateTags(false);
+ audioSource.Close();
+ audioSource = null;
+ }
+#endif
+ }
+ if (style != CUEStyle.SingleFileWithCUE && style != CUEStyle.SingleFile && _config.createM3U)
+ WriteText(Path.ChangeExtension(_cuePath, ".m3u"), M3UContents(style));
+ }
}
- if (_accurateRip)
+ if (_accurateRipMode != AccurateRipMode.None)
{
ShowProgress((string)"Generating AccurateRip report...", 0, 0, null, null);
- if (!_accurateOffset && _config.writeArTagsOnVerify && _writeOffset == 0 && !_isArchive)
+ if (_accurateRipMode == AccurateRipMode.Verify && _config.writeArTagsOnVerify && _writeOffset == 0 && !_isArchive && !_isCD)
{
uint tracksMatch;
int bestOffset;
@@ -1684,7 +1731,7 @@ namespace CUETools.Processor
GenerateAccurateRipTags (tags, 0, bestOffset, -1);
#if !MONO
if (audioSource is FLACReader)
- ((FLACReader)audioSource).UpdateTags (true);
+ audioSource.UpdateTags (true);
#endif
audioSource.Close();
audioSource = null;
@@ -1700,7 +1747,7 @@ namespace CUETools.Processor
NameValueCollection tags = audioSource.Tags;
CleanupTags(tags, "ACCURATERIP");
GenerateAccurateRipTags (tags, 0, bestOffset, iTrack);
- ((FLACReader)audioSource).UpdateTags(true);
+ audioSource.UpdateTags(true);
}
#endif
audioSource.Close();
@@ -1709,8 +1756,8 @@ namespace CUETools.Processor
}
}
- if ((_accurateOffset && _config.writeArLogOnConvert) ||
- (!_accurateOffset && _config.writeArLogOnVerify))
+ if ((_accurateRipMode != AccurateRipMode.Verify && _config.writeArLogOnConvert) ||
+ (_accurateRipMode == AccurateRipMode.Verify && _config.writeArLogOnVerify))
{
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
@@ -1723,7 +1770,7 @@ namespace CUETools.Processor
{
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
- WriteTOC(Path.ChangeExtension(_cuePath, ".toc"));
+ WriteText(Path.ChangeExtension(_cuePath, ".toc"), TOCContents());
}
}
}
@@ -1769,9 +1816,9 @@ namespace CUETools.Processor
if (destTags.Get("ARTIST") == null && "" != _tracks[iTrack].Artist)
destTags.Add("ARTIST", _tracks[iTrack].Artist);
destTags.Add("TRACKNUMBER", (iTrack + 1).ToString());
- if (_accurateRipId != null && _config.writeArTagsOnConvert)
+ if (_config.writeArTagsOnConvert)
{
- if (_accurateOffset && _arVerify.AccResult == HttpStatusCode.OK)
+ if (!_isCD && _accurateRipMode == AccurateRipMode.VerifyThenConvert && _arVerify.AccResult == HttpStatusCode.OK)
GenerateAccurateRipTags(destTags, _writeOffset, bestOffset, iTrack);
else
destTags.Add("ACCURATERIPID", _accurateRipId);
@@ -1818,13 +1865,8 @@ namespace CUETools.Processor
CleanupTags(destTags, "ACCURATERIP");
CleanupTags(destTags, "REPLAYGAIN");
- if (fWithCUE)
- {
- StringWriter sw = new StringWriter();
- Write(sw, CUEStyle.SingleFileWithCUE);
- destTags.Add("CUESHEET", sw.ToString());
- sw.Close();
- }
+ if (fWithCUE && (!_isCD || _accurateRipMode == AccurateRipMode.VerifyThenConvert))
+ destTags.Add("CUESHEET", CUESheetContents(CUEStyle.SingleFileWithCUE));
if (_config.embedLog)
{
@@ -1835,9 +1877,9 @@ namespace CUETools.Processor
destTags.Add("LOG", _eacLog);
}
- if (_accurateRipId != null && _config.writeArTagsOnConvert)
+ if (_config.writeArTagsOnConvert)
{
- if (fWithCUE && _accurateOffset && _arVerify.AccResult == HttpStatusCode.OK)
+ if (fWithCUE && !_isCD && _accurateRipMode == AccurateRipMode.VerifyThenConvert && _arVerify.AccResult == HttpStatusCode.OK)
GenerateAccurateRipTags(destTags, _writeOffset, bestOffset, -1);
else
destTags.Add("ACCURATERIPID", _accurateRipId);
@@ -1857,6 +1899,7 @@ namespace CUETools.Processor
int iSource = -1;
int iDest = -1;
uint samplesRemSource = 0;
+ CDImageLayout updatedTOC = null;
if (_writeOffset != 0)
{
@@ -1901,11 +1944,16 @@ namespace CUETools.Processor
uint tracksMatch;
int bestOffset = _writeOffset;
- if (!noOutput && _accurateRipId != null && _config.writeArTagsOnConvert && _accurateOffset && _arVerify.AccResult == HttpStatusCode.OK)
+ if (!noOutput && _accurateRipMode == AccurateRipMode.VerifyThenConvert && _config.writeArTagsOnConvert && _arVerify.AccResult == HttpStatusCode.OK)
FindBestOffset(1, true, out tracksMatch, out bestOffset);
- if (hdcdDecoder != null)
- hdcdDecoder.Reset();
+ if (_config.detectHDCD)
+ {
+ // currently broken verifyThenConvert on HDCD detection!!!! need to check for HDCD results higher
+ try { hdcdDecoder = new HDCDDotNet.HDCDDotNet(2, 44100, ((_outputLossyWAV && _config.decodeHDCDtoLW16) || !_config.decodeHDCDto24bit) ? 20 : 24, _config.decodeHDCD); }
+ catch { }
+ }
+
if (style == CUEStyle.SingleFile || style == CUEStyle.SingleFileWithCUE)
{
@@ -1917,137 +1965,183 @@ 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)
+ if (_accurateRipMode != AccurateRipMode.None)
_arVerify.Init();
ShowProgress(String.Format("{2} track {0:00} ({1:00}%)...", 0, 0, noOutput ? "Verifying" : "Writing"), 0, 0.0, null, null);
- for (iTrack = 0; iTrack < TrackCount; iTrack++) {
- track = _tracks[iTrack];
+ try
+ {
+ for (iTrack = 0; iTrack < TrackCount; iTrack++)
+ {
+ track = _tracks[iTrack];
- if ((style == CUEStyle.GapsPrepended) || (style == CUEStyle.GapsLeftOut)) {
- iDest++;
- if (hdcdDecoder != null)
- hdcdDecoder.AudioDest = null;
- if (audioDest != null)
- audioDest.Close();
- audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput);
- if (!noOutput)
- SetTrackTags(audioDest, iTrack, bestOffset);
- }
-
- for (iIndex = 0; iIndex <= _toc[iTrack+1].LastIndex; iIndex++) {
- uint trackPercent= 0, lastTrackPercent= 101;
- uint samplesRemIndex = _toc[iTrack + 1][iIndex].Length * 588;
-
- if (iIndex == 1)
- {
- previousOffset = currentOffset;
- currentOffset = 0;
- trackLength = _toc[iTrack + 1].Length * 588;
- }
-
- if ((style == CUEStyle.GapsAppended) && (iIndex == 1))
+ if ((style == CUEStyle.GapsPrepended) || (style == CUEStyle.GapsLeftOut))
{
+ iDest++;
if (hdcdDecoder != null)
hdcdDecoder.AudioDest = null;
if (audioDest != null)
audioDest.Close();
- iDest++;
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput);
if (!noOutput)
SetTrackTags(audioDest, iTrack, bestOffset);
}
- if ((style == CUEStyle.GapsAppended) && (iIndex == 0) && (iTrack == 0)) {
- discardOutput = !htoaToFile;
- if (htoaToFile) {
+ for (iIndex = 0; iIndex <= _toc[iTrack + 1].LastIndex; iIndex++)
+ {
+ uint samplesRemIndex = _toc.IndexLength(iTrack + 1, iIndex) * 588;
+
+ if (iIndex == 1)
+ {
+ previousOffset = currentOffset;
+ currentOffset = 0;
+ trackLength = _toc[iTrack + 1].Length * 588;
+ }
+
+ if ((style == CUEStyle.GapsAppended) && (iIndex == 1))
+ {
+ if (hdcdDecoder != null)
+ hdcdDecoder.AudioDest = null;
+ if (audioDest != null)
+ audioDest.Close();
iDest++;
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput);
- }
- }
- else if ((style == CUEStyle.GapsLeftOut) && (iIndex == 0)) {
- discardOutput = true;
- }
- else {
- discardOutput = false;
- }
-
- while (samplesRemIndex != 0) {
- if (samplesRemSource == 0) {
- if (audioSource != null) audioSource.Close();
- audioSource = GetAudioSource(++iSource);
- samplesRemSource = (uint) _sources[iSource].Length;
+ if (!noOutput)
+ SetTrackTags(audioDest, iTrack, bestOffset);
}
- uint copyCount = (uint) Math.Min(Math.Min(samplesRemIndex, samplesRemSource), buffLen);
-
- if ( trackLength > 0 )
+ if ((style == CUEStyle.GapsAppended) && (iIndex == 0) && (iTrack == 0))
{
- trackPercent = (uint)(currentOffset / 0.01 / trackLength);
- double diskPercent = ((float)diskOffset) / diskLength;
- 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);
- lastTrackPercent = trackPercent;
- }
-
- audioSource.Read(sampleBuffer, copyCount);
- if (!discardOutput)
- {
- if (!_config.detectHDCD || !_config.decodeHDCD)
- audioDest.Write(sampleBuffer, copyCount);
- if (_config.detectHDCD && hdcdDecoder != null)
+ discardOutput = !htoaToFile;
+ if (htoaToFile)
{
- if (_config.wait750FramesForHDCD && diskOffset > 750 * 588 && !hdcdDecoder.Detected)
+ iDest++;
+ audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput);
+ }
+ }
+ else if ((style == CUEStyle.GapsLeftOut) && (iIndex == 0))
+ {
+ discardOutput = true;
+ }
+ else
+ {
+ discardOutput = false;
+ }
+
+ while (samplesRemIndex != 0)
+ {
+ if (samplesRemSource == 0)
+ {
+#if !MONO
+ if (_isCD && audioSource != null && audioSource is CDDriveReader)
{
- hdcdDecoder.AudioDest = null;
- hdcdDecoder = null;
- if (_config.decodeHDCD)
+ updatedTOC = ((CDDriveReader)audioSource).TOC;
+ _cdErrors = ((CDDriveReader)audioSource).Errors;
+ }
+#endif
+ if (audioSource != null) audioSource.Close();
+ audioSource = GetAudioSource(++iSource);
+ samplesRemSource = (uint)_sources[iSource].Length;
+ }
+
+ uint copyCount = (uint)Math.Min(Math.Min(samplesRemIndex, samplesRemSource), buffLen);
+
+ if (trackLength > 0 && !_isCD)
+ {
+ double trackPercent = (double)currentOffset / trackLength;
+ double diskPercent = (double)diskOffset / diskLength;
+ ShowProgress(String.Format("{2} track {0:00} ({1:00}%)...", iIndex > 0 ? iTrack + 1 : iTrack, (uint)(100*trackPercent),
+ noOutput ? "Verifying" : "Writing"), trackPercent, diskPercent,
+ _isCD ? string.Format("{0}: {1:00} - {2}", audioSource.Path, iTrack + 1, _tracks[iTrack].Title) : audioSource.Path, discardOutput ? null : audioDest.Path);
+ }
+
+ if (audioSource.Read(sampleBuffer, copyCount) != copyCount)
+ throw new Exception("samples read != samples expected");
+ if (!discardOutput)
+ {
+ if (!_config.detectHDCD || !_config.decodeHDCD)
+ audioDest.Write(sampleBuffer, copyCount);
+ if (_config.detectHDCD && hdcdDecoder != null)
+ {
+ if (_config.wait750FramesForHDCD && diskOffset > 750 * 588 && !hdcdDecoder.Detected)
{
- audioSource.Close();
- audioDest.Delete();
- throw new Exception("HDCD not detected.");
+ hdcdDecoder.AudioDest = null;
+ hdcdDecoder = null;
+ if (_config.decodeHDCD)
+ {
+ audioSource.Close();
+ audioDest.Delete();
+ throw new Exception("HDCD not detected.");
+ }
+ }
+ else
+ {
+ if (_config.decodeHDCD)
+ hdcdDecoder.AudioDest = (discardOutput || noOutput) ? null : audioDest;
+ hdcdDecoder.Process(sampleBuffer, copyCount);
}
}
- else
- {
- if (_config.decodeHDCD)
- hdcdDecoder.AudioDest = (discardOutput || noOutput) ? null : audioDest;
- hdcdDecoder.Process(sampleBuffer, copyCount);
- }
}
- }
- if (_accurateRip && noOutput)
- _arVerify.Write(sampleBuffer, copyCount);
+ if (_accurateRipMode != AccurateRipMode.None)
+ _arVerify.Write(sampleBuffer, copyCount);
- currentOffset += copyCount;
- diskOffset += copyCount;
- samplesRemIndex -= copyCount;
- samplesRemSource -= copyCount;
+ currentOffset += copyCount;
+ diskOffset += copyCount;
+ samplesRemIndex -= copyCount;
+ samplesRemSource -= copyCount;
- lock (this) {
- if (_stop) {
- if (hdcdDecoder != null)
- hdcdDecoder.AudioDest = null;
- audioSource.Close();
- try {
- if (audioDest != null) audioDest.Close();
- } catch { }
- throw new StopException();
- }
- if (_pause)
+ lock (this)
{
- ShowProgress("Paused...", 0, 0, null, null);
- Monitor.Wait(this);
+ if (_stop)
+ throw new StopException();
+ if (_pause)
+ {
+ ShowProgress("Paused...", 0, 0, null, null);
+ Monitor.Wait(this);
+ }
}
}
}
}
}
+ catch (Exception ex)
+ {
+ if (hdcdDecoder != null)
+ hdcdDecoder.AudioDest = null;
+ hdcdDecoder = null;
+ try { if (audioSource != null) audioSource.Close(); }
+ catch { }
+ audioSource = null;
+ try { if (audioDest != null) audioDest.Close(); }
+ catch { }
+ audioDest = null;
+ throw ex;
+ }
+
+#if !MONO
+ if (_isCD && audioSource != null && audioSource is CDDriveReader)
+ {
+ updatedTOC = ((CDDriveReader)audioSource).TOC;
+ _cdErrors = ((CDDriveReader)audioSource).Errors;
+ }
+ if (updatedTOC != null)
+ {
+ _toc = updatedTOC;
+ if (_toc.Catalog != null)
+ General.SetCUELine(_attributes, "CATALOG", _toc.Catalog, false);
+ for (iTrack = 1; iTrack <= _toc.TrackCount; iTrack++)
+ {
+ if (_toc[iTrack].IsAudio && _toc[iTrack].ISRC != null)
+ 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
if (hdcdDecoder != null)
hdcdDecoder.AudioDest = null;
@@ -2155,7 +2249,8 @@ namespace CUETools.Processor
}
}
- private int[] CalculateAudioFileLengths(CUEStyle style) {
+ private int[] CalculateAudioFileLengths(CUEStyle style)
+ {
int iTrack, iIndex, iFile;
TrackInfo track;
int[] fileLengths;
@@ -2187,7 +2282,7 @@ namespace CUETools.Processor
discardOutput = (style == CUEStyle.GapsLeftOut && iIndex == 0);
if (!discardOutput)
- fileLengths[iFile] += (int) _toc[iTrack+1][iIndex].Length * 588;
+ fileLengths[iFile] += (int)_toc.IndexLength(iTrack + 1, iIndex) * 588;
}
}
@@ -2242,6 +2337,14 @@ namespace CUETools.Processor
}
else {
#if !MONO
+ if (_isCD)
+ {
+ CDDriveReader ripper = new CDDriveReader();
+ ripper.Open(sourceInfo.Path[0]);
+ ripper.DriveOffset = _driveOffset;
+ ripper.ReadProgress += new EventHandler(CDReadProgress);
+ audioSource = ripper;
+ } else
if (_isArchive)
{
RarStream IO = new RarStream(_archivePath, sourceInfo.Path);
@@ -2372,7 +2475,9 @@ namespace CUETools.Processor
if (dtl != 0)
{
_dataTrackLength = dtl;
- _accurateRipId = _accurateRipIdActual = CalculateAccurateRipId();
+ CDImageLayout toc2 = new CDImageLayout(_toc);
+ toc2.AddTrack(new CDTrack((uint)_toc.TrackCount, _toc.Length + 152 * 75, dtl, false, false));
+ _accurateRipIdActual = _accurateRipId = AccurateRipVerify.CalculateAccurateRipId(toc2);
}
}
}
@@ -2386,31 +2491,33 @@ namespace CUETools.Processor
}
}
- public CUEConfig Config {
- get {
+ public CUEConfig Config
+ {
+ get
+ {
return _config;
}
}
- public bool AccurateRip {
- get {
- return _accurateRip;
+ public AccurateRipMode AccurateRip
+ {
+ get
+ {
+ return _accurateRipMode;
}
- set {
- _accurateRip = value;
+ set
+ {
+ _accurateRipMode = value;
}
}
- public bool AccurateOffset {
- get {
- return _accurateOffset;
- }
- set {
- _accurateOffset = value;
+ public bool IsCD
+ {
+ get
+ {
+ return _isCD;
}
}
-
- CDImageLayout _toc;
}
public class CUELine {
@@ -2542,7 +2649,4 @@ namespace CUETools.Processor
public StopException() : base() {
}
}
-
- class HttpNotFoundException : Exception {
- }
}
\ No newline at end of file
diff --git a/CUETools.Ripper.SCSI/SCSIDrive.cs b/CUETools.Ripper.SCSI/SCSIDrive.cs
index 204f6f0..5f5932e 100644
--- a/CUETools.Ripper.SCSI/SCSIDrive.cs
+++ b/CUETools.Ripper.SCSI/SCSIDrive.cs
@@ -806,6 +806,8 @@ namespace CUETools.Ripper.SCSI
}
set
{
+ _currentTrack = -1;
+ _currentIndex = -1;
_sampleOffset = (int) value + _driveOffset;
}
}
diff --git a/CUETools/frmBatch.cs b/CUETools/frmBatch.cs
index 6847b95..aac94f0 100644
--- a/CUETools/frmBatch.cs
+++ b/CUETools/frmBatch.cs
@@ -133,7 +133,7 @@ namespace JDP
cueName = Path.GetFileNameWithoutExtension(pathIn) + ".cue";
bool outputAudio = _accurateRip != AccurateRipMode.Verify;
- cueSheet.Open(pathIn, _lossyWAV);
+ cueSheet.Open(pathIn);
if (outputAudio)
{
bool pathFound = false;
@@ -153,7 +153,7 @@ namespace JDP
}
else
pathOut = Path.Combine(Path.GetDirectoryName(pathIn) ?? pathIn, cueName);
- cueSheet.GenerateFilenames(_audioFormat, pathOut);
+ cueSheet.GenerateFilenames(_audioFormat, _lossyWAV, pathOut);
if (outputAudio)
{
if (_cueStyle == CUEStyle.SingleFileWithCUE)
diff --git a/CUETools/frmCUETools.cs b/CUETools/frmCUETools.cs
index 47adc3b..ec0be01 100644
--- a/CUETools/frmCUETools.cs
+++ b/CUETools/frmCUETools.cs
@@ -304,7 +304,7 @@ namespace JDP {
bool outputCUE = cueStyle != CUEStyle.SingleFileWithCUE && accurateRip != AccurateRipMode.Verify;
string pathOut = null;
- cueSheet.Open(pathIn, lossyWAV);
+ cueSheet.Open(pathIn);
this.Invoke((MethodInvoker)delegate()
{
@@ -312,7 +312,7 @@ namespace JDP {
pathOut = txtOutputPath.Text;
});
- cueSheet.GenerateFilenames(outputFormat, pathOut);
+ cueSheet.GenerateFilenames(outputFormat, lossyWAV, pathOut);
string outDir = Path.GetDirectoryName(pathOut);
if (cueStyle == CUEStyle.SingleFileWithCUE)
cueSheet.SingleFilename = Path.GetFileName(pathOut);
diff --git a/MAC_SDK/Source/MACLib/Assembly/Assembly64.obj b/MAC_SDK/Source/MACLib/Assembly/Assembly64.obj
index 0391c8e..130fc61 100644
Binary files a/MAC_SDK/Source/MACLib/Assembly/Assembly64.obj and b/MAC_SDK/Source/MACLib/Assembly/Assembly64.obj differ