[Refactor] Use collection expressions.

This commit is contained in:
2024-05-01 04:39:38 +01:00
parent f7ca79b09e
commit 134ce7041e
667 changed files with 12936 additions and 13750 deletions

View File

@@ -1,335 +1,309 @@
using System;
namespace CUETools.Codecs
namespace CUETools.Codecs;
public unsafe class BitReader
{
unsafe public class BitReader
byte* bptr_m;
int buffer_len_m;
ulong cache_m;
ushort crc16_m;
int have_bits_m;
public BitReader()
{
#region Static Methods
Buffer = null;
bptr_m = null;
buffer_len_m = 0;
have_bits_m = 0;
cache_m = 0;
crc16_m = 0;
}
public static int log2i(int v)
public BitReader(byte* _buffer, int _pos, int _len)
{
Reset(_buffer, _pos, _len);
}
public int Position => (int)(bptr_m - Buffer - (have_bits_m >> 3));
public byte* Buffer { get; private set; }
public void Reset(byte* _buffer, int _pos, int _len)
{
Buffer = _buffer;
bptr_m = _buffer + _pos;
buffer_len_m = _len;
have_bits_m = 0;
cache_m = 0;
crc16_m = 0;
fill();
}
public void fill()
{
while(have_bits_m < 56)
{
return log2i((uint)v);
have_bits_m += 8;
byte b = *bptr_m++;
cache_m |= (ulong)b << 64 - have_bits_m;
crc16_m = (ushort)(crc16_m << 8 ^ Crc16.table[crc16_m >> 8 ^ b]);
}
}
public static readonly byte[] MultiplyDeBruijnBitPosition = new byte[32]
{
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};
public static int log2i(ulong v)
{
v |= v >> 1; // first round down to one less than a power of 2
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
if (v >> 32 == 0)
return MultiplyDeBruijnBitPosition[(uint)((uint)v * 0x07C4ACDDU) >> 27];
return 32 + MultiplyDeBruijnBitPosition[(uint)((uint)(v >> 32) * 0x07C4ACDDU) >> 27];
}
public static int log2i(uint v)
{
v |= v >> 1; // first round down to one less than a power of 2
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
return MultiplyDeBruijnBitPosition[(uint)(v * 0x07C4ACDDU) >> 27];
}
public static readonly byte[] byte_to_unary_table = new byte[]
{
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
#endregion
private byte* buffer_m;
private byte* bptr_m;
private int buffer_len_m;
private int have_bits_m;
private ulong cache_m;
private ushort crc16_m;
public int Position
{
get { return (int)(bptr_m - buffer_m - (have_bits_m >> 3)); }
}
public byte* Buffer
{
get
{
return buffer_m;
}
}
public BitReader()
{
buffer_m = null;
bptr_m = null;
buffer_len_m = 0;
have_bits_m = 0;
cache_m = 0;
crc16_m = 0;
}
public BitReader(byte* _buffer, int _pos, int _len)
{
Reset(_buffer, _pos, _len);
}
public void Reset(byte* _buffer, int _pos, int _len)
{
buffer_m = _buffer;
bptr_m = _buffer + _pos;
buffer_len_m = _len;
have_bits_m = 0;
cache_m = 0;
crc16_m = 0;
fill();
}
public void fill()
{
while (have_bits_m < 56)
{
have_bits_m += 8;
byte b = *(bptr_m++);
cache_m |= (ulong)b << (64 - have_bits_m);
crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ b]);
}
}
/* skip any number of bits */
public void skipbits(int bits)
{
while (bits > have_bits_m)
{
bits -= have_bits_m;
cache_m = 0;
have_bits_m = 0;
fill();
}
cache_m <<= bits;
have_bits_m -= bits;
}
public long read_long()
{
return ((long)readbits(32) << 32) | readbits(32);
}
public ulong read_ulong()
{
return ((ulong)readbits(32) << 32) | readbits(32);
}
public int read_int()
{
return (int)readbits(sizeof(int));
}
public uint read_uint()
{
return (uint)readbits(sizeof(uint));
}
public short read_short()
{
return (short)readbits(16);
}
public ushort read_ushort()
{
return (ushort)readbits(16);
}
/* supports reading 1 to 32 bits, in big endian format */
public uint readbits(int bits)
/* skip any number of bits */
public void skipbits(int bits)
{
while(bits > have_bits_m)
{
bits -= have_bits_m;
cache_m = 0;
have_bits_m = 0;
fill();
uint result = (uint)(cache_m >> 64 - bits);
skipbits(bits);
return result;
}
/* supports reading 1 to 64 bits, in big endian format */
public ulong readbits64(int bits)
{
if (bits <= 56)
return readbits(bits);
return ((ulong)readbits(32) << bits - 32) | readbits(bits - 32);
}
cache_m <<= bits;
have_bits_m -= bits;
}
/* reads a single bit */
public uint readbit()
{
return readbits(1);
}
public long read_long() => (long)readbits(32) << 32 | readbits(32);
public uint read_unary()
{
fill();
uint val = 0;
ulong result = cache_m >> 56;
while (result == 0)
{
val += 8;
cache_m <<= 8;
byte b = *(bptr_m++);
cache_m |= (ulong)b << (64 - have_bits_m);
crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ b]);
result = cache_m >> 56;
}
val += byte_to_unary_table[result];
skipbits((int)(val & 7) + 1);
return val;
}
public ulong read_ulong() => (ulong)readbits(32) << 32 | readbits(32);
public void flush()
{
if ((have_bits_m & 7) > 0)
{
cache_m <<= have_bits_m & 7;
have_bits_m -= have_bits_m & 7;
}
}
public int read_int() => (int)readbits(sizeof(int));
public ushort get_crc16()
public uint read_uint() => readbits(sizeof(uint));
public short read_short() => (short)readbits(16);
public ushort read_ushort() => (ushort)readbits(16);
/* supports reading 1 to 32 bits, in big endian format */
public uint readbits(int bits)
{
fill();
var result = (uint)(cache_m >> 64 - bits);
skipbits(bits);
return result;
}
/* supports reading 1 to 64 bits, in big endian format */
public ulong readbits64(int bits)
{
if(bits <= 56) return readbits(bits);
return (ulong)readbits(32) << bits - 32 | readbits(bits - 32);
}
/* reads a single bit */
public uint readbit() => readbits(1);
public uint read_unary()
{
fill();
uint val = 0;
ulong result = cache_m >> 56;
while(result == 0)
{
if (have_bits_m == 0)
return crc16_m;
ushort crc = 0;
int n = have_bits_m >> 3;
for (int i = 0; i < n; i++)
crc = (ushort)((crc << 8) ^ Crc16.table[(crc >> 8) ^ (byte)(cache_m >> (56 - (i << 3)))]);
return Crc16.Subtract(crc16_m, crc, n);
val += 8;
cache_m <<= 8;
byte b = *bptr_m++;
cache_m |= (ulong)b << 64 - have_bits_m;
crc16_m = (ushort)(crc16_m << 8 ^ Crc16.table[crc16_m >> 8 ^ b]);
result = cache_m >> 56;
}
public int readbits_signed(int bits)
{
int val = (int)readbits(bits);
val <<= (32 - bits);
val >>= (32 - bits);
return val;
}
val += byte_to_unary_table[result];
skipbits((int)(val & 7) + 1);
public uint read_utf8()
{
uint x = readbits(8);
uint v;
int i;
if (0 == (x & 0x80))
{
v = x;
i = 0;
}
else if (0xC0 == (x & 0xE0)) /* 110xxxxx */
{
v = x & 0x1F;
i = 1;
}
else if (0xE0 == (x & 0xF0)) /* 1110xxxx */
{
v = x & 0x0F;
i = 2;
}
else if (0xF0 == (x & 0xF8)) /* 11110xxx */
{
v = x & 0x07;
i = 3;
}
else if (0xF8 == (x & 0xFC)) /* 111110xx */
{
v = x & 0x03;
i = 4;
}
else if (0xFC == (x & 0xFE)) /* 1111110x */
{
v = x & 0x01;
i = 5;
}
else if (0xFE == x) /* 11111110 */
{
v = 0;
i = 6;
}
else
{
throw new Exception("invalid utf8 encoding");
}
for (; i > 0; i--)
{
x = readbits(8);
if (0x80 != (x & 0xC0)) /* 10xxxxxx */
throw new Exception("invalid utf8 encoding");
v <<= 6;
v |= (x & 0x3F);
}
return v;
}
return val;
}
public void read_rice_block(int n, int k, int* r)
{
fill();
fixed (byte* unary_table = byte_to_unary_table)
fixed (ushort* t = Crc16.table)
public void flush()
{
if((have_bits_m & 7) > 0)
{
cache_m <<= have_bits_m & 7;
have_bits_m -= have_bits_m & 7;
}
}
public ushort get_crc16()
{
if(have_bits_m == 0) return crc16_m;
ushort crc = 0;
int n = have_bits_m >> 3;
for(var i = 0; i < n; i++) crc = (ushort)(crc << 8 ^ Crc16.table[crc >> 8 ^ (byte)(cache_m >> 56 - (i << 3))]);
return Crc16.Subtract(crc16_m, crc, n);
}
public int readbits_signed(int bits)
{
var val = (int)readbits(bits);
val <<= 32 - bits;
val >>= 32 - bits;
return val;
}
public uint read_utf8()
{
uint x = readbits(8);
uint v;
int i;
if(0 == (x & 0x80))
{
v = x;
i = 0;
}
else if(0xC0 == (x & 0xE0)) /* 110xxxxx */
{
v = x & 0x1F;
i = 1;
}
else if(0xE0 == (x & 0xF0)) /* 1110xxxx */
{
v = x & 0x0F;
i = 2;
}
else if(0xF0 == (x & 0xF8)) /* 11110xxx */
{
v = x & 0x07;
i = 3;
}
else if(0xF8 == (x & 0xFC)) /* 111110xx */
{
v = x & 0x03;
i = 4;
}
else if(0xFC == (x & 0xFE)) /* 1111110x */
{
v = x & 0x01;
i = 5;
}
else if(0xFE == x) /* 11111110 */
{
v = 0;
i = 6;
}
else
throw new Exception("invalid utf8 encoding");
for(; i > 0; i--)
{
x = readbits(8);
if(0x80 != (x & 0xC0)) /* 10xxxxxx */ throw new Exception("invalid utf8 encoding");
v <<= 6;
v |= x & 0x3F;
}
return v;
}
public void read_rice_block(int n, int k, int* r)
{
fill();
fixed(byte* unary_table = byte_to_unary_table)
{
fixed(ushort* t = Crc16.table)
{
uint mask = (1U << k) - 1;
byte* bptr = bptr_m;
int have_bits = have_bits_m;
ulong cache = cache_m;
ushort crc = crc16_m;
for (int i = n; i > 0; i--)
uint mask = (1U << k) - 1;
byte* bptr = bptr_m;
int have_bits = have_bits_m;
ulong cache = cache_m;
ushort crc = crc16_m;
for(int i = n; i > 0; i--)
{
uint bits;
uint bits;
byte* orig_bptr = bptr;
while ((bits = unary_table[cache >> 56]) == 8)
while((bits = unary_table[cache >> 56]) == 8)
{
cache <<= 8;
byte b = *(bptr++);
cache |= (ulong)b << (64 - have_bits);
crc = (ushort)((crc << 8) ^ t[(crc >> 8) ^ b]);
}
uint msbs = bits + ((uint)(bptr - orig_bptr) << 3);
// assumes k <= 41 (have_bits < 41 + 7 + 1 + 8 == 57, so we don't loose bits here)
while (have_bits < 56)
{
have_bits += 8;
byte b = *(bptr++);
cache |= (ulong)b << (64 - have_bits);
crc = (ushort)((crc << 8) ^ t[(crc >> 8) ^ b]);
byte b = *bptr++;
cache |= (ulong)b << 64 - have_bits;
crc = (ushort)(crc << 8 ^ t[crc >> 8 ^ b]);
}
int btsk = k + (int)bits + 1;
uint uval = (msbs << k) | (uint)((cache >> (64 - btsk)) & mask);
cache <<= btsk;
have_bits -= btsk;
*(r++) = (int)(uval >> 1 ^ -(int)(uval & 1));
uint msbs = bits + ((uint)(bptr - orig_bptr) << 3);
// assumes k <= 41 (have_bits < 41 + 7 + 1 + 8 == 57, so we don't loose bits here)
while(have_bits < 56)
{
have_bits += 8;
byte b = *bptr++;
cache |= (ulong)b << 64 - have_bits;
crc = (ushort)(crc << 8 ^ t[crc >> 8 ^ b]);
}
int btsk = k + (int)bits + 1;
uint uval = msbs << k | (uint)(cache >> 64 - btsk & mask);
cache <<= btsk;
have_bits -= btsk;
*r++ = (int)(uval >> 1 ^ -(int)(uval & 1));
}
have_bits_m = have_bits;
cache_m = cache;
bptr_m = bptr;
crc16_m = crc;
cache_m = cache;
bptr_m = bptr;
crc16_m = crc;
}
}
}
}
}
}
#region Static Methods
public static int log2i(int v) => log2i((uint)v);
public static readonly byte[] MultiplyDeBruijnBitPosition =
[
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26,
5, 4, 31
];
public static int log2i(ulong v)
{
v |= v >> 1; // first round down to one less than a power of 2
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
if(v >> 32 == 0) return MultiplyDeBruijnBitPosition[(uint)v * 0x07C4ACDDU >> 27];
return 32 + MultiplyDeBruijnBitPosition[(uint)(v >> 32) * 0x07C4ACDDU >> 27];
}
public static int log2i(uint v)
{
v |= v >> 1; // first round down to one less than a power of 2
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
return MultiplyDeBruijnBitPosition[v * 0x07C4ACDDU >> 27];
}
public static readonly byte[] byte_to_unary_table =
[
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
];
#endregion
}

