mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
Code cleanup; Reader classes renamed to Decoders, Writers to Encoders, every Decoder must have a corresponding Settings class now just like Encoders. UserDefinedEncoders renamed to CommandLineEncoders, etc.
This commit is contained in:
270
CUETools.Codecs/WAV/AudioDecoder.cs
Normal file
270
CUETools.Codecs/WAV/AudioDecoder.cs
Normal file
@@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace CUETools.Codecs.WAV
|
||||
{
|
||||
[AudioDecoderClass(typeof(DecoderSettings))]
|
||||
public class AudioDecoder : IAudioSource
|
||||
{
|
||||
Stream _IO;
|
||||
BinaryReader _br;
|
||||
long _dataOffset, _samplePos, _sampleLen;
|
||||
private AudioPCMConfig pcm;
|
||||
long _dataLen;
|
||||
bool _largeFile;
|
||||
string _path;
|
||||
|
||||
private DecoderSettings m_settings;
|
||||
public AudioDecoderSettings Settings => m_settings;
|
||||
|
||||
public long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return _samplePos;
|
||||
}
|
||||
set
|
||||
{
|
||||
long seekPos;
|
||||
|
||||
if (_samplePos == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldSamplePos = _samplePos;
|
||||
if (_sampleLen >= 0 && value > _sampleLen)
|
||||
_samplePos = _sampleLen;
|
||||
else
|
||||
_samplePos = value;
|
||||
|
||||
if (_IO.CanSeek || _samplePos < oldSamplePos)
|
||||
{
|
||||
seekPos = _dataOffset + _samplePos * PCM.BlockAlign;
|
||||
_IO.Seek(seekPos, SeekOrigin.Begin);
|
||||
}
|
||||
else
|
||||
{
|
||||
int offs = (int)(_samplePos - oldSamplePos) * PCM.BlockAlign;
|
||||
while (offs > 0)
|
||||
{
|
||||
int chunk = Math.Min(offs, 16536);
|
||||
_br.ReadBytes(chunk);
|
||||
offs -= chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sampleLen;
|
||||
}
|
||||
}
|
||||
|
||||
public long Remaining
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sampleLen - _samplePos;
|
||||
}
|
||||
}
|
||||
|
||||
public AudioPCMConfig PCM { get { return pcm; } }
|
||||
|
||||
public string Path { get { return _path; } }
|
||||
|
||||
public AudioDecoder(DecoderSettings settings, string path, Stream IO = null)
|
||||
{
|
||||
m_settings = settings;
|
||||
_path = path;
|
||||
_IO = IO ?? new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan);
|
||||
_br = new BinaryReader(_IO);
|
||||
|
||||
ParseHeaders();
|
||||
|
||||
if (_dataLen < 0 || m_settings.IgnoreChunkSizes)
|
||||
_sampleLen = -1;
|
||||
else
|
||||
_sampleLen = _dataLen / pcm.BlockAlign;
|
||||
}
|
||||
|
||||
public AudioDecoder(DecoderSettings settings, string path, Stream IO, AudioPCMConfig _pcm)
|
||||
{
|
||||
m_settings = settings;
|
||||
_path = path;
|
||||
_IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan);
|
||||
_br = new BinaryReader(_IO);
|
||||
|
||||
_largeFile = false;
|
||||
_dataOffset = 0;
|
||||
_samplePos = 0;
|
||||
pcm = _pcm;
|
||||
_dataLen = _IO.CanSeek ? _IO.Length : -1;
|
||||
if (_dataLen < 0)
|
||||
_sampleLen = -1;
|
||||
else
|
||||
{
|
||||
_sampleLen = _dataLen / pcm.BlockAlign;
|
||||
if ((_dataLen % pcm.BlockAlign) != 0)
|
||||
throw new Exception("odd file size");
|
||||
}
|
||||
}
|
||||
|
||||
public static AudioBuffer ReadAllSamples(DecoderSettings settings, string path, Stream IO = null)
|
||||
{
|
||||
AudioDecoder reader = new AudioDecoder(settings, path, IO);
|
||||
AudioBuffer buff = new AudioBuffer(reader, (int)reader.Length);
|
||||
reader.Read(buff, -1);
|
||||
if (reader.Remaining != 0)
|
||||
throw new Exception("couldn't read the whole file");
|
||||
reader.Close();
|
||||
return buff;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (_br != null)
|
||||
{
|
||||
_br.Close();
|
||||
_br = null;
|
||||
}
|
||||
_IO = 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;
|
||||
bool foundFormat, foundData;
|
||||
|
||||
if (_br.ReadUInt32() != fccRIFF)
|
||||
{
|
||||
throw new Exception("Not a valid RIFF file.");
|
||||
}
|
||||
|
||||
lenRIFF = _br.ReadUInt32();
|
||||
|
||||
if (_br.ReadUInt32() != fccWAVE)
|
||||
{
|
||||
throw new Exception("Not a valid WAVE file.");
|
||||
}
|
||||
|
||||
_largeFile = false;
|
||||
foundFormat = false;
|
||||
foundData = false;
|
||||
long pos = 12;
|
||||
do
|
||||
{
|
||||
uint ckID, ckSize, ckSizePadded;
|
||||
long ckEnd;
|
||||
|
||||
ckID = _br.ReadUInt32();
|
||||
ckSize = _br.ReadUInt32();
|
||||
ckSizePadded = (ckSize + 1U) & ~1U;
|
||||
pos += 8;
|
||||
ckEnd = pos + (long)ckSizePadded;
|
||||
|
||||
if (ckID == fccFormat)
|
||||
{
|
||||
foundFormat = true;
|
||||
|
||||
uint fmtTag = _br.ReadUInt16();
|
||||
int _channelCount = _br.ReadInt16();
|
||||
int _sampleRate = _br.ReadInt32();
|
||||
_br.ReadInt32(); // bytes per second
|
||||
int _blockAlign = _br.ReadInt16();
|
||||
int _bitsPerSample = _br.ReadInt16();
|
||||
int _channelMask = 0;
|
||||
pos += 16;
|
||||
|
||||
if (fmtTag == 0xFFFEU && ckSize >= 34) // WAVE_FORMAT_EXTENSIBLE
|
||||
{
|
||||
_br.ReadInt16(); // CbSize
|
||||
_br.ReadInt16(); // ValidBitsPerSample
|
||||
_channelMask = _br.ReadInt32();
|
||||
fmtTag = _br.ReadUInt16();
|
||||
pos += 10;
|
||||
}
|
||||
|
||||
if (fmtTag != 1) // WAVE_FORMAT_PCM
|
||||
throw new Exception("WAVE format tag not WAVE_FORMAT_PCM.");
|
||||
|
||||
pcm = new AudioPCMConfig(_bitsPerSample, _channelCount, _sampleRate, (AudioPCMConfig.SpeakerConfig)_channelMask);
|
||||
if (pcm.BlockAlign != _blockAlign)
|
||||
throw new Exception("WAVE has strange BlockAlign");
|
||||
}
|
||||
else if (ckID == fccData)
|
||||
{
|
||||
foundData = true;
|
||||
|
||||
_dataOffset = pos;
|
||||
if (!_IO.CanSeek || _IO.Length <= maxFileSize)
|
||||
{
|
||||
if (ckSize == 0 || ckSize >= 0x7fffffff)
|
||||
_dataLen = -1;
|
||||
else
|
||||
_dataLen = (long)ckSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
_largeFile = true;
|
||||
_dataLen = _IO.Length - pos;
|
||||
}
|
||||
}
|
||||
|
||||
if ((foundFormat & foundData) || _largeFile)
|
||||
break;
|
||||
if (_IO.CanSeek)
|
||||
_IO.Seek(ckEnd, SeekOrigin.Begin);
|
||||
else
|
||||
_br.ReadBytes((int)(ckEnd - pos));
|
||||
pos = ckEnd;
|
||||
} while (true);
|
||||
|
||||
if ((foundFormat & foundData) == false || pcm == null)
|
||||
throw new Exception("Format or data chunk not found.");
|
||||
if (pcm.ChannelCount <= 0)
|
||||
throw new Exception("Channel count is invalid.");
|
||||
if (pcm.SampleRate <= 0)
|
||||
throw new Exception("Sample rate is invalid.");
|
||||
if ((pcm.BitsPerSample <= 0) || (pcm.BitsPerSample > 32))
|
||||
throw new Exception("Bits per sample is invalid.");
|
||||
if (pos != _dataOffset)
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
public int Read(AudioBuffer buff, int maxLength)
|
||||
{
|
||||
buff.Prepare(this, maxLength);
|
||||
|
||||
byte[] bytes = buff.Bytes;
|
||||
int byteCount = (int)buff.ByteLength;
|
||||
int pos = 0;
|
||||
|
||||
while (pos < byteCount)
|
||||
{
|
||||
int len = _IO.Read(bytes, pos, byteCount - pos);
|
||||
if (len <= 0)
|
||||
{
|
||||
if ((pos % PCM.BlockAlign) != 0 || _sampleLen >= 0)
|
||||
throw new Exception("Incomplete file read.");
|
||||
buff.Length = pos / PCM.BlockAlign;
|
||||
_samplePos += buff.Length;
|
||||
_sampleLen = _samplePos;
|
||||
return buff.Length;
|
||||
}
|
||||
pos += len;
|
||||
}
|
||||
_samplePos += buff.Length;
|
||||
return buff.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
181
CUETools.Codecs/WAV/AudioEncoder.cs
Normal file
181
CUETools.Codecs/WAV/AudioEncoder.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace CUETools.Codecs.WAV
|
||||
{
|
||||
[AudioEncoderClass(typeof(EncoderSettings))]
|
||||
public class AudioEncoder : IAudioDest
|
||||
{
|
||||
private Stream _IO;
|
||||
private BinaryWriter _bw;
|
||||
private long _sampleLen;
|
||||
private string _path;
|
||||
private long hdrLen = 0;
|
||||
private bool _headersWritten = false;
|
||||
private long _finalSampleCount = -1;
|
||||
private List<byte[]> _chunks = null;
|
||||
private List<uint> _chunkFCCs = null;
|
||||
|
||||
public long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sampleLen;
|
||||
}
|
||||
}
|
||||
|
||||
public long FinalSampleCount
|
||||
{
|
||||
set { _finalSampleCount = value; }
|
||||
}
|
||||
|
||||
private EncoderSettings m_settings;
|
||||
public AudioEncoderSettings Settings => m_settings;
|
||||
|
||||
public string Path { get { return _path; } }
|
||||
|
||||
public AudioEncoder(EncoderSettings settings, string path, Stream IO = null)
|
||||
{
|
||||
m_settings = settings;
|
||||
_path = path;
|
||||
_IO = IO ?? new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||
_bw = new BinaryWriter(_IO);
|
||||
}
|
||||
|
||||
public void WriteChunk(uint fcc, byte[] data)
|
||||
{
|
||||
if (_sampleLen > 0)
|
||||
throw new Exception("data already written, no chunks allowed");
|
||||
if (_chunks == null)
|
||||
{
|
||||
_chunks = new List<byte[]>();
|
||||
_chunkFCCs = new List<uint>();
|
||||
}
|
||||
_chunkFCCs.Add(fcc);
|
||||
_chunks.Add(data);
|
||||
hdrLen += 8 + data.Length + (data.Length & 1);
|
||||
}
|
||||
|
||||
private void WriteHeaders()
|
||||
{
|
||||
const uint fccRIFF = 0x46464952;
|
||||
const uint fccWAVE = 0x45564157;
|
||||
const uint fccFormat = 0x20746D66;
|
||||
const uint fccData = 0x61746164;
|
||||
|
||||
bool wavex = (Settings.PCM.BitsPerSample != 16 && Settings.PCM.BitsPerSample != 24) || Settings.PCM.ChannelMask != AudioPCMConfig.GetDefaultChannelMask(Settings.PCM.ChannelCount);
|
||||
|
||||
hdrLen += 36 + (wavex ? 24 : 0) + 8;
|
||||
|
||||
uint dataLen = (uint)(_finalSampleCount * Settings.PCM.BlockAlign);
|
||||
uint dataLenPadded = dataLen + (dataLen & 1);
|
||||
|
||||
_bw.Write(fccRIFF);
|
||||
if (_finalSampleCount <= 0)
|
||||
_bw.Write((uint)0xffffffff);
|
||||
else
|
||||
_bw.Write((uint)(dataLenPadded + hdrLen - 8));
|
||||
_bw.Write(fccWAVE);
|
||||
_bw.Write(fccFormat);
|
||||
if (wavex)
|
||||
{
|
||||
_bw.Write((uint)40);
|
||||
_bw.Write((ushort)0xfffe); // WAVEX follows
|
||||
}
|
||||
else
|
||||
{
|
||||
_bw.Write((uint)16);
|
||||
_bw.Write((ushort)1); // PCM
|
||||
}
|
||||
_bw.Write((ushort)Settings.PCM.ChannelCount);
|
||||
_bw.Write((uint)Settings.PCM.SampleRate);
|
||||
_bw.Write((uint)(Settings.PCM.SampleRate * Settings.PCM.BlockAlign));
|
||||
_bw.Write((ushort)Settings.PCM.BlockAlign);
|
||||
_bw.Write((ushort)((Settings.PCM.BitsPerSample + 7) / 8 * 8));
|
||||
if (wavex)
|
||||
{
|
||||
_bw.Write((ushort)22); // length of WAVEX structure
|
||||
_bw.Write((ushort)Settings.PCM.BitsPerSample);
|
||||
_bw.Write((uint)Settings.PCM.ChannelMask);
|
||||
_bw.Write((ushort)1); // PCM Guid
|
||||
_bw.Write((ushort)0);
|
||||
_bw.Write((ushort)0);
|
||||
_bw.Write((ushort)0x10);
|
||||
_bw.Write((byte)0x80);
|
||||
_bw.Write((byte)0x00);
|
||||
_bw.Write((byte)0x00);
|
||||
_bw.Write((byte)0xaa);
|
||||
_bw.Write((byte)0x00);
|
||||
_bw.Write((byte)0x38);
|
||||
_bw.Write((byte)0x9b);
|
||||
_bw.Write((byte)0x71);
|
||||
}
|
||||
if (_chunks != null)
|
||||
for (int i = 0; i < _chunks.Count; i++)
|
||||
{
|
||||
_bw.Write(_chunkFCCs[i]);
|
||||
_bw.Write((uint)_chunks[i].Length);
|
||||
_bw.Write(_chunks[i]);
|
||||
if ((_chunks[i].Length & 1) != 0)
|
||||
_bw.Write((byte)0);
|
||||
}
|
||||
|
||||
_bw.Write(fccData);
|
||||
if (_finalSampleCount <= 0)
|
||||
_bw.Write((uint)0xffffffff);
|
||||
else
|
||||
_bw.Write(dataLen);
|
||||
|
||||
_headersWritten = true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (_finalSampleCount <= 0 && _IO.CanSeek)
|
||||
{
|
||||
long dataLen = _sampleLen * Settings.PCM.BlockAlign;
|
||||
long dataLenPadded = dataLen + (dataLen & 1);
|
||||
if (dataLenPadded + hdrLen - 8 < 0xffffffff)
|
||||
{
|
||||
if ((dataLen & 1) == 1)
|
||||
_bw.Write((byte)0);
|
||||
|
||||
_bw.Seek(4, SeekOrigin.Begin);
|
||||
_bw.Write((uint)(dataLenPadded + hdrLen - 8));
|
||||
|
||||
_bw.Seek((int)hdrLen - 4, SeekOrigin.Begin);
|
||||
_bw.Write((uint)dataLen);
|
||||
}
|
||||
}
|
||||
|
||||
_bw.Close();
|
||||
|
||||
_bw = null;
|
||||
_IO = null;
|
||||
|
||||
if (_finalSampleCount > 0 && _sampleLen != _finalSampleCount)
|
||||
throw new Exception("Samples written differs from the expected sample count.");
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
_bw.Close();
|
||||
_bw = null;
|
||||
_IO = null;
|
||||
if (_path != "")
|
||||
File.Delete(_path);
|
||||
}
|
||||
|
||||
public void Write(AudioBuffer buff)
|
||||
{
|
||||
if (buff.Length == 0)
|
||||
return;
|
||||
buff.Prepare(this);
|
||||
if (!_headersWritten)
|
||||
WriteHeaders();
|
||||
_IO.Write(buff.Bytes, 0, buff.ByteLength);
|
||||
_sampleLen += buff.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
CUETools.Codecs/WAV/DecoderSettings.cs
Normal file
19
CUETools.Codecs/WAV/DecoderSettings.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace CUETools.Codecs.WAV
|
||||
{
|
||||
public class DecoderSettings : AudioDecoderSettings
|
||||
{
|
||||
public override string Name => "cuetools";
|
||||
|
||||
public override string Extension => "wav";
|
||||
|
||||
public override Type DecoderType => typeof(AudioDecoder);
|
||||
|
||||
public override int Priority => 2;
|
||||
|
||||
public DecoderSettings() : base() { }
|
||||
|
||||
public bool IgnoreChunkSizes;
|
||||
}
|
||||
}
|
||||
29
CUETools.Codecs/WAV/EncoderSettings.cs
Normal file
29
CUETools.Codecs/WAV/EncoderSettings.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace CUETools.Codecs.WAV
|
||||
{
|
||||
public class EncoderSettings : AudioEncoderSettings
|
||||
{
|
||||
public override string Extension => "wav";
|
||||
|
||||
public override string Name => "cuetools";
|
||||
|
||||
public override Type EncoderType => typeof(WAV.AudioEncoder);
|
||||
|
||||
public override int Priority => 10;
|
||||
|
||||
public override bool Lossless => true;
|
||||
|
||||
public EncoderSettings()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public EncoderSettings(AudioPCMConfig pcm)
|
||||
: base(pcm)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user