Move cuetools.net codec locally.

This commit is contained in:
2022-12-16 18:20:23 +00:00
parent 631bbfdbd0
commit 93b833c5bc
69 changed files with 11306 additions and 28 deletions

View File

@@ -0,0 +1,271 @@
using System;
using System.IO;
namespace CUETools.Codecs.WAV
{
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 IAudioDecoderSettings 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 TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
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;
}
}
}

View File

@@ -0,0 +1,180 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace CUETools.Codecs.WAV
{
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 IAudioEncoderSettings 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;
}
}
}

View File

@@ -0,0 +1,36 @@
using Newtonsoft.Json;
using System;
using System.ComponentModel;
namespace CUETools.Codecs.WAV
{
[JsonObject(MemberSerialization.OptIn)]
public class DecoderSettings : IAudioDecoderSettings
{
#region IAudioDecoderSettings implementation
[Browsable(false)]
public string Extension => "wav";
[Browsable(false)]
public string Name => "cuetools";
[Browsable(false)]
public Type DecoderType => typeof(AudioDecoder);
[Browsable(false)]
public int Priority => 2;
public IAudioDecoderSettings Clone()
{
return MemberwiseClone() as IAudioDecoderSettings;
}
#endregion
public DecoderSettings()
{
this.Init();
}
public bool IgnoreChunkSizes { get; set; }
}
}

View File

@@ -0,0 +1,64 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace CUETools.Codecs.WAV
{
[JsonObject(MemberSerialization.OptIn)]
public class EncoderSettings : IAudioEncoderSettings
{
#region IAudioEncoderSettings implementation
[Browsable(false)]
public string Extension => "wav";
[Browsable(false)]
public string Name => "cuetools";
[Browsable(false)]
public Type EncoderType => typeof(WAV.AudioEncoder);
[Browsable(false)]
public bool Lossless => true;
[Browsable(false)]
public int Priority => 10;
[Browsable(false)]
public string SupportedModes => "";
[Browsable(false)]
public string DefaultMode => "";
[Browsable(false)]
[DefaultValue("")]
public string EncoderMode { get; set; }
[Browsable(false)]
public AudioPCMConfig PCM { get; set; }
[Browsable(false)]
public int BlockSize { get; set; }
[Browsable(false)]
[DefaultValue(4096)]
public int Padding { get; set; }
public IAudioEncoderSettings Clone()
{
return MemberwiseClone() as IAudioEncoderSettings;
}
#endregion
public EncoderSettings()
{
this.Init();
}
public EncoderSettings(AudioPCMConfig pcm)
{
this.Init(pcm);
}
}
}