View File

@@ -1,51 +1,54 @@
namespace CUETools.Codecs
namespace CUETools.Codecs;
public enum InitialCrcValue
{
public enum InitialCrcValue { Zeros, NonZero1 = 0xffff, NonZero2 = 0x1D0F }
public class Crc16Ccitt
{
const ushort poly = 4129;
ushort[] table = new ushort[256];
ushort initialValue = 0;
public ushort ComputeChecksum(byte[] bytes, int pos, int count)
{
ushort crc = this.initialValue;
for (int i = pos; i < pos + count; i++)
{
crc = (ushort)((crc << 8) ^ table[((crc >> 8) ^ (0xff & bytes[i]))]);
}
return crc;
}
public byte[] ComputeChecksumBytes(byte[] bytes, int pos, int count)
{
ushort crc = ComputeChecksum(bytes, pos, count);
return new byte[] { (byte)(crc >> 8), (byte)(crc & 0x00ff) };
}
public Crc16Ccitt(InitialCrcValue initialValue)
{
this.initialValue = (ushort)initialValue;
ushort temp, a;
for (int i = 0; i < table.Length; i++)
{
temp = 0;
a = (ushort)(i << 8);
for (int j = 0; j < 8; j++)
{
if (((temp ^ a) & 0x8000) != 0)
{
temp = (ushort)((temp << 1) ^ poly);
}
else
{
temp <<= 1;
}
a <<= 1;
}
table[i] = temp;
}
}
}
Zeros,
NonZero1 = 0xffff,
NonZero2 = 0x1D0F
}
public class Crc16Ccitt
{
const ushort poly = 4129;
readonly ushort initialValue;
readonly ushort[] table = new ushort[256];
public Crc16Ccitt(InitialCrcValue initialValue)
{
this.initialValue = (ushort)initialValue;
ushort temp, a;
for(var i = 0; i < table.Length; i++)
{
temp = 0;
a = (ushort)(i << 8);
for(var j = 0; j < 8; j++)
{
if(((temp ^ a) & 0x8000) != 0)
temp = (ushort)(temp << 1 ^ poly);
else
temp <<= 1;
a <<= 1;
}
table[i] = temp;
}
}
public ushort ComputeChecksum(byte[] bytes, int pos, int count)
{
ushort crc = initialValue;
for(int i = pos; i < pos + count; i++) crc = (ushort)(crc << 8 ^ table[crc >> 8 ^ 0xff & bytes[i]]);
return crc;
}
public byte[] ComputeChecksumBytes(byte[] bytes, int pos, int count)
{
ushort crc = ComputeChecksum(bytes, pos, count);
return [(byte)(crc >> 8), (byte)(crc & 0x00ff)];
}
}

