Files
cuetools.net/CUETools.Codecs.libwavpack/Reader.cs

247 lines
7.9 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using CUETools.Codecs;
using Newtonsoft.Json;
namespace CUETools.Codecs.libwavpack
{
[JsonObject(MemberSerialization.OptIn)]
public class DecoderSettings : IAudioDecoderSettings
{
#region IAudioDecoderSettings implementation
[Browsable(false)]
public string Extension => "wv";
[Browsable(false)]
public string Name => "libwavpack";
[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 unsafe class AudioDecoder : IAudioSource
{
private readonly void* IO_ID_WV = ((IntPtr)1).ToPointer();
private readonly void* IO_ID_WVC = ((IntPtr)2).ToPointer();
public AudioDecoder(DecoderSettings settings, string path, Stream IO, Stream IO_WVC)
{
m_settings = settings;
m_read_bytes = ReadCallback;
m_get_pos = TellCallback;
m_set_pos_abs = SeekCallback;
m_set_pos_rel = SeekRelativeCallback;
m_push_back_byte = PushBackCallback;
m_get_length = LengthCallback;
m_can_seek = CanSeekCallback;
m_ioReader = (WavpackStreamReader64*)Marshal.AllocHGlobal(sizeof(WavpackStreamReader64)).ToPointer();
m_ioReader->read_bytes = Marshal.GetFunctionPointerForDelegate(m_read_bytes);
m_ioReader->write_bytes = IntPtr.Zero;
m_ioReader->get_pos = Marshal.GetFunctionPointerForDelegate(m_get_pos);
m_ioReader->set_pos_abs = Marshal.GetFunctionPointerForDelegate(m_set_pos_abs);
m_ioReader->set_pos_rel = Marshal.GetFunctionPointerForDelegate(m_set_pos_rel);
m_ioReader->push_back_byte = Marshal.GetFunctionPointerForDelegate(m_push_back_byte);
m_ioReader->get_length = Marshal.GetFunctionPointerForDelegate(m_get_length);
m_ioReader->can_seek = Marshal.GetFunctionPointerForDelegate(m_can_seek);
m_ioReader->truncate_here = IntPtr.Zero;
m_ioReader->close = IntPtr.Zero;
_IO_ungetc = _IO_WVC_ungetc = -1;
_path = path;
_IO = (IO != null) ? IO : new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.Read);
_IO_WVC = (IO != null) ? IO_WVC : File.Exists (path+"c") ? new FileStream (path+"c", FileMode.Open, FileAccess.Read, FileShare.Read) : null;
string errorMessage;
_wpc = wavpackdll.WavpackOpenFileInputEx64(m_ioReader, IO_ID_WV, IO_ID_WVC, out errorMessage, OpenFlags.OPEN_WVC, 0);
if (_wpc == null) {
throw new Exception("Unable to initialize the decoder: " + errorMessage);
}
pcm = new AudioPCMConfig(
wavpackdll.WavpackGetBitsPerSample(_wpc),
wavpackdll.WavpackGetNumChannels(_wpc),
(int)wavpackdll.WavpackGetSampleRate(_wpc),
(AudioPCMConfig.SpeakerConfig)wavpackdll.WavpackGetChannelMask(_wpc));
_sampleCount = wavpackdll.WavpackGetNumSamples64(_wpc);
_sampleOffset = 0;
}
public AudioDecoder(DecoderSettings settings, string path, Stream IO = null)
: this(settings, path, IO, null)
{}
private DecoderSettings m_settings;
public IAudioDecoderSettings Settings => m_settings;
public AudioPCMConfig PCM => pcm;
public string Path => _path;
public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
public long Length => _sampleCount;
public long Position
{
get => _sampleOffset;
set
{
_sampleOffset = value;
if (0 == wavpackdll.WavpackSeekSample64(_wpc, value))
throw new Exception("unable to seek: " + wavpackdll.WavpackGetErrorMessage(_wpc));
}
}
public long Remaining => _sampleCount - _sampleOffset;
public void Close()
{
if (_wpc != null)
_wpc = wavpackdll.WavpackCloseFile(_wpc);
if (_IO != null)
{
_IO.Close ();
_IO = null;
}
if (_IO_WVC != null)
{
_IO_WVC.Close ();
_IO_WVC = null;
}
Marshal.FreeHGlobal((IntPtr)m_ioReader);
m_ioReader = null;
}
public int Read(AudioBuffer buff, int maxLength)
{
buff.Prepare(this, maxLength);
fixed (int*pSampleBuffer = &buff.Samples[0,0])
{
uint samplesRead = wavpackdll.WavpackUnpackSamples(_wpc, pSampleBuffer, (uint)buff.Length);
_sampleOffset += samplesRead;
if (samplesRead != buff.Length)
throw new Exception("Decoder returned a different number of samples than requested.");
}
return buff.Length;
}
private int ReadCallback(void* id, void* data, int bcount)
{
Stream IO = (id == IO_ID_WVC) ? _IO_WVC : _IO;
int IO_ungetc = (id == IO_ID_WVC) ? _IO_WVC_ungetc : _IO_ungetc;
int unget_len = 0;
if (IO_ungetc != -1)
{
*(byte*)data = (byte) IO_ungetc;
if (IO == _IO)
_IO_ungetc = -1;
else
_IO_WVC_ungetc = -1;
bcount--;
if (bcount <= 0)
return 1;
data = 1 + (byte*)data;
unget_len = 1;
}
if (_readBuffer == null || _readBuffer.Length < bcount)
_readBuffer = new byte[Math.Max(bcount, 0x4000)];
int len = IO.Read(_readBuffer, 0, bcount);
if (len > 0) Marshal.Copy(_readBuffer, 0, (IntPtr)data, len);
return len + unget_len;
}
long TellCallback(void* id)
{
Stream IO = (id == IO_ID_WVC) ? _IO_WVC : _IO;
return IO.Position;
}
int SeekCallback(void* id, long pos)
{
Stream IO = (id == IO_ID_WVC) ? _IO_WVC : _IO;
IO.Position = pos;
return 0;
}
int SeekRelativeCallback(void* id, long delta, int mode)
{
Stream IO = (id == IO_ID_WVC) ? _IO_WVC : _IO;
IO.Seek(delta, (SeekOrigin)(mode));
return 0;
}
int PushBackCallback(void* id, int c)
{
Stream IO = (id == IO_ID_WVC) ? _IO_WVC : _IO;
if (IO == _IO)
{
if (_IO_ungetc != -1)
throw new Exception("Double PushBackCallback unsupported.");
_IO_ungetc = c;
}
else
{
if (_IO_WVC_ungetc != -1)
throw new Exception("Double PushBackCallback unsupported.");
_IO_WVC_ungetc = c;
}
return 0;
}
long LengthCallback(void* id)
{
Stream IO = (id == IO_ID_WVC) ? _IO_WVC : _IO;
return IO.Length;
}
int CanSeekCallback(void* id)
{
Stream IO = (id == IO_ID_WVC) ? _IO_WVC : _IO;
return IO.CanSeek ? 1 : 0;
}
WavpackContext* _wpc;
long _sampleCount, _sampleOffset;
Stream _IO;
Stream _IO_WVC;
string _path;
int _IO_ungetc, _IO_WVC_ungetc;
AudioPCMConfig pcm;
WavpackStreamReader64* m_ioReader;
DecoderReadDelegate m_read_bytes;
DecoderTellDelegate64 m_get_pos;
DecoderSeekDelegate64 m_set_pos_abs;
DecoderSeekRelativeDelegate64 m_set_pos_rel;
DecoderPushBackDelegate m_push_back_byte;
DecoderLengthDelegate64 m_get_length;
DecoderCanSeekDelegate m_can_seek;
byte[] _readBuffer;
}
}