mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
* 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:
@@ -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; } }
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; } }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user