View File

@@ -1,94 +1,296 @@
using Newtonsoft.Json;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;
using CUETools.Codecs.CommandLine;
using Newtonsoft.Json;
namespace CUETools.Codecs
namespace CUETools.Codecs;
public class CUEToolsCodecsConfig
{
public class CUEToolsCodecsConfig
public List<IAudioDecoderSettings> decoders;
[JsonIgnore]
public DecoderListViewModel decodersViewModel;
public List<IAudioEncoderSettings> encoders;
[JsonIgnore]
public EncoderListViewModel encodersViewModel;
[JsonIgnore]
public Dictionary<string, CUEToolsFormat> formats;
public CUEToolsCodecsConfig()
{
[JsonIgnore]
public Dictionary<string, CUEToolsFormat> formats;
public List<IAudioEncoderSettings> encoders;
public List<IAudioDecoderSettings> decoders;
[JsonIgnore]
public EncoderListViewModel encodersViewModel;
[JsonIgnore]
public DecoderListViewModel decodersViewModel;
public CUEToolsCodecsConfig()
{
encoders = new List<IAudioEncoderSettings>();
decoders = new List<IAudioDecoderSettings>();
encodersViewModel = new EncoderListViewModel(encoders);
decodersViewModel = new DecoderListViewModel(decoders);
formats = new Dictionary<string, CUEToolsFormat>();
}
public CUEToolsCodecsConfig(CUEToolsCodecsConfig src)
{
encoders = new List<IAudioEncoderSettings>();
decoders = new List<IAudioDecoderSettings>();
src.encoders.ForEach(item => encoders.Add(item.Clone()));
src.decoders.ForEach(item => decoders.Add(item.Clone()));
encodersViewModel = new EncoderListViewModel(encoders);
decodersViewModel = new DecoderListViewModel(decoders);
formats = new Dictionary<string, CUEToolsFormat>();
foreach (var fmt in src.formats)
formats.Add(fmt.Key, fmt.Value.Clone(this));
}
public void Init(List<IAudioEncoderSettings> src_encoders, List<IAudioDecoderSettings> src_decoders)
{
encoders = new List<IAudioEncoderSettings>();
decoders = new List<IAudioDecoderSettings>();
src_encoders.ForEach(item => encoders.Add(item.Clone()));
src_decoders.ForEach(item => decoders.Add(item.Clone()));
if (Type.GetType("Mono.Runtime", false) == null)
{
encoders.Add(new CommandLine.EncoderSettings("flake.exe", "flac", true, "0 1 2 3 4 5 6 7 8 9 10 11 12", "8", "flake.exe", "-%M - -o %O -p %P"));
encoders.Add(new CommandLine.EncoderSettings("takc.exe", "tak", true, "0 1 2 2e 2m 3 3e 3m 4 4e 4m", "2", "takc.exe", "-e -p%M -overwrite - %O"));
encoders.Add(new CommandLine.EncoderSettings("ffmpeg.exe", "m4a", true, "", "", "ffmpeg.exe", "-i - -f ipod -acodec alac -y %O"));
encoders.Add(new CommandLine.EncoderSettings("lame.exe (VBR)", "mp3", false, "V9 V8 V7 V6 V5 V4 V3 V2 V1 V0", "V2", "lame.exe", "--vbr-new -%M - %O"));
encoders.Add(new CommandLine.EncoderSettings("lame.exe (CBR)", "mp3", false, "96 128 192 256 320", "256", "lame.exe", "-m s -q 0 -b %M --noreplaygain - %O"));
encoders.Add(new CommandLine.EncoderSettings("oggenc.exe", "ogg", false, "-1 -0.5 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5 8", "3", "oggenc.exe", "-q %M - -o %O"));
encoders.Add(new CommandLine.EncoderSettings("opusenc.exe", "opus", false, "6 16 32 48 64 96 128 192 256", "128", "opusenc.exe", "--bitrate %M - %O"));
encoders.Add(new CommandLine.EncoderSettings("neroAacEnc.exe", "m4a", false, "0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9", "0.4", "neroAacEnc.exe", "-q %M -if - -of %O"));
encoders.Add(new CommandLine.EncoderSettings("qaac.exe (tvbr)", "m4a", false, "10 20 30 40 50 60 70 80 90 100 110 127", "80", "qaac.exe", "-s -V %M -q 2 - -o %O"));
decoders.Add(new CommandLine.DecoderSettings("takc.exe", "tak", "takc.exe", "-d %I -"));
decoders.Add(new CommandLine.DecoderSettings("ffmpeg.exe", "m4a", "ffmpeg.exe", "-v 0 -i %I -f wav -"));
}
else
{
// !!!
}
encodersViewModel = new EncoderListViewModel(encoders);
decodersViewModel = new DecoderListViewModel(decoders);
formats = new Dictionary<string, CUEToolsFormat>();
formats.Add("flac", new CUEToolsFormat("flac", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("flac", true), null, decodersViewModel.GetDefault("flac")));
formats.Add("wv", new CUEToolsFormat("wv", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("wv", true), null, decodersViewModel.GetDefault("wv")));
formats.Add("ape", new CUEToolsFormat("ape", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("ape", true), null, decodersViewModel.GetDefault("ape")));
formats.Add("tta", new CUEToolsFormat("tta", CUEToolsTagger.APEv2, true, false, false, true, encodersViewModel.GetDefault("tta", true), null, decodersViewModel.GetDefault("tta")));
formats.Add("m2ts", new CUEToolsFormat("m2ts", CUEToolsTagger.APEv2, true, false, false, true, null, null, decodersViewModel.GetDefault("m2ts")));
formats.Add("mpls", new CUEToolsFormat("mpls", CUEToolsTagger.APEv2, true, false, false, true, null, null, decodersViewModel.GetDefault("mpls")));
formats.Add("wav", new CUEToolsFormat("wav", CUEToolsTagger.TagLibSharp, true, false, false, true, encodersViewModel.GetDefault("wav", true), null, decodersViewModel.GetDefault("wav")));
formats.Add("m4a", new CUEToolsFormat("m4a", CUEToolsTagger.TagLibSharp, true, true, false, true, encodersViewModel.GetDefault("m4a", true), encodersViewModel.GetDefault("m4a", false), decodersViewModel.GetDefault("m4a")));
formats.Add("tak", new CUEToolsFormat("tak", CUEToolsTagger.APEv2, true, false, true, true, encodersViewModel.GetDefault("tak", true), null, decodersViewModel.GetDefault("tak")));
formats.Add("wma", new CUEToolsFormat("wma", CUEToolsTagger.TagLibSharp, true, true, false, true, encodersViewModel.GetDefault("wma", true), encodersViewModel.GetDefault("wma", false), decodersViewModel.GetDefault("wma")));
formats.Add("mp3", new CUEToolsFormat("mp3", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("mp3", false), null));
formats.Add("ogg", new CUEToolsFormat("ogg", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("ogg", false), null));
formats.Add("opus", new CUEToolsFormat("opus", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("opus", false), null));
formats.Add("mlp", new CUEToolsFormat("mlp", CUEToolsTagger.APEv2, true, false, false, false, null, null, decodersViewModel.GetDefault("mlp")));
formats.Add("aob", new CUEToolsFormat("aob", CUEToolsTagger.APEv2, true, false, false, false, null, null, decodersViewModel.GetDefault("aob")));
}
encoders = [];
decoders = [];
encodersViewModel = new EncoderListViewModel(encoders);
decodersViewModel = new DecoderListViewModel(decoders);
formats = new Dictionary<string, CUEToolsFormat>();
}
}
public CUEToolsCodecsConfig(CUEToolsCodecsConfig src)
{
encoders = [];
decoders = [];
src.encoders.ForEach(item => encoders.Add(item.Clone()));
src.decoders.ForEach(item => decoders.Add(item.Clone()));
encodersViewModel = new EncoderListViewModel(encoders);
decodersViewModel = new DecoderListViewModel(decoders);
formats = new Dictionary<string, CUEToolsFormat>();
foreach(KeyValuePair<string, CUEToolsFormat> fmt in src.formats) formats.Add(fmt.Key, fmt.Value.Clone(this));
}
public void Init(List<IAudioEncoderSettings> src_encoders, List<IAudioDecoderSettings> src_decoders)
{
encoders = [];
decoders = [];
src_encoders.ForEach(item => encoders.Add(item.Clone()));
src_decoders.ForEach(item => decoders.Add(item.Clone()));
if(Type.GetType("Mono.Runtime", false) == null)
{
encoders.Add(new EncoderSettings("flake.exe",
"flac",
true,
"0 1 2 3 4 5 6 7 8 9 10 11 12",
"8",
"flake.exe",
"-%M - -o %O -p %P"));
encoders.Add(new EncoderSettings("takc.exe",
"tak",
true,
"0 1 2 2e 2m 3 3e 3m 4 4e 4m",
"2",
"takc.exe",
"-e -p%M -overwrite - %O"));
encoders.Add(new EncoderSettings("ffmpeg.exe",
"m4a",
true,
"",
"",
"ffmpeg.exe",
"-i - -f ipod -acodec alac -y %O"));
encoders.Add(new EncoderSettings("lame.exe (VBR)",
"mp3",
false,
"V9 V8 V7 V6 V5 V4 V3 V2 V1 V0",
"V2",
"lame.exe",
"--vbr-new -%M - %O"));
encoders.Add(new EncoderSettings("lame.exe (CBR)",
"mp3",
false,
"96 128 192 256 320",
"256",
"lame.exe",
"-m s -q 0 -b %M --noreplaygain - %O"));
encoders.Add(new EncoderSettings("oggenc.exe",
"ogg",
false,
"-1 -0.5 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5 8",
"3",
"oggenc.exe",
"-q %M - -o %O"));
encoders.Add(new EncoderSettings("opusenc.exe",
"opus",
false,
"6 16 32 48 64 96 128 192 256",
"128",
"opusenc.exe",
"--bitrate %M - %O"));
encoders.Add(new EncoderSettings("neroAacEnc.exe",
"m4a",
false,
"0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9",
"0.4",
"neroAacEnc.exe",
"-q %M -if - -of %O"));
encoders.Add(new EncoderSettings("qaac.exe (tvbr)",
"m4a",
false,
"10 20 30 40 50 60 70 80 90 100 110 127",
"80",
"qaac.exe",
"-s -V %M -q 2 - -o %O"));
decoders.Add(new DecoderSettings("takc.exe", "tak", "takc.exe", "-d %I -"));
decoders.Add(new DecoderSettings("ffmpeg.exe", "m4a", "ffmpeg.exe", "-v 0 -i %I -f wav -"));
}
// !!!
encodersViewModel = new EncoderListViewModel(encoders);
decodersViewModel = new DecoderListViewModel(decoders);
formats = new Dictionary<string, CUEToolsFormat>();
formats.Add("flac",
new CUEToolsFormat("flac",
CUEToolsTagger.TagLibSharp,
true,
false,
true,
true,
encodersViewModel.GetDefault("flac", true),
null,
decodersViewModel.GetDefault("flac")));
formats.Add("wv",
new CUEToolsFormat("wv",
CUEToolsTagger.TagLibSharp,
true,
false,
true,
true,
encodersViewModel.GetDefault("wv", true),
null,
decodersViewModel.GetDefault("wv")));
formats.Add("ape",
new CUEToolsFormat("ape",
CUEToolsTagger.TagLibSharp,
true,
false,
true,
true,
encodersViewModel.GetDefault("ape", true),
null,
decodersViewModel.GetDefault("ape")));
formats.Add("tta",
new CUEToolsFormat("tta",
CUEToolsTagger.APEv2,
true,
false,
false,
true,
encodersViewModel.GetDefault("tta", true),
null,
decodersViewModel.GetDefault("tta")));
formats.Add("m2ts",
new CUEToolsFormat("m2ts",
CUEToolsTagger.APEv2,
true,
false,
false,
true,
null,
null,
decodersViewModel.GetDefault("m2ts")));
formats.Add("mpls",
new CUEToolsFormat("mpls",
CUEToolsTagger.APEv2,
true,
false,
false,
true,
null,
null,
decodersViewModel.GetDefault("mpls")));
formats.Add("wav",
new CUEToolsFormat("wav",
CUEToolsTagger.TagLibSharp,
true,
false,
false,
true,
encodersViewModel.GetDefault("wav", true),
null,
decodersViewModel.GetDefault("wav")));
formats.Add("m4a",
new CUEToolsFormat("m4a",
CUEToolsTagger.TagLibSharp,
true,
true,
false,
true,
encodersViewModel.GetDefault("m4a", true),
encodersViewModel.GetDefault("m4a", false),
decodersViewModel.GetDefault("m4a")));
formats.Add("tak",
new CUEToolsFormat("tak",
CUEToolsTagger.APEv2,
true,
false,
true,
true,
encodersViewModel.GetDefault("tak", true),
null,
decodersViewModel.GetDefault("tak")));
formats.Add("wma",
new CUEToolsFormat("wma",
CUEToolsTagger.TagLibSharp,
true,
true,
false,
true,
encodersViewModel.GetDefault("wma", true),
encodersViewModel.GetDefault("wma", false),
decodersViewModel.GetDefault("wma")));
formats.Add("mp3",
new CUEToolsFormat("mp3",
CUEToolsTagger.TagLibSharp,
false,
true,
false,
true,
null,
encodersViewModel.GetDefault("mp3", false),
null));
formats.Add("ogg",
new CUEToolsFormat("ogg",
CUEToolsTagger.TagLibSharp,
false,
true,
false,
true,
null,
encodersViewModel.GetDefault("ogg", false),
null));
formats.Add("opus",
new CUEToolsFormat("opus",
CUEToolsTagger.TagLibSharp,
false,
true,
false,
true,
null,
encodersViewModel.GetDefault("opus", false),
null));
formats.Add("mlp",
new CUEToolsFormat("mlp",
CUEToolsTagger.APEv2,
true,
false,
false,
false,
null,
null,
decodersViewModel.GetDefault("mlp")));
formats.Add("aob",
new CUEToolsFormat("aob",
CUEToolsTagger.APEv2,
true,
false,
false,
false,
null,
null,
decodersViewModel.GetDefault("aob")));
}
}

