using System; namespace CUETools.Codecs { public class AudioBuffer { #region Static Methods public static unsafe void FLACSamplesToBytes_16(int[,] inSamples, int inSampleOffset, byte* outSamples, int sampleCount, int channelCount) { int loopCount = sampleCount * channelCount; if (inSamples.GetLength(0) - inSampleOffset < sampleCount) throw new IndexOutOfRangeException(); fixed (int* pInSamplesFixed = &inSamples[inSampleOffset, 0]) { int* pInSamples = pInSamplesFixed; short* pOutSamples = (short*)outSamples; for (int i = 0; i < loopCount; i++) pOutSamples[i] = (short)pInSamples[i]; //*(pOutSamples++) = (short)*(pInSamples++); } } public static unsafe void FLACSamplesToBytes_16(int[,] inSamples, int inSampleOffset, byte[] outSamples, int outByteOffset, int sampleCount, int channelCount) { int loopCount = sampleCount * channelCount; if ((inSamples.GetLength(0) - inSampleOffset < sampleCount) || (outSamples.Length - outByteOffset < loopCount * 2)) { throw new IndexOutOfRangeException(); } fixed (byte* pOutSamplesFixed = &outSamples[outByteOffset]) FLACSamplesToBytes_16(inSamples, inSampleOffset, pOutSamplesFixed, sampleCount, channelCount); } public static unsafe void FLACSamplesToBytes_24(int[,] inSamples, int inSampleOffset, byte[] outSamples, int outByteOffset, int sampleCount, int channelCount, int wastedBits) { int loopCount = sampleCount * channelCount; if ((inSamples.GetLength(0) - inSampleOffset < sampleCount) || (outSamples.Length - outByteOffset < loopCount * 3)) { throw new IndexOutOfRangeException(); } fixed (int* pInSamplesFixed = &inSamples[inSampleOffset, 0]) { fixed (byte* pOutSamplesFixed = &outSamples[outByteOffset]) { int* pInSamples = pInSamplesFixed; byte* pOutSamples = pOutSamplesFixed; for (int i = 0; i < loopCount; i++) { uint sample_out = (uint)*(pInSamples++) << wastedBits; *(pOutSamples++) = (byte)(sample_out & 0xFF); sample_out >>= 8; *(pOutSamples++) = (byte)(sample_out & 0xFF); sample_out >>= 8; *(pOutSamples++) = (byte)(sample_out & 0xFF); } } } } public static unsafe void FloatToBytes_16(float[,] inSamples, int inSampleOffset, byte[] outSamples, int outByteOffset, int sampleCount, int channelCount) { int loopCount = sampleCount * channelCount; if ((inSamples.GetLength(0) - inSampleOffset < sampleCount) || (outSamples.Length - outByteOffset < loopCount * 2)) { throw new IndexOutOfRangeException(); } fixed (float* pInSamplesFixed = &inSamples[inSampleOffset, 0]) { fixed (byte* pOutSamplesFixed = &outSamples[outByteOffset]) { float* pInSamples = pInSamplesFixed; short* pOutSamples = (short*)pOutSamplesFixed; for (int i = 0; i < loopCount; i++) { *(pOutSamples++) = (short)(32758 * (*(pInSamples++))); } } } } public static unsafe void FloatToBytes(float[,] inSamples, int inSampleOffset, byte[] outSamples, int outByteOffset, int sampleCount, int channelCount, int bitsPerSample) { if (bitsPerSample == 16) FloatToBytes_16(inSamples, inSampleOffset, outSamples, outByteOffset, sampleCount, channelCount); //else if (bitsPerSample > 16 && bitsPerSample <= 24) // FLACSamplesToBytes_24(inSamples, inSampleOffset, outSamples, outByteOffset, sampleCount, channelCount, 24 - bitsPerSample); else if (bitsPerSample == 32) Buffer.BlockCopy(inSamples, inSampleOffset * 4 * channelCount, outSamples, outByteOffset, sampleCount * 4 * channelCount); else throw new Exception("Unsupported bitsPerSample value"); } public static unsafe void FLACSamplesToBytes(int[,] inSamples, int inSampleOffset, byte[] outSamples, int outByteOffset, int sampleCount, int channelCount, int bitsPerSample) { if (bitsPerSample == 16) FLACSamplesToBytes_16(inSamples, inSampleOffset, outSamples, outByteOffset, sampleCount, channelCount); else if (bitsPerSample > 16 && bitsPerSample <= 24) FLACSamplesToBytes_24(inSamples, inSampleOffset, outSamples, outByteOffset, sampleCount, channelCount, 24 - bitsPerSample); else throw new Exception("Unsupported bitsPerSample value"); } public static unsafe void FLACSamplesToBytes(int[,] inSamples, int inSampleOffset, byte* outSamples, int sampleCount, int channelCount, int bitsPerSample) { if (bitsPerSample == 16) FLACSamplesToBytes_16(inSamples, inSampleOffset, outSamples, sampleCount, channelCount); else throw new Exception("Unsupported bitsPerSample value"); } public static unsafe void Bytes16ToFloat(byte[] inSamples, int inByteOffset, float[,] outSamples, int outSampleOffset, int sampleCount, int channelCount) { int loopCount = sampleCount * channelCount; if ((inSamples.Length - inByteOffset < loopCount * 2) || (outSamples.GetLength(0) - outSampleOffset < sampleCount)) throw new IndexOutOfRangeException(); fixed (byte* pInSamplesFixed = &inSamples[inByteOffset]) { fixed (float* pOutSamplesFixed = &outSamples[outSampleOffset, 0]) { short* pInSamples = (short*)pInSamplesFixed; float* pOutSamples = pOutSamplesFixed; for (int i = 0; i < loopCount; i++) *(pOutSamples++) = *(pInSamples++) / 32768.0f; } } } public static unsafe void BytesToFLACSamples_16(byte[] inSamples, int inByteOffset, int[,] outSamples, int outSampleOffset, int sampleCount, int channelCount) { int loopCount = sampleCount * 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 static unsafe void BytesToFLACSamples_24(byte[] inSamples, int inByteOffset, int[,] outSamples, int outSampleOffset, int sampleCount, int channelCount, int wastedBits) { int loopCount = sampleCount * channelCount; if ((inSamples.Length - inByteOffset < loopCount * 3) || (outSamples.GetLength(0) - outSampleOffset < sampleCount)) throw new IndexOutOfRangeException(); fixed (byte* pInSamplesFixed = &inSamples[inByteOffset]) { fixed (int* pOutSamplesFixed = &outSamples[outSampleOffset, 0]) { byte* pInSamples = (byte*)pInSamplesFixed; int* pOutSamples = pOutSamplesFixed; for (int i = 0; i < loopCount; i++) { int sample = (int)*(pInSamples++); sample += (int)*(pInSamples++) << 8; sample += (int)*(pInSamples++) << 16; *(pOutSamples++) = (sample << 8) >> (8 + wastedBits); } } } } public static unsafe void BytesToFLACSamples(byte[] inSamples, int inByteOffset, int[,] outSamples, int outSampleOffset, int sampleCount, int channelCount, int bitsPerSample) { if (bitsPerSample == 16) BytesToFLACSamples_16(inSamples, inByteOffset, outSamples, outSampleOffset, sampleCount, channelCount); else if (bitsPerSample > 16 && bitsPerSample <= 24) BytesToFLACSamples_24(inSamples, inByteOffset, outSamples, outSampleOffset, sampleCount, channelCount, 24 - bitsPerSample); else throw new Exception("Unsupported bitsPerSample value"); } #endregion private int[,] samples; private float[,] fsamples; private byte[] bytes; private int length; private int size; private AudioPCMConfig pcm; private bool dataInSamples = false; private bool dataInBytes = false; private bool dataInFloat = false; public int Length { get { return length; } set { length = value; } } public int Size { get { return size; } } public AudioPCMConfig PCM { get { return pcm; } } public int ByteLength { get { return length * pcm.BlockAlign; } } public int[,] Samples { get { if (samples == null || samples.GetLength(0) < length) samples = new int[size, pcm.ChannelCount]; if (!dataInSamples && dataInBytes && length != 0) BytesToFLACSamples(bytes, 0, samples, 0, length, pcm.ChannelCount, pcm.BitsPerSample); dataInSamples = true; return samples; } } public float[,] Float { get { if (fsamples == null || fsamples.GetLength(0) < length) fsamples = new float[size, pcm.ChannelCount]; if (!dataInFloat && dataInBytes && length != 0) { if (pcm.BitsPerSample == 16) Bytes16ToFloat(bytes, 0, fsamples, 0, length, pcm.ChannelCount); //else if (pcm.BitsPerSample > 16 && PCM.BitsPerSample <= 24) // BytesToFLACSamples_24(bytes, 0, fsamples, 0, length, pcm.ChannelCount, 24 - pcm.BitsPerSample); else if (pcm.BitsPerSample == 32) Buffer.BlockCopy(bytes, 0, fsamples, 0, length * 4 * pcm.ChannelCount); else throw new Exception("Unsupported bitsPerSample value"); } dataInFloat = true; return fsamples; } } public byte[] Bytes { get { if (bytes == null || bytes.Length < length * pcm.BlockAlign) bytes = new byte[size * pcm.BlockAlign]; if (!dataInBytes && length != 0) { if (dataInSamples) FLACSamplesToBytes(samples, 0, bytes, 0, length, pcm.ChannelCount, pcm.BitsPerSample); else if (dataInFloat) FloatToBytes(fsamples, 0, bytes, 0, length, pcm.ChannelCount, pcm.BitsPerSample); } dataInBytes = true; return bytes; } } public AudioBuffer(AudioPCMConfig _pcm, int _size) { pcm = _pcm; size = _size; length = 0; } public AudioBuffer(AudioPCMConfig _pcm, int[,] _samples, int _length) { pcm = _pcm; // assert _samples.GetLength(1) == pcm.ChannelCount Prepare(_samples, _length); } public AudioBuffer(AudioPCMConfig _pcm, byte[] _bytes, int _length) { pcm = _pcm; Prepare(_bytes, _length); } public AudioBuffer(IAudioSource source, int _size) { pcm = source.PCM; size = _size; } public void Prepare(IAudioDest dest) { if (dest.PCM.ChannelCount != pcm.ChannelCount || dest.PCM.BitsPerSample != pcm.BitsPerSample) throw new Exception("AudioBuffer format mismatch"); } public void Prepare(IAudioSource source, int maxLength) { if (source.PCM.ChannelCount != pcm.ChannelCount || source.PCM.BitsPerSample != pcm.BitsPerSample) throw new Exception("AudioBuffer format mismatch"); length = size; if (maxLength >= 0) length = Math.Min(length, maxLength); if (source.Remaining >= 0) length = (int)Math.Min((long)length, source.Remaining); dataInBytes = false; dataInSamples = false; dataInFloat = false; } public void Prepare(int maxLength) { length = size; if (maxLength >= 0) length = Math.Min(length, maxLength); dataInBytes = false; dataInSamples = false; dataInFloat = false; } public void Prepare(int[,] _samples, int _length) { length = _length; size = _samples.GetLength(0); samples = _samples; dataInSamples = true; dataInBytes = false; dataInFloat = false; if (length > size) throw new Exception("Invalid length"); } public void Prepare(byte[] _bytes, int _length) { length = _length; size = _bytes.Length / PCM.BlockAlign; bytes = _bytes; dataInSamples = false; dataInBytes = true; dataInFloat = false; if (length > size) throw new Exception("Invalid length"); } internal unsafe void Load(int dstOffset, AudioBuffer src, int srcOffset, int copyLength) { if (dataInBytes) Buffer.BlockCopy(src.Bytes, srcOffset * pcm.BlockAlign, Bytes, dstOffset * pcm.BlockAlign, copyLength * pcm.BlockAlign); if (dataInSamples) Buffer.BlockCopy(src.Samples, srcOffset * pcm.ChannelCount * 4, Samples, dstOffset * pcm.ChannelCount * 4, copyLength * pcm.ChannelCount * 4); if (dataInFloat) Buffer.BlockCopy(src.Float, srcOffset * pcm.ChannelCount * 4, Float, dstOffset * pcm.ChannelCount * 4, copyLength * pcm.ChannelCount * 4); } public unsafe void Prepare(AudioBuffer _src, int _offset, int _length) { length = Math.Min(size, _src.Length - _offset); if (_length >= 0) length = Math.Min(length, _length); dataInBytes = false; dataInFloat = false; dataInSamples = false; if (_src.dataInBytes) dataInBytes = true; else if (_src.dataInSamples) dataInSamples = true; else if (_src.dataInFloat) dataInFloat = true; Load(0, _src, _offset, length); } public void Swap(AudioBuffer buffer) { if (pcm.BitsPerSample != buffer.PCM.BitsPerSample || pcm.ChannelCount != buffer.PCM.ChannelCount) throw new Exception("AudioBuffer format mismatch"); int[,] samplesTmp = samples; float[,] floatsTmp = fsamples; byte[] bytesTmp = bytes; fsamples = buffer.fsamples; samples = buffer.samples; bytes = buffer.bytes; length = buffer.length; size = buffer.size; dataInSamples = buffer.dataInSamples; dataInBytes = buffer.dataInBytes; dataInFloat = buffer.dataInFloat; buffer.samples = samplesTmp; buffer.bytes = bytesTmp; buffer.fsamples = floatsTmp; buffer.length = 0; buffer.dataInSamples = false; buffer.dataInBytes = false; buffer.dataInFloat = false; } unsafe public void Interlace(int pos, int* src1, int* src2, int n) { if (PCM.ChannelCount != 2) { throw new Exception("Must be stereo"); } if (PCM.BitsPerSample == 16) { fixed (byte* bs = Bytes) { int* res = ((int*)bs) + pos; for (int i = n; i > 0; i--) *(res++) = (*(src1++) & 0xffff) ^ (*(src2++) << 16); } } else if (PCM.BitsPerSample == 24) { fixed (byte* bs = Bytes) { byte* res = bs + pos * 6; for (int i = n; i > 0; i--) { uint sample_out = (uint)*(src1++); *(res++) = (byte)(sample_out & 0xFF); sample_out >>= 8; *(res++) = (byte)(sample_out & 0xFF); sample_out >>= 8; *(res++) = (byte)(sample_out & 0xFF); sample_out = (uint)*(src2++); *(res++) = (byte)(sample_out & 0xFF); sample_out >>= 8; *(res++) = (byte)(sample_out & 0xFF); sample_out >>= 8; *(res++) = (byte)(sample_out & 0xFF); } } } else { throw new Exception("Unsupported BPS"); } } //public void Clear() //{ // length = 0; //} } }