diff --git a/APETagDotNet/APETagDotNet.cs b/APETagDotNet/APETagDotNet.cs index b7f7320..3d10d55 100644 --- a/APETagDotNet/APETagDotNet.cs +++ b/APETagDotNet/APETagDotNet.cs @@ -177,6 +177,7 @@ namespace APETagsDotNet { m_spIO = new FileStream(filename, FileMode.Open, isReadonly?FileAccess.Read:FileAccess.ReadWrite, FileShare.Read); m_spInfo = new FileInfo(filename); + _closeIO = true; m_lastModified = m_spInfo.LastWriteTime; m_bAnalyzed = false; m_aryFields = new APETagField[0]; @@ -185,9 +186,27 @@ namespace APETagsDotNet if (analyze) Analyze (); } + // create an APE tags object + // bAnalyze determines whether it will analyze immediately or on the first request + // be careful with multiple threads / file pointer movement if you don't analyze immediately + public APETagDotNet(Stream IO, bool analyze) + { + m_spIO = IO; + _closeIO = false; + m_bAnalyzed = false; + m_aryFields = new APETagField[0]; + m_nTagBytes = 0; + m_bIgnoreReadOnly = false; + if (analyze) Analyze(); + } + public void Close () { - m_spIO.Close (); + if (_closeIO) + { + m_spIO.Close(); + m_spIO = null; + } ClearFields (); } @@ -629,7 +648,7 @@ namespace APETagsDotNet //int GetFieldID3String(string pFieldName, char * pBuffer, int nBytes); // private data - private FileStream m_spIO; + private Stream m_spIO; private FileInfo m_spInfo; private DateTime m_lastModified; private bool m_bAnalyzed; @@ -639,5 +658,6 @@ namespace APETagsDotNet private int m_nAPETagVersion; //private bool m_bHasID3Tag; private bool m_bIgnoreReadOnly; + private bool _closeIO; }; } diff --git a/CUEToolsLib/AudioReadWrite.cs b/CUEToolsLib/AudioReadWrite.cs index faea071..68bf233 100644 --- a/CUEToolsLib/AudioReadWrite.cs +++ b/CUEToolsLib/AudioReadWrite.cs @@ -18,7 +18,7 @@ namespace CUEToolsLib { case ".flac": return new FLACReader(path, null); case ".wv": - return new WavPackReader(path); + return new WavPackReader(path, null, null); case ".ape": return new APEReader(path); case ".m4a": @@ -37,6 +37,8 @@ namespace CUEToolsLib { #if !MONO case ".flac": return new FLACReader(path, IO); + case ".wv": + return new WavPackReader(path, IO, null); case ".m4a": return new ALACReader(path, IO); #endif @@ -416,8 +418,8 @@ namespace CUEToolsLib { class WavPackReader : IAudioSource { WavPackDotNet.WavPackReader _wavPackReader; - public WavPackReader(string path) { - _wavPackReader = new WavPackDotNet.WavPackReader(path); + public WavPackReader(string path, Stream IO, Stream IO_WVC) { + _wavPackReader = new WavPackDotNet.WavPackReader(path, IO, IO_WVC); } public void Close() { diff --git a/FLACDotNet/flacdotnet.cpp b/FLACDotNet/flacdotnet.cpp index 30a6f42..1175cd4 100644 --- a/FLACDotNet/flacdotnet.cpp +++ b/FLACDotNet/flacdotnet.cpp @@ -88,10 +88,7 @@ namespace FLACDotNet { _sampleBuffer = nullptr; _path = path; - if (IO) - _IO = IO; - else - _IO = gcnew FileStream (path, FileMode::Open, FileAccess::Read, FileShare::Read); + _IO = (IO != nullptr) ? IO : gcnew FileStream (path, FileMode::Open, FileAccess::Read, FileShare::Read); _decoder = FLAC__stream_decoder_new(); diff --git a/UnRarDotNet/RarStream.cs b/UnRarDotNet/RarStream.cs index eca9ab1..deb9d03 100644 --- a/UnRarDotNet/RarStream.cs +++ b/UnRarDotNet/RarStream.cs @@ -13,18 +13,20 @@ namespace UnRarDotNet { public class RarStream : Stream { - public RarStream(string archive, string fileName) + public RarStream(string path, string fileName) { - _stop = false; + _close = false; + _eof = false; + _rewind = false; _unrar = new Unrar(); _buffer = null; _offset = 0; _length = 0; _pos = 0; + _path = path; + _fileName = fileName; _unrar.PasswordRequired += new PasswordRequiredHandler(unrar_PasswordRequired); _unrar.DataAvailable += new DataAvailableHandler(unrar_DataAvailable); - _unrar.Open(archive, Unrar.OpenMode.Extract); - _fileName = fileName; _workThread = new Thread(Decompress); _workThread.Priority = ThreadPriority.BelowNormal; _workThread.IsBackground = true; @@ -48,7 +50,7 @@ namespace UnRarDotNet { lock (this) { - while (_size == null && !_stop) + while (_size == null && !_close) Monitor.Wait(this); } if (_size == null) @@ -65,7 +67,7 @@ namespace UnRarDotNet { lock (this) { - _stop = true; + _close = true; Monitor.Pulse(this); } if (_workThread != null) @@ -95,7 +97,7 @@ namespace UnRarDotNet { lock (this) { - while (_buffer == null && !_stop) + while (_buffer == null && !_eof) Monitor.Wait(this); if (_buffer == null) return total; @@ -139,7 +141,7 @@ namespace UnRarDotNet { lock (this) { - while (_size == null && !_stop) + while (_size == null && !_close) Monitor.Wait(this); if (_size == null) throw new NotSupportedException(); @@ -163,8 +165,15 @@ namespace UnRarDotNet } if (_seek_to.Value < _pos) { - _seek_to = null; - throw new NotSupportedException("cannot seek backwards"); + lock (this) + { + _pos = 0; + _rewind = true; + _buffer = null; + Monitor.Pulse(this); + } + //_seek_to = null; + //throw new NotSupportedException("cannot seek backwards"); } return _seek_to.Value; } @@ -176,12 +185,13 @@ namespace UnRarDotNet private Unrar _unrar; private string _fileName; private Thread _workThread; - private bool _stop; + private bool _close, _rewind, _eof; private byte[] _buffer; int _offset, _length; long? _size; long? _seek_to; long _pos; + string _path; private void unrar_PasswordRequired(object sender, PasswordRequiredEventArgs e) { @@ -193,9 +203,15 @@ namespace UnRarDotNet { lock (this) { - while (_buffer != null && !_stop) + while (_buffer != null && !_close) Monitor.Wait(this); - if (_stop) + if (_close) + { + e.ContinueOperation = false; + Monitor.Pulse(this); + return; + } + if (_rewind) { e.ContinueOperation = false; Monitor.Pulse(this); @@ -213,28 +229,47 @@ namespace UnRarDotNet { //try { - while (_unrar.ReadHeader()) + do { - if (_unrar.CurrentFile.FileName == _fileName) + _unrar.Open(_path, Unrar.OpenMode.Extract); + while (_unrar.ReadHeader()) { - lock (this) + if (_unrar.CurrentFile.FileName == _fileName) { - _size = _unrar.CurrentFile.UnpackedSize; - Monitor.Pulse(this); + lock (this) + { + if (_size == null) + { + _size = _unrar.CurrentFile.UnpackedSize; + Monitor.Pulse(this); + } + } + _unrar.Test(); + break; } - _unrar.Test(); - break; + else + _unrar.Skip(); } - else - _unrar.Skip(); - } + _unrar.Close(); + lock (this) + { + _eof = true; + Monitor.Pulse(this); + while (!_rewind && !_close) + Monitor.Wait(this); + if (_close) + break; + _rewind = false; + _eof = false; + } + } while (true); } //catch (StopExtractionException) //{ //} lock (this) { - _stop = true; + _close = true; Monitor.Pulse(this); } } diff --git a/WavPackDotNet/WavPackDotNet.cpp b/WavPackDotNet/WavPackDotNet.cpp index 3ae49a2..7db375d 100644 --- a/WavPackDotNet/WavPackDotNet.cpp +++ b/WavPackDotNet/WavPackDotNet.cpp @@ -33,6 +33,7 @@ using namespace System; using namespace System::Runtime::InteropServices; using namespace System::Collections::Specialized; using namespace System::Security::Cryptography; +using namespace System::IO; using namespace APETagsDotNet; #include @@ -43,21 +44,60 @@ using namespace APETagsDotNet; namespace WavPackDotNet { int write_block(void *id, void *data, int32_t length); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate int32_t DecoderReadDelegate(void *id, void *data, int32_t bcount); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate uint32_t DecoderTellDelegate(void *id); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate int DecoderSeekDelegate(void *id, uint32_t pos); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate int DecoderSeekRelativeDelegate(void *id, int32_t delta, int mode); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate int DecoderPushBackDelegate(void *id, int c); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate uint32_t DecoderLengthDelegate(void *id); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate int DecoderCanSeekDelegate(void *id); + public ref class WavPackReader { public: - WavPackReader(String^ path) { - IntPtr pathChars; + WavPackReader(String^ path, Stream^ IO, Stream^ IO_WVC) { char errorMessage[256]; + _readDel = gcnew DecoderReadDelegate (this, &WavPackReader::ReadCallback); + _tellDel = gcnew DecoderTellDelegate (this, &WavPackReader::TellCallback); + _seekDel = gcnew DecoderSeekDelegate (this, &WavPackReader::SeekCallback); + _seekRelDel = gcnew DecoderSeekRelativeDelegate (this, &WavPackReader::SeekRelCallback); + _pushBackDel = gcnew DecoderPushBackDelegate (this, &WavPackReader::PushBackCallback); + _lengthDel = gcnew DecoderLengthDelegate (this, &WavPackReader::LengthCallback); + _canSeekDel = gcnew DecoderCanSeekDelegate (this, &WavPackReader::CanSeekCallback); + + ioReader = new WavpackStreamReader; + ioReader->read_bytes = (int32_t (*)(void *, void *, int32_t)) Marshal::GetFunctionPointerForDelegate(_readDel).ToPointer(); + ioReader->get_pos = (uint32_t (*)(void *)) Marshal::GetFunctionPointerForDelegate(_tellDel).ToPointer(); + ioReader->set_pos_abs = (int (*)(void *, uint32_t)) Marshal::GetFunctionPointerForDelegate(_seekDel).ToPointer(); + ioReader->set_pos_rel = (int (*)(void *, int32_t, int)) Marshal::GetFunctionPointerForDelegate(_seekRelDel).ToPointer(); + ioReader->push_back_byte = (int (*)(void *, int)) Marshal::GetFunctionPointerForDelegate(_pushBackDel).ToPointer(); + ioReader->get_length = (uint32_t (*)(void *)) Marshal::GetFunctionPointerForDelegate(_lengthDel).ToPointer(); + ioReader->can_seek = (int (*)(void *)) Marshal::GetFunctionPointerForDelegate(_canSeekDel).ToPointer(); + ioReader->write_bytes = NULL; + + _IO_ungetc = _IO_WVC_ungetc = -1; + _path = path; - pathChars = Marshal::StringToHGlobalUni(path); - size_t pathLen = wcslen ((const wchar_t*)pathChars.ToPointer())+1; - wchar_t * pPath = new wchar_t[pathLen]; - memcpy ((void*) pPath, (const wchar_t*)pathChars.ToPointer(), pathLen*sizeof(wchar_t)); - Marshal::FreeHGlobal(pathChars); + _IO = (IO != nullptr) ? IO : gcnew FileStream (path, FileMode::Open, FileAccess::Read, FileShare::Read); + _IO_WVC = (IO != nullptr) ? IO_WVC : System::IO::File::Exists (path+"c") ? gcnew FileStream (path+"c", FileMode::Open, FileAccess::Read, FileShare::Read) : nullptr; - _wpc = WavpackOpenFileInput (pPath, errorMessage, OPEN_WVC, 0); + //IntPtr pathChars; + //pathChars = Marshal::StringToHGlobalUni(path); + //size_t pathLen = wcslen ((const wchar_t*)pathChars.ToPointer())+1; + //wchar_t * pPath = new wchar_t[pathLen]; + //memcpy ((void*) pPath, (const wchar_t*)pathChars.ToPointer(), pathLen*sizeof(wchar_t)); + //Marshal::FreeHGlobal(pathChars); + //_wpc = WavpackOpenFileInput (pPath, errorMessage, OPEN_WVC, 0); + + _wpc = WavpackOpenFileInputEx (ioReader, "v", _IO_WVC != nullptr ? "c" : NULL, errorMessage, OPEN_WVC, 0); if (_wpc == NULL) { throw gcnew Exception("Unable to initialize the decoder."); } @@ -71,6 +111,7 @@ namespace WavPackDotNet { ~WavPackReader() { + delete ioReader; } property Int32 BitsPerSample { @@ -125,7 +166,7 @@ namespace WavPackDotNet { NameValueCollection^ get () { if (!_tags) { - APETagDotNet^ apeTag = gcnew APETagDotNet (_path, true, true); + APETagDotNet^ apeTag = gcnew APETagDotNet (_IO, true); _tags = apeTag->GetStringTags (true); apeTag->Close (); } @@ -136,8 +177,19 @@ namespace WavPackDotNet { } } - void Close() { + void Close() + { _wpc = WavpackCloseFile(_wpc); + if (_IO != nullptr) + { + _IO->Close (); + _IO = nullptr; + } + if (_IO_WVC != nullptr) + { + _IO_WVC->Close (); + _IO_WVC = nullptr; + } } void Read(array^ sampleBuffer, Int32 sampleCount) { @@ -158,6 +210,106 @@ namespace WavPackDotNet { Int32 _sampleCount, _sampleOffset; Int32 _bitsPerSample, _channelCount, _sampleRate; String^ _path; + Stream^ _IO; + Stream^ _IO_WVC; + DecoderReadDelegate^ _readDel; + DecoderTellDelegate^ _tellDel; + DecoderSeekDelegate^ _seekDel; + DecoderSeekRelativeDelegate^ _seekRelDel; + DecoderPushBackDelegate^ _pushBackDel; + DecoderLengthDelegate^ _lengthDel; + DecoderCanSeekDelegate^ _canSeekDel; + array^ _readBuffer; + int _IO_ungetc, _IO_WVC_ungetc; + WavpackStreamReader* ioReader; + + int32_t ReadCallback (void *id, void *data, int32_t bcount) + { + Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO; + int IO_ungetc = (*(char*)id=='c') ? _IO_WVC_ungetc : _IO_ungetc; + int unget_len = 0; + + if (IO_ungetc != -1) + { + *(unsigned char*)data = (unsigned char) IO_ungetc; + if (IO == _IO) + _IO_ungetc = -1; + else + _IO_WVC_ungetc = -1; + bcount --; + if (!bcount) + return 1; + data = 1 + (unsigned char*)data; + unget_len = 1; + } + + if (_readBuffer == nullptr || _readBuffer->Length < bcount) + _readBuffer = gcnew array(bcount < 0x4000 ? 0x4000 : bcount); + int len = IO->Read (_readBuffer, 0, bcount); + if (len) Marshal::Copy (_readBuffer, 0, (IntPtr)data, len); + return len + unget_len; + } + + uint32_t TellCallback(void *id) + { + Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO; + return IO->Position; + } + + int SeekCallback (void *id, uint32_t pos) + { + Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO; + IO->Position = pos; + return 0; + } + + int SeekRelCallback (void *id, int32_t delta, int mode) + { + Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO; + switch (mode) + { + case SEEK_SET: + IO->Seek (delta, System::IO::SeekOrigin::Begin); + break; + case SEEK_END: + IO->Seek (delta, System::IO::SeekOrigin::End); + break; + case SEEK_CUR: + IO->Seek (delta, System::IO::SeekOrigin::Current); + break; + default: + return -1; + } + return 0; + } + + int PushBackCallback (void *id, int c) + { + Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO; + if (IO == _IO) + { + if (_IO_ungetc != -1) + throw gcnew Exception("Double PushBackCallback unsupported."); + _IO_ungetc = c; + } else + { + if (_IO_WVC_ungetc != -1) + throw gcnew Exception("Double PushBackCallback unsupported."); + _IO_WVC_ungetc = c; + } + } + + uint32_t LengthCallback (void *id) + { + Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO; + return IO->Length; + } + + int CanSeekCallback(void *id) + { + Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO; + return IO->CanSeek; + } }; public ref class WavPackWriter { diff --git a/wavpack-4.5.0/src/libwavpack.vcproj b/wavpack-4.5.0/src/libwavpack.vcproj index 132c0f5..c4bf75d 100644 --- a/wavpack-4.5.0/src/libwavpack.vcproj +++ b/wavpack-4.5.0/src/libwavpack.vcproj @@ -4,6 +4,7 @@ Version="8,00" Name="libwavpack" ProjectGUID="{5CCCB9CF-0384-458F-BA08-72B73866840F}" + RootNamespace="libwavpack" Keyword="Win32Proj" > @@ -41,7 +42,7 @@