View File

@@ -1,82 +1,98 @@
using System;
using System.Collections.Generic;
namespace CUETools.Codecs
namespace CUETools.Codecs;
public interface IAudioSource
{
public interface IAudioSource
{
IAudioDecoderSettings Settings { get; }
IAudioDecoderSettings Settings { get; }
AudioPCMConfig PCM { get; }
string Path { get; }
AudioPCMConfig PCM { get; }
string Path { get; }
TimeSpan Duration { get; }
long Length { get; }
long Position { get; set; }
long Remaining { get; }
TimeSpan Duration { get; }
long Length { get; }
long Position { get; set; }
long Remaining { get; }
int Read(AudioBuffer buffer, int maxLength);
void Close();
}
int Read(AudioBuffer buffer, int maxLength);
void Close();
}
public interface IAudioTitle
public interface IAudioTitle
{
List<TimeSpan> Chapters { get; }
AudioPCMConfig PCM { get; }
string Codec { get; }
string Language { get; }
int StreamId { get; }
//IAudioSource Open { get; }
}
public interface IAudioTitleSet
{
List<IAudioTitle> AudioTitles { get; }
}
public static class IAudioTitleExtensions
{
public static TimeSpan GetDuration(this IAudioTitle title)
{
List<TimeSpan> Chapters { get; }
AudioPCMConfig PCM { get; }
string Codec { get; }
string Language { get; }
int StreamId { get; }
//IAudioSource Open { get; }
List<TimeSpan> chapters = title.Chapters;
return chapters[chapters.Count - 1];
}
public interface IAudioTitleSet
public static string GetRateString(this IAudioTitle title)
{
List<IAudioTitle> AudioTitles { get; }
int sr = title.PCM.SampleRate;
if(sr % 1000 == 0) return $"{sr / 1000}KHz";
if(sr % 100 == 0) return $"{sr / 100}.{sr / 100 % 10}KHz";
return $"{sr}Hz";
}
public static class IAudioTitleExtensions
public static string GetFormatString(this IAudioTitle title)
{
public static TimeSpan GetDuration(this IAudioTitle title)
switch(title.PCM.ChannelCount)
{
var chapters = title.Chapters;
return chapters[chapters.Count - 1];
case 1:
return "mono";
case 2:
return "stereo";
default:
return "multi-channel";
}
public static string GetRateString(this IAudioTitle title)
{
var sr = title.PCM.SampleRate;
if (sr % 1000 == 0) return $"{sr / 1000}KHz";
if (sr % 100 == 0) return $"{sr / 100}.{(sr / 100) % 10}KHz";
return $"{sr}Hz";
}
public static string GetFormatString(this IAudioTitle title)
{
switch (title.PCM.ChannelCount)
{
case 1: return "mono";
case 2: return "stereo";
default: return "multi-channel";
}
}
}
public class SingleAudioTitle : IAudioTitle
{
public SingleAudioTitle(IAudioSource source) { this.source = source; }
public List<TimeSpan> Chapters => new List<TimeSpan> { TimeSpan.Zero, source.Duration };
public AudioPCMConfig PCM => source.PCM;
public string Codec => source.Settings.Extension;
public string Language => "";
public int StreamId => 0;
IAudioSource source;
}
public class SingleAudioTitleSet : IAudioTitleSet
{
public SingleAudioTitleSet(IAudioSource source) { this.source = source; }
public List<IAudioTitle> AudioTitles => new List<IAudioTitle> { new SingleAudioTitle(source) };
IAudioSource source;
}
}
public class SingleAudioTitle : IAudioTitle
{
readonly IAudioSource source;
public SingleAudioTitle(IAudioSource source) => this.source = source;
#region IAudioTitle Members
public List<TimeSpan> Chapters => [TimeSpan.Zero, source.Duration];
public AudioPCMConfig PCM => source.PCM;
public string Codec => source.Settings.Extension;
public string Language => "";
public int StreamId => 0;
#endregion
}
public class SingleAudioTitleSet : IAudioTitleSet
{
readonly IAudioSource source;
public SingleAudioTitleSet(IAudioSource source) => this.source = source;
#region IAudioTitleSet Members
public List<IAudioTitle> AudioTitles => [new SingleAudioTitle(source)];
#endregion
}

