* HDCD detection & decoding with library by Christopher Key

* All output formats allow for 24-bit audio
* ALAC (Apple Lossless) input support (preliminary)
* AccurateRip PartialContent error fixed
* 'pause' button switches to 'resume' button when pressed
* option to add md5 checksum to wavpack files
* option to generate .m3u playlists
* option to export .cue even when embedding it
This commit is contained in:
chudov
2008-11-04 01:23:30 +00:00
parent bbb87a2d49
commit 4bd778c594
14 changed files with 2482 additions and 2044 deletions

View File

@@ -3,31 +3,12 @@ using System.IO;
using FLACDotNet;
using WavPackDotNet;
using APEDotNet;
using ALACDotNet;
using AudioCodecsDotNet;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace CUEToolsLib {
public interface IAudioSource {
uint Read(byte[] buff, uint sampleCount);
ulong Length { get; }
ulong Position { get; set; }
NameValueCollection Tags { get; set; }
ulong Remaining { get; }
void Close();
int BitsPerSample { get; }
int ChannelCount { get; }
int SampleRate { get; }
string Path { get; }
}
public interface IAudioDest {
void Write(byte[] buff, uint sampleCount);
bool SetTags(NameValueCollection tags);
void Close();
long FinalSampleCount { set; }
string Path { get; }
}
public static class AudioReadWrite {
public static IAudioSource GetAudioSource(string path) {
switch (Path.GetExtension(path).ToLower()) {
@@ -40,6 +21,8 @@ namespace CUEToolsLib {
return new WavPackReader(path);
case ".ape":
return new APEReader(path);
case ".m4a":
return new ALACReader(path);
#endif
default:
throw new Exception("Unsupported audio type.");
@@ -69,332 +52,6 @@ namespace CUEToolsLib {
}
}
public class DummyWriter : IAudioDest {
public DummyWriter (string path, int bitsPerSample, int channelCount, int sampleRate) {
}
public bool SetTags(NameValueCollection tags)
{
return false;
}
public void Close() {
}
public long FinalSampleCount {
set {
}
}
public void Write(byte[] buff, uint sampleCount) {
}
public string Path { get { return null; } }
}
public class WAVReader : IAudioSource {
FileStream _fs;
BinaryReader _br;
ulong _dataOffset, _dataLen;
ulong _samplePos, _sampleLen;
int _bitsPerSample, _channelCount, _sampleRate, _blockAlign;
bool _largeFile;
string _path;
public WAVReader(string path) {
_path = path;
//_fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
_fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan);
_br = new BinaryReader(_fs);
ParseHeaders();
_sampleLen = _dataLen / (uint)_blockAlign;
Position = 0;
}
public void Close() {
_br.Close();
_br = null;
_fs = null;
}
private void ParseHeaders() {
const long maxFileSize = 0x7FFFFFFEL;
const uint fccRIFF = 0x46464952;
const uint fccWAVE = 0x45564157;
const uint fccFormat = 0x20746D66;
const uint fccData = 0x61746164;
uint lenRIFF;
long fileEnd;
bool foundFormat, foundData;
if (_br.ReadUInt32() != fccRIFF) {
throw new Exception("Not a valid RIFF file.");
}
lenRIFF = _br.ReadUInt32();
fileEnd = (long)lenRIFF + 8;
if (_br.ReadUInt32() != fccWAVE) {
throw new Exception("Not a valid WAVE file.");
}
_largeFile = false;
foundFormat = false;
foundData = false;
while (_fs.Position < fileEnd) {
uint ckID, ckSize, ckSizePadded;
long ckEnd;
ckID = _br.ReadUInt32();
ckSize = _br.ReadUInt32();
ckSizePadded = (ckSize + 1U) & ~1U;
ckEnd = _fs.Position + (long)ckSizePadded;
if (ckID == fccFormat) {
foundFormat = true;
if (_br.ReadUInt16() != 1) {
throw new Exception("WAVE must be PCM format.");
}
_channelCount = _br.ReadInt16();
_sampleRate = _br.ReadInt32();
_br.ReadInt32();
_blockAlign = _br.ReadInt16();
_bitsPerSample = _br.ReadInt16();
}
else if (ckID == fccData) {
foundData = true;
_dataOffset = (ulong) _fs.Position;
if (_fs.Length <= maxFileSize) {
_dataLen = ckSize;
}
else {
_largeFile = true;
_dataLen = ((ulong)_fs.Length) - _dataOffset;
}
}
if ((foundFormat & foundData) || _largeFile) {
break;
}
_fs.Seek(ckEnd, SeekOrigin.Begin);
}
if ((foundFormat & foundData) == false) {
throw new Exception("Format or data chunk not found.");
}
if (_channelCount <= 0) {
throw new Exception("Channel count is invalid.");
}
if (_sampleRate <= 0) {
throw new Exception("Sample rate is invalid.");
}
if (_blockAlign != (_channelCount * ((_bitsPerSample + 7) / 8))) {
throw new Exception("Block align is invalid.");
}
if ((_bitsPerSample <= 0) || (_bitsPerSample > 32)) {
throw new Exception("Bits per sample is invalid.");
}
}
public ulong Position {
get {
return _samplePos;
}
set {
ulong seekPos;
if (value > _sampleLen) {
_samplePos = _sampleLen;
}
else {
_samplePos = value;
}
seekPos = _dataOffset + (_samplePos * (uint)_blockAlign);
_fs.Seek((long) seekPos, SeekOrigin.Begin);
}
}
public ulong Length {
get {
return _sampleLen;
}
}
public ulong Remaining {
get {
return _sampleLen - _samplePos;
}
}
public int ChannelCount {
get {
return _channelCount;
}
}
public int SampleRate {
get {
return _sampleRate;
}
}
public int BitsPerSample {
get {
return _bitsPerSample;
}
}
public int BlockAlign {
get {
return _blockAlign;
}
}
public NameValueCollection Tags {
get {
return new NameValueCollection();
}
set {
}
}
public void GetTags(out List<string> names, out List<string> values)
{
names = new List<string>();
values = new List<string>();
}
public uint Read(byte[] buff, uint sampleCount) {
if (sampleCount > Remaining)
sampleCount = (uint) Remaining;
uint byteCount = sampleCount * (uint) _blockAlign;
if (sampleCount != 0) {
if (_fs.Read(buff, 0, (int) byteCount) != byteCount) {
throw new Exception("Incomplete file read.");
}
_samplePos += sampleCount;
}
return sampleCount;
}
public string Path { get { return _path; } }
}
public class WAVWriter : IAudioDest {
FileStream _fs;
BinaryWriter _bw;
int _bitsPerSample, _channelCount, _sampleRate, _blockAlign;
long _sampleLen;
string _path;
public WAVWriter(string path, int bitsPerSample, int channelCount, int sampleRate) {
_path = path;
_bitsPerSample = bitsPerSample;
_channelCount = channelCount;
_sampleRate = sampleRate;
_blockAlign = _channelCount * ((_bitsPerSample + 7) / 8);
_fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
_bw = new BinaryWriter(_fs);
WriteHeaders();
}
public bool SetTags(NameValueCollection tags)
{
return false;
}
private void WriteHeaders() {
const uint fccRIFF = 0x46464952;
const uint fccWAVE = 0x45564157;
const uint fccFormat = 0x20746D66;
const uint fccData = 0x61746164;
_bw.Write(fccRIFF);
_bw.Write((uint)0);
_bw.Write(fccWAVE);
_bw.Write(fccFormat);
_bw.Write((uint)16);
_bw.Write((ushort)1);
_bw.Write((ushort)_channelCount);
_bw.Write((uint)_sampleRate);
_bw.Write((uint)(_sampleRate * _blockAlign));
_bw.Write((ushort)_blockAlign);
_bw.Write((ushort)_bitsPerSample);
_bw.Write(fccData);
_bw.Write((uint)0);
}
public void Close() {
const long maxFileSize = 0x7FFFFFFEL;
long dataLen, dataLenPadded;
dataLen = _sampleLen * _blockAlign;
if ((dataLen & 1) == 1) {
_bw.Write((byte)0);
}
if ((dataLen + 44) > maxFileSize) {
dataLen = ((maxFileSize - 44) / _blockAlign) * _blockAlign;
}
dataLenPadded = ((dataLen & 1) == 1) ? (dataLen + 1) : dataLen;
_bw.Seek(4, SeekOrigin.Begin);
_bw.Write((uint)(dataLenPadded + 36));
_bw.Seek(40, SeekOrigin.Begin);
_bw.Write((uint)dataLen);
_bw.Close();
_bw = null;
_fs = null;
}
public long Position {
get {
return _sampleLen;
}
}
public long FinalSampleCount {
set {
}
}
public void Write(byte[] buff, uint sampleCount) {
if (sampleCount < 0) {
sampleCount = 0;
}
if (sampleCount != 0) {
_fs.Write(buff, 0, (int) sampleCount * _blockAlign);
_sampleLen += sampleCount;
}
}
public string Path { get { return _path; } }
}
#if !MONO
class FLACReader : IAudioSource {
FLACDotNet.FLACReader _flacReader;
@@ -469,29 +126,6 @@ namespace CUEToolsLib {
}
}
private unsafe void FLACSamplesToBytes_16(int[,] inSamples, uint inSampleOffset,
byte[] outSamples, uint outByteOffset, uint sampleCount, int channelCount)
{
uint loopCount = sampleCount * (uint) channelCount;
if ((inSamples.GetLength(0) - inSampleOffset < sampleCount) ||
(outSamples.Length - outByteOffset < loopCount * 2))
{
throw new IndexOutOfRangeException();
}
fixed (int* pInSamplesFixed = &inSamples[inSampleOffset, 0]) {
fixed (byte* pOutSamplesFixed = &outSamples[outByteOffset]) {
int* pInSamples = pInSamplesFixed;
short* pOutSamples = (short*)pOutSamplesFixed;
for (int i = 0; i < loopCount; i++) {
*(pOutSamples++) = (short)*(pInSamples++);
}
}
}
}
public uint Read(byte[] buff, uint sampleCount) {
if (_flacReader.BitsPerSample != 16) {
throw new Exception("Reading is only supported for 16 bit sample depth.");
@@ -509,7 +143,7 @@ namespace CUEToolsLib {
copyCount = Math.Min(samplesNeeded, SamplesInBuffer);
FLACSamplesToBytes_16(_sampleBuffer, _bufferOffset, buff, buffOffset,
AudioCodecsDotNet.AudioCodecsDotNet.FLACSamplesToBytes_16(_sampleBuffer, _bufferOffset, buff, buffOffset,
copyCount, chanCount);
samplesNeeded -= copyCount;
@@ -525,14 +159,13 @@ namespace CUEToolsLib {
class FLACWriter : IAudioDest {
FLACDotNet.FLACWriter _flacWriter;
int[,] _sampleBuffer;
int _bitsPerSample;
int _channelCount;
int _sampleRate;
public FLACWriter(string path, int bitsPerSample, int channelCount, int sampleRate) {
if (bitsPerSample != 16) {
throw new Exception("Bits per sample must be 16.");
if (bitsPerSample != 16 && bitsPerSample != 24) {
throw new Exception("Bits per sample must be 16 or 24.");
}
_bitsPerSample = bitsPerSample;
_channelCount = channelCount;
@@ -577,35 +210,9 @@ namespace CUEToolsLib {
_flacWriter.Close();
}
private unsafe void BytesToFLACSamples_16(byte[] inSamples, int inByteOffset,
int[,] outSamples, int outSampleOffset, uint sampleCount, int channelCount)
public void Write(int[,] buff, uint sampleCount)
{
uint loopCount = sampleCount * (uint) channelCount;
if ((inSamples.Length - inByteOffset < loopCount * 2) ||
(outSamples.GetLength(0) - outSampleOffset < sampleCount))
{
throw new IndexOutOfRangeException();
}
fixed (byte* pInSamplesFixed = &inSamples[inByteOffset]) {
fixed (int* pOutSamplesFixed = &outSamples[outSampleOffset, 0]) {
short* pInSamples = (short*)pInSamplesFixed;
int* pOutSamples = pOutSamplesFixed;
for (int i = 0; i < loopCount; i++) {
*(pOutSamples++) = (int)*(pInSamples++);
}
}
}
}
public void Write(byte[] buff, uint sampleCount) {
if ((_sampleBuffer == null) || (_sampleBuffer.GetLength(0) < sampleCount)) {
_sampleBuffer = new int[sampleCount, _channelCount];
}
BytesToFLACSamples_16(buff, 0, _sampleBuffer, 0, sampleCount, _channelCount);
_flacWriter.Write(_sampleBuffer, (int) sampleCount);
_flacWriter.Write(buff, (int) sampleCount);
}
public string Path { get { return _flacWriter.Path; } }
@@ -739,20 +346,22 @@ namespace CUEToolsLib {
class APEWriter : IAudioDest
{
APEDotNet.APEWriter _apeWriter;
//int[,] _sampleBuffer;
byte[] _sampleBuffer;
int _bitsPerSample;
int _channelCount;
int _sampleRate;
int _blockAlign;
public APEWriter(string path, int bitsPerSample, int channelCount, int sampleRate)
{
if (bitsPerSample != 16)
if (bitsPerSample != 16 && bitsPerSample != 24)
{
throw new Exception("Bits per sample must be 16.");
throw new Exception("Bits per sample must be 16 or 24.");
}
_bitsPerSample = bitsPerSample;
_channelCount = channelCount;
_sampleRate = sampleRate;
_blockAlign = _channelCount * ((_bitsPerSample + 7) / 8);
_apeWriter = new APEDotNet.APEWriter(path, bitsPerSample, channelCount, sampleRate);
}
@@ -775,40 +384,12 @@ namespace CUEToolsLib {
{
_apeWriter.Close();
}
private unsafe void BytesToAPESamples_16(byte[] inSamples, int inByteOffset,
int[,] outSamples, int outSampleOffset, uint sampleCount, int channelCount)
public void Write(int [,] buff, uint sampleCount)
{
uint loopCount = sampleCount * (uint)channelCount;
if ((inSamples.Length - inByteOffset < loopCount * 2) ||
(outSamples.GetLength(0) - outSampleOffset < sampleCount))
{
throw new IndexOutOfRangeException();
}
fixed (byte* pInSamplesFixed = &inSamples[inByteOffset])
{
fixed (int* pOutSamplesFixed = &outSamples[outSampleOffset, 0])
{
short* pInSamples = (short*)pInSamplesFixed;
int* pOutSamples = pOutSamplesFixed;
for (int i = 0; i < loopCount; i++)
{
*(pOutSamples++) = (int)*(pInSamples++);
}
}
}
}
public void Write(byte[] buff, uint sampleCount)
{
//if ((_sampleBuffer == null) || (_sampleBuffer.GetLength(0) < sampleCount))
//{
// _sampleBuffer = new int[sampleCount, _channelCount];
//}
//BytesToAPESamples_16(buff, 0, _sampleBuffer, 0, sampleCount, _channelCount);
//_apeWriter.Write(_sampleBuffer, (int)sampleCount);
_apeWriter.Write (buff, sampleCount);
if (_sampleBuffer == null || _sampleBuffer.Length < sampleCount * _blockAlign)
_sampleBuffer = new byte[sampleCount * _blockAlign];
AudioCodecsDotNet.AudioCodecsDotNet.FLACSamplesToBytes (buff, 0, _sampleBuffer, 0, sampleCount, _channelCount, _bitsPerSample);
_apeWriter.Write(_sampleBuffer, sampleCount);
}
public string Path { get { return _apeWriter.Path; } }
}
@@ -871,29 +452,6 @@ namespace CUEToolsLib {
set { _wavPackReader.Tags = value; }
}
private unsafe void WavPackSamplesToBytes_16(int[,] inSamples, uint inSampleOffset,
byte[] outSamples, uint outByteOffset, uint sampleCount, int channelCount)
{
uint loopCount = sampleCount * (uint) channelCount;
if ((inSamples.GetLength(0) - inSampleOffset < sampleCount) ||
(outSamples.Length - outByteOffset < loopCount * 2))
{
throw new IndexOutOfRangeException();
}
fixed (int* pInSamplesFixed = &inSamples[inSampleOffset, 0]) {
fixed (byte* pOutSamplesFixed = &outSamples[outByteOffset]) {
int* pInSamples = pInSamplesFixed;
short* pOutSamples = (short*)pOutSamplesFixed;
for (int i = 0; i < loopCount; i++) {
*(pOutSamples++) = (short)*(pInSamples++);
}
}
}
}
public uint Read(byte[] buff, uint sampleCount) {
if (_wavPackReader.BitsPerSample != 16) {
throw new Exception("Reading is only supported for 16 bit sample depth.");
@@ -903,8 +461,7 @@ namespace CUEToolsLib {
sampleBuffer = new int[sampleCount * 2, chanCount];
_wavPackReader.Read(sampleBuffer, (int) sampleCount);
WavPackSamplesToBytes_16(sampleBuffer, 0, buff, 0, sampleCount, chanCount);
AudioCodecsDotNet.AudioCodecsDotNet.FLACSamplesToBytes_16(sampleBuffer, 0, buff, 0, sampleCount, chanCount);
return sampleCount;
}
@@ -913,18 +470,21 @@ namespace CUEToolsLib {
class WavPackWriter : IAudioDest {
WavPackDotNet.WavPackWriter _wavPackWriter;
int[,] _sampleBuffer;
int _bitsPerSample;
int _channelCount;
int _sampleRate;
int _blockAlign;
byte[] _sampleBuffer;
public WavPackWriter(string path, int bitsPerSample, int channelCount, int sampleRate) {
if (bitsPerSample != 16) {
throw new Exception("Bits per sample must be 16.");
if (bitsPerSample != 16 && bitsPerSample != 24)
{
throw new Exception("Bits per sample must be 16 or 24.");
}
_bitsPerSample = bitsPerSample;
_channelCount = channelCount;
_sampleRate = sampleRate;
_blockAlign = _channelCount * ((_bitsPerSample + 7) / 8);
_wavPackWriter = new WavPackDotNet.WavPackWriter(path, bitsPerSample, channelCount, sampleRate);
}
@@ -961,39 +521,31 @@ namespace CUEToolsLib {
}
}
public bool MD5Sum
{
get
{
return _wavPackWriter.MD5Sum;
}
set
{
_wavPackWriter.MD5Sum = value;
}
}
public void Close() {
_wavPackWriter.Close();
}
private unsafe void BytesToWavPackSamples_16(byte[] inSamples, int inByteOffset,
int[,] outSamples, int outSampleOffset, uint sampleCount, int channelCount)
{
uint loopCount = sampleCount * (uint) channelCount;
if ((inSamples.Length - inByteOffset < loopCount * 2) ||
(outSamples.GetLength(0) - outSampleOffset < sampleCount))
public void Write(int[,] sampleBuffer, uint sampleCount) {
if (MD5Sum)
{
throw new IndexOutOfRangeException();
if (_sampleBuffer == null || _sampleBuffer.Length < sampleCount * _blockAlign)
_sampleBuffer = new byte[sampleCount * _blockAlign];
AudioCodecsDotNet.AudioCodecsDotNet.FLACSamplesToBytes(sampleBuffer, 0, _sampleBuffer, 0, sampleCount, _channelCount, _bitsPerSample);
_wavPackWriter.UpdateHash(_sampleBuffer, (int) sampleCount * _blockAlign);
}
fixed (byte* pInSamplesFixed = &inSamples[inByteOffset]) {
fixed (int* pOutSamplesFixed = &outSamples[outSampleOffset, 0]) {
short* pInSamples = (short*)pInSamplesFixed;
int* pOutSamples = pOutSamplesFixed;
for (int i = 0; i < loopCount; i++) {
*(pOutSamples++) = (int)*(pInSamples++);
}
}
}
}
public void Write(byte[] buff, uint sampleCount) {
if ((_sampleBuffer == null) || (_sampleBuffer.GetLength(0) < sampleCount)) {
_sampleBuffer = new int[sampleCount, _channelCount];
}
BytesToWavPackSamples_16(buff, 0, _sampleBuffer, 0, sampleCount, _channelCount);
_wavPackWriter.Write(_sampleBuffer, (int) sampleCount);
_wavPackWriter.Write(sampleBuffer, (int) sampleCount);
}
public string Path { get { return _wavPackWriter.Path; } }

View File

@@ -89,14 +89,26 @@
<Compile Include="Settings.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ALACDotNet\ALACDotNet.csproj">
<Project>{F2EC7193-D5E5-4252-9803-5CEB407E910F}</Project>
<Name>ALACDotNet</Name>
</ProjectReference>
<ProjectReference Include="..\APEDotNet\APEDotNet.vcproj">
<Project>{9AE965C4-301E-4C01-B90F-297AF341ACC6}</Project>
<Name>APEDotNet</Name>
</ProjectReference>
<ProjectReference Include="..\AudioCodecsDotNet\AudioCodecsDotNet.csproj">
<Project>{6458A13A-30EF-45A9-9D58-E5031B17BEE2}</Project>
<Name>AudioCodecsDotNet</Name>
</ProjectReference>
<ProjectReference Include="..\FLACDotNet\FLACDotNet.vcproj">
<Project>{E70FA90A-7012-4A52-86B5-362B699D1540}</Project>
<Name>FLACDotNet</Name>
</ProjectReference>
<ProjectReference Include="..\HDCDDotNet\HDCDDotNet.csproj">
<Project>{32338A04-5B6B-4C63-8EE7-C6400F73B5D7}</Project>
<Name>HDCDDotNet</Name>
</ProjectReference>
<ProjectReference Include="..\WavPackDotNet\WavPackDotNet.vcproj">
<Project>{CC2E74B6-534A-43D8-9F16-AC03FE955000}</Project>
<Name>WavPackDotNet</Name>

View File

@@ -27,6 +27,8 @@ using System.Globalization;
using System.IO;
using System.Net;
using System.Threading;
using AudioCodecsDotNet;
using HDCDDotNet;
namespace CUEToolsLib
{
@@ -215,6 +217,7 @@ namespace CUEToolsLib
public bool preserveHTOA;
public int wvCompressionMode;
public int wvExtraMode;
public bool wvStoreMD5;
public bool keepOriginalFilenames;
public string trackFilenameFormat;
public string singleFilenameFormat;
@@ -225,6 +228,11 @@ namespace CUEToolsLib
public bool fillUpCUE;
public bool filenamesANSISafe;
public bool bruteForceDTL;
public bool detectHDCD;
public bool decodeHDCD;
public bool wait750FramesForHDCD;
public bool createM3U;
public bool createCUEFileWhenEmbedded;
public CUEConfig()
{
@@ -247,6 +255,7 @@ namespace CUEToolsLib
preserveHTOA = true;
wvCompressionMode = 1;
wvExtraMode = 0;
wvStoreMD5 = false;
keepOriginalFilenames = true;
trackFilenameFormat = "%N-%A-%T";
singleFilenameFormat = "%F";
@@ -257,6 +266,11 @@ namespace CUEToolsLib
fillUpCUE = true;
filenamesANSISafe = true;
bruteForceDTL = false;
detectHDCD = true;
wait750FramesForHDCD = true;
decodeHDCD = false;
createM3U = false;
createCUEFileWhenEmbedded = false;
}
public void Save (SettingsWriter sw)
@@ -280,6 +294,7 @@ namespace CUEToolsLib
sw.Save("FLACVerify", flacVerify);
sw.Save("WVCompressionMode", wvCompressionMode);
sw.Save("WVExtraMode", wvExtraMode);
sw.Save("WVStoreMD5", wvStoreMD5);
sw.Save("KeepOriginalFilenames", keepOriginalFilenames);
sw.Save("SingleFilenameFormat", singleFilenameFormat);
sw.Save("TrackFilenameFormat", trackFilenameFormat);
@@ -290,6 +305,11 @@ namespace CUEToolsLib
sw.Save("FillUpCUE", fillUpCUE);
sw.Save("FilenamesANSISafe", filenamesANSISafe);
sw.Save("BruteForceDTL", bruteForceDTL);
sw.Save("DetectHDCD", detectHDCD);
sw.Save("Wait750FramesForHDCD", wait750FramesForHDCD);
sw.Save("DecodeHDCD", decodeHDCD);
sw.Save("CreateM3U", createM3U);
sw.Save("CreateCUEFileWhenEmbedded", createCUEFileWhenEmbedded);
}
public void Load(SettingsReader sr)
@@ -313,6 +333,7 @@ namespace CUEToolsLib
apeCompressionLevel = sr.LoadUInt32("APECompressionLevel", 1, 5) ?? 2;
wvCompressionMode = sr.LoadInt32("WVCompressionMode", 0, 3) ?? 1;
wvExtraMode = sr.LoadInt32("WVExtraMode", 0, 6) ?? 0;
wvStoreMD5 = sr.LoadBoolean("WVStoreMD5") ?? false;
keepOriginalFilenames = sr.LoadBoolean("KeepOriginalFilenames") ?? true;
singleFilenameFormat = sr.Load("SingleFilenameFormat") ?? "%F";
trackFilenameFormat = sr.Load("TrackFilenameFormat") ?? "%N-%A-%T";
@@ -323,6 +344,11 @@ namespace CUEToolsLib
fillUpCUE = sr.LoadBoolean("FillUpCUE") ?? true;
filenamesANSISafe = sr.LoadBoolean("FilenamesANSISafe") ?? true;
bruteForceDTL = sr.LoadBoolean("BruteForceDTL") ?? false;
detectHDCD = sr.LoadBoolean("DetectHDCD") ?? true;
wait750FramesForHDCD = sr.LoadBoolean("Wait750FramesForHDCD") ?? true;
decodeHDCD = sr.LoadBoolean("DecodeHDCD") ?? false;
createM3U = sr.LoadBoolean("CreateM3U") ?? false;
createCUEFileWhenEmbedded = sr.LoadBoolean("CreateCUEFileWhenEmbedded") ?? false;
}
public string CleanseString (string s)
@@ -374,6 +400,7 @@ namespace CUEToolsLib
private List<AccDisk> accDisks;
private HttpStatusCode accResult;
private const int _arOffsetRange = 5 * 588 - 1;
private HDCDDotNet.HDCDDotNet hdcdDecoder;
CUEConfig _config;
string _cddbDiscIdTag;
@@ -381,6 +408,13 @@ namespace CUEToolsLib
{
_config = config;
hdcdDecoder = null;
if (_config.detectHDCD)
{
try { hdcdDecoder = new HDCDDotNet.HDCDDotNet(2, 44100, _config.decodeHDCD); }
catch { }
}
string cueDir, lineStr, command, pathAudio, fileType;
CUELine line;
TrackInfo trackInfo;
@@ -890,6 +924,31 @@ namespace CUEToolsLib
return (int)audioSource.Length;
}
public void WriteM3U(string path, CUEStyle style)
{
StringWriter sw = new StringWriter();
WriteM3U(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();
}
public void WriteM3U(TextWriter sw, CUEStyle style)
{
int iTrack;
bool htoaToFile = ((style == CUEStyle.GapsAppended) && _config.preserveHTOA &&
(_tracks[0].IndexLengths[0] != 0));
if (htoaToFile) {
WriteLine(sw, 0, _htoaFilename);
}
for (iTrack = 0; iTrack < TrackCount; iTrack++) {
WriteLine(sw, 0, _trackFilenames[iTrack]);
}
}
public void Write(string path, CUEStyle style) {
StringWriter sw = new StringWriter();
Write(sw, style);
@@ -1064,11 +1123,19 @@ namespace CUEToolsLib
// Allocate byte buffer to hold stream contents
byte [] urlData = new byte[13];
long urlDataLen;
int urlDataLen, bytesRead;
accDisks.Clear();
while ( 0 != (urlDataLen = respStream.Read(urlData, 0, 13)) )
while ( true )
{
for (urlDataLen = 0; urlDataLen < 13; urlDataLen += bytesRead)
{
bytesRead = respStream.Read(urlData, urlDataLen, 13 - urlDataLen);
if (0 == bytesRead)
break;
}
if (urlDataLen == 0)
break;
if (urlDataLen < 13)
{
accResult = HttpStatusCode.PartialContent;
@@ -1082,11 +1149,14 @@ namespace CUEToolsLib
for (int i = 0; i < dsk.count; i++)
{
urlDataLen = respStream.Read(urlData, 0, 9);
if (urlDataLen < 9)
for (urlDataLen = 0; urlDataLen < 9; urlDataLen += bytesRead)
{
accResult = HttpStatusCode.PartialContent;
return;
bytesRead = respStream.Read(urlData, urlDataLen, 9 - urlDataLen);
if (0 == bytesRead)
{
accResult = HttpStatusCode.PartialContent;
return;
}
}
AccTrack trk = new AccTrack();
trk.count = urlData[0];
@@ -1259,6 +1329,20 @@ namespace CUEToolsLib
if (_accurateRipIdActual != _accurateRipId)
sw.WriteLine("Using preserved id, actual id is {0}.", _accurateRipIdActual);
}
if (hdcdDecoder != null && hdcdDecoder.Detected)
{
hdcd_decoder_statistics stats;
hdcdDecoder.GetStatistics(out stats);
sw.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))
);
}
if (accResult == HttpStatusCode.NotFound)
{
sw.WriteLine("Disk not present in database.");
@@ -1513,6 +1597,10 @@ namespace CUEToolsLib
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, statusDel, destPaths, destLengths, htoaToFile, verifyOnly);
}
@@ -1681,6 +1769,7 @@ namespace CUEToolsLib
TrackInfo track;
IAudioSource audioSource = null;
IAudioDest audioDest = null;
IAudioDest decodedAudioDest = null;
bool discardOutput;
int iSource = -1;
int iDest = -1;
@@ -1732,14 +1821,25 @@ namespace CUEToolsLib
if (!noOutput && _accurateRipId != null && _config.writeArTagsOnConvert && _accurateOffset && accResult == HttpStatusCode.OK)
FindBestOffset(1, true, out tracksMatch, out bestOffset);
if (hdcdDecoder != null)
hdcdDecoder.Reset();
if (style == CUEStyle.SingleFile || style == CUEStyle.SingleFileWithCUE)
{
iDest++;
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput);
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput, 16);
if (!noOutput)
SetAlbumTags(audioDest, bestOffset, style == CUEStyle.SingleFileWithCUE);
if (_config.detectHDCD && hdcdDecoder != null && _config.decodeHDCD && !noOutput)
{
decodedAudioDest = GetAudioDest(Path.ChangeExtension(destPaths[iDest], ".24bit" + Path.GetExtension(destPaths[iDest])),
destLengths[iDest], noOutput, 24);
SetAlbumTags(decodedAudioDest, bestOffset, style == CUEStyle.SingleFileWithCUE);
}
}
int[,] sampleBuffer = null;
if (_accurateRip && noOutput)
for (iTrack = 0; iTrack < TrackCount; iTrack++)
for (int iCRC = 0; iCRC < 10 * 588; iCRC++)
@@ -1765,9 +1865,18 @@ namespace CUEToolsLib
if ((style == CUEStyle.GapsPrepended) || (style == CUEStyle.GapsLeftOut)) {
if (audioDest != null) audioDest.Close();
iDest++;
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput);
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput, 16);
if (!noOutput)
SetTrackTags(audioDest, iTrack, bestOffset);
if (_config.detectHDCD && hdcdDecoder != null && _config.decodeHDCD && !noOutput)
{
hdcdDecoder.AudioDest = null;
if (decodedAudioDest != null)
decodedAudioDest.Close();
decodedAudioDest = GetAudioDest(Path.ChangeExtension(destPaths[iDest], ".24bit" + Path.GetExtension(destPaths[iDest])),
destLengths[iDest], noOutput, 24);
SetTrackTags(decodedAudioDest, iTrack, bestOffset);
}
}
for (iIndex = 0; iIndex <= track.LastIndex; iIndex++) {
@@ -1788,16 +1897,30 @@ namespace CUEToolsLib
if ((style == CUEStyle.GapsAppended) && (iIndex == 1)) {
if (audioDest != null) audioDest.Close();
iDest++;
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput);
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput, 16);
if (!noOutput)
SetTrackTags(audioDest, iTrack, bestOffset);
if (_config.detectHDCD && hdcdDecoder != null && _config.decodeHDCD && !noOutput)
{
hdcdDecoder.AudioDest = null;
if (decodedAudioDest != null)
decodedAudioDest.Close();
decodedAudioDest = GetAudioDest(Path.ChangeExtension(destPaths[iDest], ".24bit" + Path.GetExtension(destPaths[iDest])),
destLengths[iDest], noOutput, 24);
SetTrackTags(decodedAudioDest, iTrack, bestOffset);
}
}
if ((style == CUEStyle.GapsAppended) && (iIndex == 0) && (iTrack == 0)) {
discardOutput = !htoaToFile;
if (htoaToFile) {
iDest++;
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput);
audioDest = GetAudioDest(destPaths[iDest], destLengths[iDest], noOutput, 16);
if (_config.detectHDCD && hdcdDecoder != null && _config.decodeHDCD && !noOutput)
{
decodedAudioDest = GetAudioDest(Path.ChangeExtension(destPaths[iDest], ".24bit" + Path.GetExtension(destPaths[iDest])),
destLengths[iDest], noOutput, 24);
}
}
}
else if ((style == CUEStyle.GapsLeftOut) && (iIndex == 0)) {
@@ -1828,7 +1951,24 @@ namespace CUEToolsLib
}
audioSource.Read(buff, copyCount);
if (!discardOutput) audioDest.Write(buff, copyCount);
if (sampleBuffer == null || sampleBuffer.GetLength(0) < copyCount)
sampleBuffer = new int[copyCount, audioSource.ChannelCount];
AudioCodecsDotNet.AudioCodecsDotNet.BytesToFLACSamples_16(buff, 0, sampleBuffer, 0, copyCount, audioSource.ChannelCount);
if (!discardOutput) audioDest.Write(sampleBuffer, copyCount);
if (_config.detectHDCD && hdcdDecoder != null)
{
if (_config.wait750FramesForHDCD && diskOffset > 750 * 588 && !hdcdDecoder.Detected)
{
hdcdDecoder.AudioDest = null;
hdcdDecoder = null;
}
else
{
if (_config.decodeHDCD)
hdcdDecoder.AudioDest = (discardOutput || noOutput) ? null : decodedAudioDest;
hdcdDecoder.Process(sampleBuffer, copyCount);
}
}
if (_accurateRip && noOutput && (iTrack != 0 || iIndex != 0))
unsafe {
fixed (byte * pBuff = &buff[0])
@@ -1857,8 +1997,18 @@ namespace CUEToolsLib
lock (this) {
if (_stop) {
if (hdcdDecoder != null)
hdcdDecoder.AudioDest = null;
audioSource.Close();
try { audioDest.Close(); } catch {}
try {
if (audioDest != null) audioDest.Close();
} catch { }
// need two separate try/catches,
// because Close always throws an exception, and
// we want both streams closed.
try {
if (decodedAudioDest != null) decodedAudioDest.Close();
} catch { }
throw new StopException();
}
if (_pause)
@@ -1871,8 +2021,12 @@ namespace CUEToolsLib
}
}
if (hdcdDecoder != null)
hdcdDecoder.AudioDest = null;
if (audioSource != null) audioSource.Close();
audioDest.Close();
if (decodedAudioDest != null)
decodedAudioDest.Close();
}
public static string CorrectAudioFilenames(string path, bool always) {
@@ -2030,11 +2184,11 @@ namespace CUEToolsLib
}
}
private IAudioDest GetAudioDest(string path, int finalSampleCount, bool noOutput) {
private IAudioDest GetAudioDest(string path, int finalSampleCount, bool noOutput, int bitsPerSample) {
if (noOutput)
return new DummyWriter(path, 16, 2, 44100);
return new DummyWriter(path, bitsPerSample, 2, 44100);
IAudioDest dest = AudioReadWrite.GetAudioDest(path, 16, 2, 44100, finalSampleCount);
IAudioDest dest = AudioReadWrite.GetAudioDest(path, bitsPerSample, 2, 44100, finalSampleCount);
#if !MONO
if (dest is FLACWriter) {
@@ -2046,6 +2200,7 @@ namespace CUEToolsLib
WavPackWriter w = (WavPackWriter)dest;
w.CompressionMode = _config.wvCompressionMode;
w.ExtraMode = _config.wvExtraMode;
w.MD5Sum = _config.wvStoreMD5;
}
if (dest is APEWriter)
{
@@ -2406,86 +2561,4 @@ namespace CUEToolsLib
public StopException() : base() {
}
}
class SilenceGenerator : IAudioSource {
private ulong _sampleOffset, _sampleCount;
public SilenceGenerator(uint sampleCount) {
_sampleOffset = 0;
_sampleCount = sampleCount;
}
public ulong Length {
get {
return _sampleCount;
}
}
public ulong Remaining {
get {
return _sampleCount - _sampleOffset;
}
}
public ulong Position {
get {
return _sampleOffset;
}
set {
_sampleOffset = value;
}
}
public int BitsPerSample {
get {
return 16;
}
}
public int ChannelCount {
get {
return 2;
}
}
public int SampleRate {
get {
return 44100;
}
}
public NameValueCollection Tags
{
get
{
return new NameValueCollection();
}
set
{
}
}
public uint Read(byte[] buff, uint sampleCount) {
uint samplesRemaining, byteCount, i;
samplesRemaining = (uint) (_sampleCount - _sampleOffset);
if (sampleCount > samplesRemaining) {
sampleCount = samplesRemaining;
}
byteCount = sampleCount * 2 * 2;
for (i = 0; i < byteCount; i++) {
buff[i] = 0;
}
_sampleOffset += sampleCount;
return sampleCount;
}
public void Close() {
}
public string Path { get { return null; } }
}
}