diff --git a/CUETools.Codecs/Codecs.cs b/CUETools.Codecs/Codecs.cs
index 623610b..5bdda5c 100644
--- a/CUETools.Codecs/Codecs.cs
+++ b/CUETools.Codecs/Codecs.cs
@@ -186,12 +186,14 @@ namespace CUETools.Codecs
public class AudioBuffer
{
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
{
@@ -236,14 +238,39 @@ namespace CUETools.Codecs
}
}
+ 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
+ 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 && dataInSamples && length != 0)
- FLACSamplesToBytes(samples, 0, bytes, 0, length, pcm.ChannelCount, pcm.BitsPerSample);
+ 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;
}
@@ -292,6 +319,17 @@ namespace CUETools.Codecs
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)
@@ -301,6 +339,7 @@ namespace CUETools.Codecs
samples = _samples;
dataInSamples = true;
dataInBytes = false;
+ dataInFloat = false;
if (length > size)
throw new Exception("Invalid length");
}
@@ -312,6 +351,7 @@ namespace CUETools.Codecs
bytes = _bytes;
dataInSamples = false;
dataInBytes = true;
+ dataInFloat = false;
if (length > size)
throw new Exception("Invalid length");
}
@@ -321,20 +361,23 @@ namespace CUETools.Codecs
length = Math.Min(size, _src.Length - _offset);
if (_length >= 0)
length = Math.Min(length, _length);
- if (_src.dataInSamples)
+ dataInBytes = false;
+ dataInFloat = false;
+ dataInSamples = false;
+ if (_src.dataInBytes)
{
- dataInBytes = false;
- dataInSamples = true;
- fixed (int* dest = Samples, src = &_src.Samples[_offset, 0])
- AudioSamples.MemCpy(dest, src, Length * pcm.ChannelCount);
- }
- else
- {
- dataInSamples = false;
dataInBytes = true;
fixed (byte* dest = Bytes, src = &_src.Bytes[_offset * pcm.BlockAlign])
AudioSamples.MemCpy(dest, src, length * pcm.BlockAlign);
}
+ else if (_src.dataInSamples)
+ {
+ dataInSamples = true;
+ fixed (int* dest = Samples, src = &_src.Samples[_offset, 0])
+ AudioSamples.MemCpy(dest, src, Length * pcm.ChannelCount);
+ }
+ else if (_src.dataInFloat)
+ throw new NotImplementedException();
}
public void Swap(AudioBuffer buffer)
@@ -343,20 +386,25 @@ namespace CUETools.Codecs
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)
@@ -450,6 +498,43 @@ namespace CUETools.Codecs
}
}
+ 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
+ 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)
{
@@ -470,6 +555,27 @@ namespace CUETools.Codecs
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)
{
@@ -647,6 +753,62 @@ namespace CUETools.Codecs
public const uint UINT32_MAX = 0xffffffff;
}
+ ///
+ /// Represents the interface to a device that can play a WaveFile
+ ///
+ public interface IWavePlayer : IDisposable, IAudioDest
+ {
+ ///
+ /// Begin playback
+ ///
+ void Play();
+
+ ///
+ /// Stop playback
+ ///
+ void Stop();
+
+ ///
+ /// Pause Playback
+ ///
+ void Pause();
+
+ ///
+ /// Current playback state
+ ///
+ PlaybackState PlaybackState { get; }
+
+ ///
+ /// The volume 1.0 is full scale
+ ///
+ float Volume { get; set; }
+
+ ///
+ /// Indicates that playback has gone into a stopped state due to
+ /// reaching the end of the input stream
+ ///
+ event EventHandler PlaybackStopped;
+ }
+
+ ///
+ /// Playback State
+ ///
+ public enum PlaybackState
+ {
+ ///
+ /// Stopped
+ ///
+ Stopped,
+ ///
+ /// Playing
+ ///
+ Playing,
+ ///
+ /// Paused
+ ///
+ Paused
+ }
+
public class DummyWriter : IAudioDest
{
AudioPCMConfig _pcm;
@@ -1352,6 +1514,31 @@ namespace CUETools.Codecs
}
}
+ public void Write(byte[] buff, int offs, int count)
+ {
+ while (count > 0)
+ {
+ int pos, chunk;
+ lock (this)
+ {
+ while (DataAvailable == 0 && !_eof)
+ Monitor.Wait(this);
+ if (DataAvailable == 0)
+ break;
+ pos = _start % _size;
+ chunk = Math.Min(DataAvailable, _size - pos);
+ }
+ if (flushOutput != null)
+ Array.Copy(_buffer, pos, buff, offs, chunk);
+ offs += chunk;
+ lock (this)
+ {
+ _start += chunk;
+ Monitor.Pulse(this);
+ }
+ }
+ }
+
private void FlushThread(object to)
{
while (true)
@@ -1466,6 +1653,76 @@ namespace CUETools.Codecs
}
}
+ public class CycilcBufferInputStream : Stream
+ {
+ CyclicBuffer _buffer;
+
+ public CycilcBufferInputStream(CyclicBuffer buffer)
+ {
+ _buffer = buffer;
+ }
+
+ public override bool CanRead
+ {
+ get { return true; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return false; }
+ }
+
+ public override long Length
+ {
+ get
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public override long Position
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+ public override void Close()
+ {
+ _buffer.Close();
+ }
+
+ public override void Flush()
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override int Read(byte[] array, int offset, int count)
+ {
+ _buffer.Write(array, offset, count);
+ return count;
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Write(byte[] array, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+ }
+
public class UserDefinedWriter : IAudioDest
{
string _path, _encoder, _encoderParams, _encoderMode;
@@ -1804,7 +2061,26 @@ namespace CUETools.Codecs
}
set
{
- throw new Exception("not supported");
+ if (value == _samplePos)
+ return;
+
+ lock (this)
+ {
+ _close = true;
+ Monitor.Pulse(this);
+ }
+ if (_workThread != null)
+ {
+ _workThread.Join();
+ _workThread = null;
+ }
+ _source.Position = value;
+ _samplePos = value;
+ _bufferPos = 0;
+ _haveData = false;
+ _close = false;
+ Go();
+ //throw new Exception("not supported");
}
}