View File

@@ -1,56 +1,56 @@
using System;
using System.ComponentModel;
using System.Reflection;
namespace CUETools.Codecs
namespace CUETools.Codecs;
/// <summary>
/// Localized description attribute
/// </summary>
[AttributeUsage(AttributeTargets.All)]
public class SRDescriptionAttribute : DescriptionAttribute
{
/// <summary>
/// Localized description attribute
/// </summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public class SRDescriptionAttribute : DescriptionAttribute
/// <summary>
/// Resource manager to use;
/// </summary>
readonly Type SR;
/// <summary>
/// Store a flag indicating whether this has been localized
/// </summary>
bool localized;
/// <summary>
/// Construct the description attribute
/// </summary>
/// <param name="text"></param>
public SRDescriptionAttribute(Type SR, string text) : base(text)
{
/// <summary>
/// Store a flag indicating whether this has been localized
/// </summary>
private bool localized;
localized = false;
this.SR = SR;
}
/// <summary>
/// Resource manager to use;
/// </summary>
private Type SR;
/// <summary>
/// Override the return of the description text to localize the text
/// </summary>
public override string Description
{
get
{
if(!localized)
{
localized = true;
/// <summary>
/// Construct the description attribute
/// </summary>
/// <param name="text"></param>
public SRDescriptionAttribute(Type SR, string text)
: base(text)
{
this.localized = false;
this.SR = SR;
}
DescriptionValue = SR.InvokeMember(DescriptionValue,
BindingFlags.GetProperty |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic,
null,
null,
[]) as string;
}
/// <summary>
/// Override the return of the description text to localize the text
/// </summary>
public override string Description
{
get
{
if (!localized)
{
localized = true;
this.DescriptionValue = SR.InvokeMember(
this.DescriptionValue,
System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic,
null,
null,
new object[] { }) as string;
}
return base.Description;
}
}
}
}
return base.Description;
}
}
}

View File

@@ -2,179 +2,181 @@
using System.Collections.Generic;
using System.IO;
namespace CUETools.Codecs.WAV
namespace CUETools.Codecs.WAV;
public class AudioEncoder : IAudioDest
{
public class AudioEncoder : IAudioDest
readonly EncoderSettings m_settings;
BinaryWriter _bw;
List<uint> _chunkFCCs;
List<byte[]> _chunks;
long _finalSampleCount = -1;
bool _headersWritten;
Stream _IO;
long hdrLen;
public AudioEncoder(EncoderSettings settings, string path, Stream IO = null)
{
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;
}
m_settings = settings;
Path = path;
_IO = IO ?? new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
_bw = new BinaryWriter(_IO);
}
}
public long Position { get; private set; }
#region IAudioDest Members
public long FinalSampleCount
{
set => _finalSampleCount = value;
}
public IAudioEncoderSettings Settings => m_settings;
public string Path { get; }
public void Close()
{
if(_finalSampleCount <= 0 && _IO.CanSeek)
{
long dataLen = Position * 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 && Position != _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);
Position += buff.Length;
}
#endregion
public void WriteChunk(uint fcc, byte[] data)
{
if(Position > 0) throw new Exception("data already written, no chunks allowed");
if(_chunks == null)
{
_chunks = [];
_chunkFCCs = [];
}
_chunkFCCs.Add(fcc);
_chunks.Add(data);
hdrLen += 8 + data.Length + (data.Length & 1);
}
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;
var dataLen = (uint)(_finalSampleCount * Settings.PCM.BlockAlign);
uint dataLenPadded = dataLen + (dataLen & 1);
_bw.Write(fccRIFF);
if(_finalSampleCount <= 0)
_bw.Write(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(var 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(0xffffffff);
else
_bw.Write(dataLen);
_headersWritten = true;
}
}