diff --git a/CUETools.Codecs.libFLAC/CUETools.Codecs.libFLAC.csproj b/CUETools.Codecs.libFLAC/CUETools.Codecs.libFLAC.csproj
new file mode 100644
index 0000000..b638934
--- /dev/null
+++ b/CUETools.Codecs.libFLAC/CUETools.Codecs.libFLAC.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net40;net20;netstandard2.0
+ 2.1.6.0
+ CUETools.Codecs.libFLAC
+ CUETools.Codecs.libFLAC
+ CUETools
+ A library for encoding flac using official encoder.
+ Copyright (c) 2008-2018 Grigory Chudov
+ Grigory Chudov
+ true
+ ..\bin\$(Configuration)\plugins
+ https://github.com/gchudov/cuetools.net
+ git
+
+
+
+
+
+ False
+
+
+
+
+
+
+
+
diff --git a/CUETools.Codecs.libFLAC/FLACDLL.cs b/CUETools.Codecs.libFLAC/FLACDLL.cs
new file mode 100644
index 0000000..ca666ff
--- /dev/null
+++ b/CUETools.Codecs.libFLAC/FLACDLL.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace CUETools.Codecs.libFLAC
+{
+ internal unsafe static class FLACDLL
+ {
+ internal const string libFLACDll = "libFLAC_dynamic";
+ internal const CallingConvention libFLACCallingConvention = CallingConvention.Cdecl;
+ internal const int FLAC__MAX_CHANNELS = 8;
+
+ [DllImport("kernel32.dll")]
+ private static extern IntPtr LoadLibrary(string dllToLoad);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate FLAC__StreamDecoderReadStatus FLAC__StreamDecoderReadCallback(IntPtr decoder, byte* buffer, ref long bytes, void* client_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate FLAC__StreamDecoderSeekStatus FLAC__StreamDecoderSeekCallback(IntPtr decoder, long absolute_byte_offset, void* client_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate FLAC__StreamDecoderTellStatus FLAC__StreamDecoderTellCallback(IntPtr decoder, out long absolute_byte_offset, void* client_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate FLAC__StreamDecoderLengthStatus FLAC__StreamDecoderLengthCallback(IntPtr decoder, out long stream_length, void* client_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate int FLAC__StreamDecoderEofCallback(IntPtr decoder, void* client_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate FLAC__StreamDecoderWriteStatus FLAC__StreamDecoderWriteCallback(IntPtr decoder, FLAC__Frame* frame, int** buffer, void* client_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void FLAC__StreamDecoderMetadataCallback(IntPtr decoder, FLAC__StreamMetadata* metadata, void* client_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void FLAC__StreamDecoderErrorCallback(IntPtr decoder, FLAC__StreamDecoderErrorStatus status, void* client_data);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern IntPtr FLAC__stream_decoder_new();
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_decoder_set_metadata_respond(IntPtr decoder, FLAC__MetadataType type);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_decoder_process_until_end_of_metadata(IntPtr decoder);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream(
+ IntPtr decoder,
+ FLAC__StreamDecoderReadCallback read_callback,
+ FLAC__StreamDecoderSeekCallback seek_callback,
+ FLAC__StreamDecoderTellCallback tell_callback,
+ FLAC__StreamDecoderLengthCallback length_callback,
+ FLAC__StreamDecoderEofCallback eof_callback,
+ FLAC__StreamDecoderWriteCallback write_callback,
+ FLAC__StreamDecoderMetadataCallback metadata_callback,
+ FLAC__StreamDecoderErrorCallback error_callback,
+ void* client_data
+ );
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_decoder_finish(IntPtr decoder);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_decoder_delete(IntPtr decoder);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_decoder_seek_absolute(IntPtr decoder, long sample);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern FLAC__StreamDecoderState FLAC__stream_decoder_get_state(IntPtr decoder);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_decoder_process_single(IntPtr decoder);
+
+
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern IntPtr FLAC__stream_encoder_new();
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_set_bits_per_sample(IntPtr encoder, uint value);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_set_sample_rate(IntPtr encoder, uint value);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_set_channels(IntPtr encoder, uint value);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_finish(IntPtr encoder);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_delete(IntPtr encoder);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_process_interleaved(IntPtr encoder, int* buffer, int samples);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern FLAC__StreamEncoderState FLAC__stream_encoder_get_state(IntPtr encoder);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern void FLAC__stream_encoder_get_verify_decoder_error_stats(IntPtr encoder, out ulong absolute_sample, out uint frame_number, out uint channel, out uint sample, out int expected, out int got);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern FLAC__StreamMetadata* FLAC__metadata_object_new(FLAC__MetadataType type);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata* metadata, int samples, long total_samples);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata* metadata, int compact);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_set_metadata(IntPtr encoder, FLAC__StreamMetadata** metadata, int num_blocks);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_set_verify(IntPtr encoder, int value);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_set_do_md5(IntPtr encoder, int value);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_set_total_samples_estimate(IntPtr encoder, long value);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_set_compression_level(IntPtr encoder, int value);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern int FLAC__stream_encoder_set_blocksize(IntPtr encoder, int value);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate FLAC__StreamEncoderWriteStatus FLAC__StreamEncoderWriteCallback(IntPtr encoder, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] buffer, long bytes, int samples, int current_frame, void* client_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate FLAC__StreamEncoderSeekStatus FLAC__StreamEncoderSeekCallback(IntPtr encoder, long absolute_byte_offset, void* client_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate FLAC__StreamEncoderTellStatus FLAC__StreamEncoderTellCallback(IntPtr encoder, out long absolute_byte_offset, void* client_data);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void FLAC__StreamEncoderMetadataCallback(IntPtr encoder, FLAC__StreamMetadata* metadata, void* client_data);
+
+ [DllImport(libFLACDll, CallingConvention = libFLACCallingConvention)]
+ internal static extern FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_stream(IntPtr encoder,
+ FLAC__StreamEncoderWriteCallback write_callback,
+ FLAC__StreamEncoderSeekCallback seek_callback,
+ FLAC__StreamEncoderTellCallback tell_callback,
+ FLAC__StreamEncoderMetadataCallback metadata_callback,
+ void* client_data);
+
+ static FLACDLL()
+ {
+ var myPath = new Uri(typeof(FLACDLL).Assembly.CodeBase).LocalPath;
+ var myFolder = System.IO.Path.GetDirectoryName(myPath);
+ var is64 = IntPtr.Size == 8;
+ var subfolder = is64 ? "plugins (x64)" : "plugins (win32)";
+#if NET40
+ LoadLibrary(System.IO.Path.Combine(myFolder, "..", subfolder, libFLACDll + ".dll"));
+#else
+ LoadLibrary(System.IO.Path.Combine(System.IO.Path.Combine(System.IO.Path.Combine(myFolder, ".."), subfolder), libFLACDll + ".dll"));
+#endif
+ }
+ };
+}
diff --git a/CUETools.Codecs.libFLAC/Reader.cs b/CUETools.Codecs.libFLAC/Reader.cs
new file mode 100644
index 0000000..5364a3b
--- /dev/null
+++ b/CUETools.Codecs.libFLAC/Reader.cs
@@ -0,0 +1,364 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using CUETools.Codecs;
+
+namespace CUETools.Codecs.libFLAC.Reader
+{
+ [AudioDecoderClass("libFLAC", "flac", 1)]
+ public unsafe class Reader : IAudioSource
+ {
+ public Reader(string path, Stream IO)
+ {
+ m_writeCallback = WriteCallback;
+ m_metadataCallback = MetadataCallback;
+ m_errorCallback = ErrorCallback;
+ m_readCallback = ReadCallback;
+ m_seekCallback = SeekCallback;
+ m_tellCallback = TellCallback;
+ m_lengthCallback = LengthCallback;
+ m_eofCallback = EofCallback;
+
+ m_decoderActive = false;
+
+ m_sampleOffset = 0;
+ m_sampleBuffer = null;
+ m_path = path;
+ m_bufferOffset = 0;
+ m_bufferLength = 0;
+
+ m_stream = (IO != null) ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
+
+ m_decoder = FLACDLL.FLAC__stream_decoder_new();
+
+ if (0 == FLACDLL.FLAC__stream_decoder_set_metadata_respond(m_decoder, FLAC__MetadataType.FLAC__METADATA_TYPE_VORBIS_COMMENT))
+ throw new Exception("unable to setup the decoder");
+
+ FLAC__StreamDecoderInitStatus st = FLACDLL.FLAC__stream_decoder_init_stream(
+ m_decoder, m_readCallback,
+ m_stream.CanSeek ? m_seekCallback : null,
+ m_stream.CanSeek ? m_tellCallback : null,
+ m_stream.CanSeek ? m_lengthCallback : null,
+ m_stream.CanSeek ? m_eofCallback : null,
+ m_writeCallback, m_metadataCallback, m_errorCallback, null);
+
+ if (st != FLAC__StreamDecoderInitStatus.FLAC__STREAM_DECODER_INIT_STATUS_OK)
+ throw new Exception(string.Format("unable to initialize the decoder: {0}", st));
+
+ m_decoderActive = true;
+
+ if (0 == FLACDLL.FLAC__stream_decoder_process_until_end_of_metadata(m_decoder))
+ throw new Exception("unable to retrieve metadata");
+ }
+
+#if SUPPORTMETADATA
+ bool UpdateTags (bool preserveTime)
+ {
+ Close ();
+
+ FLAC__Metadata_Chain* chain = FLAC__metadata_chain_new ();
+ if (!chain) return false;
+
+ IntPtr pathChars = Marshal::StringToHGlobalAnsi(_path);
+ int res = FLAC__metadata_chain_read (chain, (const char*)pathChars.ToPointer());
+ Marshal::FreeHGlobal(pathChars);
+ if (!res) {
+ FLAC__metadata_chain_delete (chain);
+ return false;
+ }
+ FLAC__Metadata_Iterator* i = FLAC__metadata_iterator_new ();
+ FLAC__metadata_iterator_init (i, chain);
+ do {
+ FLAC__StreamMetadata* metadata = FLAC__metadata_iterator_get_block (i);
+ if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
+ FLAC__metadata_iterator_delete_block (i, false);
+ } while (FLAC__metadata_iterator_next (i));
+
+ FLAC__StreamMetadata * vorbiscomment = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
+ for (int tagno = 0; tagno <_tags->Count; tagno++)
+ {
+ String ^ tag_name = _tags->GetKey(tagno);
+ int tag_len = tag_name->Length;
+ char * tag = new char [tag_len + 1];
+ IntPtr nameChars = Marshal::StringToHGlobalAnsi(tag_name);
+ memcpy (tag, (const char*)nameChars.ToPointer(), tag_len);
+ Marshal::FreeHGlobal(nameChars);
+ tag[tag_len] = 0;
+
+ array^ tag_values = _tags->GetValues(tagno);
+ for (int valno = 0; valno < tag_values->Length; valno++)
+ {
+ UTF8Encoding^ enc = new UTF8Encoding();
+ array^ value_array = enc->GetBytes (tag_values[valno]);
+ int value_len = value_array->Length;
+ char * value = new char [value_len + 1];
+ Marshal::Copy (value_array, 0, (IntPtr) value, value_len);
+ value[value_len] = 0;
+
+ FLAC__StreamMetadata_VorbisComment_Entry entry;
+ /* create and entry and append it */
+ if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, tag, value)) {
+ throw new Exception("Unable to add tags, must be valid utf8.");
+ }
+ if(!FLAC__metadata_object_vorbiscomment_append_comment(vorbiscomment, entry, /*copy=*/false)) {
+ throw new Exception("Unable to add tags.");
+ }
+ delete [] value;
+ }
+ delete [] tag;
+ }
+
+ FLAC__metadata_iterator_insert_block_after (i, vorbiscomment);
+ FLAC__metadata_iterator_delete (i);
+ FLAC__metadata_chain_sort_padding (chain);
+ res = FLAC__metadata_chain_write (chain, true, preserveTime);
+ FLAC__metadata_chain_delete (chain);
+ return 0 != res;
+ }
+#endif
+
+ FLAC__StreamDecoderWriteStatus WriteCallback(IntPtr decoder,
+ FLAC__Frame* frame, int** buffer, void* client_data)
+ {
+ int sampleCount = frame->header.blocksize;
+
+ if (m_bufferLength > 0)
+ throw new Exception("received unrequested samples");
+
+ if ((frame->header.bits_per_sample != m_pcm.BitsPerSample) ||
+ (frame->header.channels != m_pcm.ChannelCount) ||
+ (frame->header.sample_rate != m_pcm.SampleRate))
+ throw new Exception("format changes within a file are not allowed");
+
+ if (m_bufferOffset != 0)
+ throw new Exception("internal buffer error");
+
+ if (m_sampleBuffer == null || m_sampleBuffer.Size < sampleCount)
+ m_sampleBuffer = new AudioBuffer(m_pcm, sampleCount);
+ m_sampleBuffer.Length = sampleCount;
+
+ if (m_pcm.ChannelCount == 2)
+ m_sampleBuffer.Interlace(0, (int*)buffer[0], (int*)buffer[1], sampleCount);
+ else
+ {
+ int _channelCount = m_pcm.ChannelCount;
+ for (Int32 iChan = 0; iChan < _channelCount; iChan++)
+ {
+ fixed (int* pMyBuffer = &m_sampleBuffer.Samples[0, iChan])
+ {
+ int* pMyBufferPtr = pMyBuffer;
+ int* pFLACBuffer = buffer[iChan];
+ int* pFLACBufferEnd = pFLACBuffer + sampleCount;
+ while (pFLACBuffer < pFLACBufferEnd)
+ {
+ *pMyBufferPtr = *pFLACBuffer;
+ pMyBufferPtr += _channelCount;
+ pFLACBuffer++;
+ }
+ }
+ }
+ }
+ m_bufferLength = sampleCount;
+ m_sampleOffset += m_bufferLength;
+ return FLAC__StreamDecoderWriteStatus.FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+ }
+
+ void MetadataCallback(IntPtr decoder,
+ FLAC__StreamMetadata *metadata, void *client_data)
+ {
+ if (metadata->type == FLAC__MetadataType.FLAC__METADATA_TYPE_STREAMINFO)
+ {
+ m_pcm = new AudioPCMConfig(
+ metadata->stream_info.bits_per_sample,
+ metadata->stream_info.channels,
+ metadata->stream_info.sample_rate,
+ (AudioPCMConfig.SpeakerConfig)0);
+ m_sampleCount = metadata->stream_info.total_samples;
+ }
+#if SUPPORTMETADATA
+ if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
+ {
+ for (int tagno = 0; tagno < metadata->vorbis_comment.num_comments; tagno ++)
+ {
+ char * field_name, * field_value;
+ if(!FLACDLL.FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(metadata->vorbis_comment.comments[tagno], &field_name, &field_value))
+ throw new Exception("Unable to parse vorbis comment.");
+ string name = Marshal::PtrToStringAnsi ((IntPtr) field_name);
+ free (field_name);
+ array^ bvalue = new array((int) strlen (field_value));
+ Marshal.Copy ((IntPtr) field_value, bvalue, 0, (int) strlen (field_value));
+ free (field_value);
+ UTF8Encoding enc = new UTF8Encoding();
+ string value = enc.GetString(bvalue);
+ _tags.Add(name, value);
+ }
+ }
+#endif
+ }
+
+ void ErrorCallback(IntPtr decoder,
+ FLAC__StreamDecoderErrorStatus status, void *client_data)
+ {
+ switch (status)
+ {
+ case FLAC__StreamDecoderErrorStatus.FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
+ throw new Exception("synchronization was lost");
+ case FLAC__StreamDecoderErrorStatus.FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
+ throw new Exception("encountered a corrupted frame header");
+ case FLAC__StreamDecoderErrorStatus.FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
+ throw new Exception("frame CRC mismatch");
+ default:
+ throw new Exception("an unknown error has occurred");
+ }
+ }
+
+ FLAC__StreamDecoderReadStatus ReadCallback(IntPtr decoder, byte* buffer, ref long bytes, void* client_data)
+ {
+ if (bytes <= 0 || bytes > int.MaxValue)
+ return FLAC__StreamDecoderReadStatus.FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */
+
+ if (m_readBuffer == null || m_readBuffer.Length < bytes)
+ m_readBuffer = new byte[Math.Max(bytes, 0x4000)];
+
+ bytes = m_stream.Read(m_readBuffer, 0, (int)bytes);
+ //if(ferror(decoder->private_->file))
+ //return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ //else
+ if (bytes == 0)
+ return FLAC__StreamDecoderReadStatus.FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+
+ Marshal.Copy(m_readBuffer, 0, (IntPtr)buffer, (int)bytes);
+ return FLAC__StreamDecoderReadStatus.FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ }
+
+ FLAC__StreamDecoderSeekStatus SeekCallback(IntPtr decoder, long absolute_byte_offset, void* client_data)
+ {
+ //if (!_IO.CanSeek)
+ // return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
+ m_stream.Position = absolute_byte_offset;
+ //catch(Exception) {
+ // return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+ //}
+ return FLAC__StreamDecoderSeekStatus.FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+ }
+
+ FLAC__StreamDecoderTellStatus TellCallback(IntPtr decoder, out long absolute_byte_offset, void* client_data)
+ {
+ //if (!_IO.CanSeek)
+ // return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
+ absolute_byte_offset = m_stream.Position;
+ // if (_IO.Position < 0)
+ // return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
+ return FLAC__StreamDecoderTellStatus.FLAC__STREAM_DECODER_TELL_STATUS_OK;
+ }
+
+ FLAC__StreamDecoderLengthStatus LengthCallback(IntPtr decoder, out long stream_length, void* client_data)
+ {
+ //if (!_IO.CanSeek)
+ // return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
+ // if (_IO.Length < 0)
+ // return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
+ stream_length = m_stream.Length;
+ return FLAC__StreamDecoderLengthStatus.FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+ }
+
+ int EofCallback (IntPtr decoder, void *client_data)
+ {
+ return m_stream.Position == m_stream.Length ? 1 : 0;
+ }
+
+ public AudioDecoderSettings Settings => null;
+
+ public AudioPCMConfig PCM => m_pcm;
+
+ public string Path => m_path;
+
+ public long Length => m_sampleCount;
+
+ private int SamplesInBuffer => m_bufferLength - m_bufferOffset;
+
+ public long Position
+ {
+ get => m_sampleOffset - SamplesInBuffer;
+
+ set
+ {
+ m_sampleOffset = value;
+ m_bufferOffset = 0;
+ m_bufferLength = 0;
+ if (0 == FLACDLL.FLAC__stream_decoder_seek_absolute(m_decoder, value))
+ throw new Exception("unable to seek");
+ }
+ }
+
+ public long Remaining { get => m_sampleCount - Position; }
+
+ public void Close()
+ {
+ if (m_decoderActive)
+ {
+ FLACDLL.FLAC__stream_decoder_finish(m_decoder);
+ FLACDLL.FLAC__stream_decoder_delete(m_decoder);
+ m_decoderActive = false;
+ }
+ if (m_stream != null)
+ {
+ m_stream.Close();
+ m_stream = null;
+ }
+ }
+
+ public int Read(AudioBuffer buff, int maxLength)
+ {
+ buff.Prepare(this, maxLength);
+ int buffOffset = 0;
+ int samplesNeeded = buff.Length;
+
+ while (samplesNeeded != 0)
+ {
+ if (SamplesInBuffer == 0)
+ {
+ m_bufferOffset = 0;
+ m_bufferLength = 0;
+ do
+ {
+ if (FLACDLL.FLAC__stream_decoder_get_state(m_decoder) == FLAC__StreamDecoderState.FLAC__STREAM_DECODER_END_OF_STREAM)
+ {
+ buff.Length -= samplesNeeded;
+ return buff.Length;
+ }
+ if (0 == FLACDLL.FLAC__stream_decoder_process_single(m_decoder))
+ throw new Exception(string.Format("an error occurred while decoding: {0}", FLACDLL.FLAC__stream_decoder_get_state(m_decoder)));
+ } while (m_bufferLength == 0);
+ }
+ int copyCount = Math.Min(samplesNeeded, SamplesInBuffer);
+ Array.Copy(m_sampleBuffer.Bytes, m_bufferOffset * m_pcm.BlockAlign, buff.Bytes, buffOffset * m_pcm.BlockAlign, copyCount * m_pcm.BlockAlign);
+ samplesNeeded -= copyCount;
+ buffOffset += copyCount;
+ m_bufferOffset += copyCount;
+ }
+ return buff.Length;
+ }
+
+ AudioBuffer m_sampleBuffer;
+ byte[] m_readBuffer;
+ long m_sampleCount, m_sampleOffset;
+ int m_bufferOffset, m_bufferLength;
+ IntPtr m_decoder;
+ string m_path;
+ Stream m_stream;
+ bool m_decoderActive;
+ AudioPCMConfig m_pcm;
+ FLACDLL.FLAC__StreamDecoderReadCallback m_readCallback;
+ FLACDLL.FLAC__StreamDecoderSeekCallback m_seekCallback;
+ FLACDLL.FLAC__StreamDecoderTellCallback m_tellCallback;
+ FLACDLL.FLAC__StreamDecoderLengthCallback m_lengthCallback;
+ FLACDLL.FLAC__StreamDecoderEofCallback m_eofCallback;
+ FLACDLL.FLAC__StreamDecoderWriteCallback m_writeCallback;
+ FLACDLL.FLAC__StreamDecoderMetadataCallback m_metadataCallback;
+ FLACDLL.FLAC__StreamDecoderErrorCallback m_errorCallback;
+ }
+}
diff --git a/CUETools.Codecs.libFLAC/Writer.cs b/CUETools.Codecs.libFLAC/Writer.cs
new file mode 100644
index 0000000..28db1a6
--- /dev/null
+++ b/CUETools.Codecs.libFLAC/Writer.cs
@@ -0,0 +1,251 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using CUETools.Codecs;
+
+namespace CUETools.Codecs.libFLAC.Writer
+{
+ public class WriterSettings : AudioEncoderSettings
+ {
+ public WriterSettings()
+ : base("0 1 2 3 4 5 6 7 8", "5")
+ {
+ }
+
+ [DefaultValue(false)]
+ [DisplayName("Verify")]
+ [Description("Decode each frame and compare with original")]
+ public bool Verify { get; set; }
+
+ [DefaultValue(true)]
+ [DisplayName("MD5")]
+ [Description("Calculate MD5 hash for audio stream")]
+ public bool MD5Sum { get; set; }
+ };
+
+ [AudioEncoderClass("libFLAC", "flac", true, 2, typeof(WriterSettings))]
+ public unsafe class Writer : IAudioDest
+ {
+ public Writer(string path, Stream output, WriterSettings settings)
+ {
+ m_path = path;
+ m_stream = output;
+ m_settings = settings;
+ m_streamGiven = output != null;
+ m_initialized = false;
+ m_finalSampleCount = 0;
+ m_samplesWritten = 0;
+ m_write_callback = StreamEncoderWriteCallback;
+ m_seek_callback = StreamEncoderSeekCallback;
+ m_tell_callback = StreamEncoderTellCallback;
+
+ if (m_settings.PCM.BitsPerSample < 16 || m_settings.PCM.BitsPerSample > 24)
+ throw new Exception("bits per sample must be 16..24");
+
+ m_encoder = FLACDLL.FLAC__stream_encoder_new();
+
+ FLACDLL.FLAC__stream_encoder_set_bits_per_sample(m_encoder, (uint)m_settings.PCM.BitsPerSample);
+ FLACDLL.FLAC__stream_encoder_set_channels(m_encoder, (uint)m_settings.PCM.ChannelCount);
+ FLACDLL.FLAC__stream_encoder_set_sample_rate(m_encoder, (uint)m_settings.PCM.SampleRate);
+ }
+
+ public Writer(string path, WriterSettings settings)
+ : this(path, null, settings)
+ {
+ }
+
+ public AudioEncoderSettings Settings => m_settings;
+
+ public string Path { get => m_path; }
+
+ public long FinalSampleCount
+ {
+ get => m_finalSampleCount;
+ set
+ {
+ if (value < 0)
+ throw new Exception("invalid final sample count");
+ if (m_initialized)
+ throw new Exception("final sample count cannot be changed after encoding begins");
+ m_finalSampleCount = value;
+ }
+ }
+
+ public void Close()
+ {
+ if (m_initialized)
+ {
+ FLACDLL.FLAC__stream_encoder_finish(m_encoder);
+ FLACDLL.FLAC__stream_encoder_delete(m_encoder);
+ m_encoder = IntPtr.Zero;
+ m_initialized = false;
+ }
+ if (m_stream != null)
+ {
+ m_stream.Close();
+ m_stream = null;
+ }
+ if ((m_finalSampleCount != 0) && (m_samplesWritten != m_finalSampleCount))
+ throw new Exception("samples written differs from the expected sample count");
+ }
+
+ public void Delete()
+ {
+ try
+ {
+ if (m_initialized)
+ {
+ FLACDLL.FLAC__stream_encoder_delete(m_encoder);
+ m_encoder = IntPtr.Zero;
+ m_initialized = false;
+ }
+ if (m_stream != null)
+ {
+ m_stream.Close();
+ m_stream = null;
+ }
+ }
+ catch (Exception)
+ {
+ }
+ if (m_path != "")
+ File.Delete(m_path);
+ }
+
+ public void Write(AudioBuffer sampleBuffer)
+ {
+ if (!m_initialized) Initialize();
+
+ sampleBuffer.Prepare(this);
+
+ fixed (int* pSampleBuffer = &sampleBuffer.Samples[0, 0])
+ {
+ if (0 == FLACDLL.FLAC__stream_encoder_process_interleaved(m_encoder,
+ pSampleBuffer, sampleBuffer.Length))
+ {
+ var state = FLACDLL.FLAC__stream_encoder_get_state(m_encoder);
+ string status = state.ToString();
+ if (state == FLAC__StreamEncoderState.FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA)
+ {
+ ulong absolute_sample;
+ uint frame_number;
+ uint channel;
+ uint sample;
+ int expected, got;
+ FLACDLL.FLAC__stream_encoder_get_verify_decoder_error_stats(m_encoder, out absolute_sample, out frame_number, out channel, out sample, out expected, out got);
+ status = status + String.Format("({0:x} instead of {1:x} @{2:x})", got, expected, absolute_sample);
+ }
+ throw new Exception("an error occurred while encoding: " + status);
+ }
+ }
+
+ m_samplesWritten += sampleBuffer.Length;
+ }
+
+ internal FLAC__StreamEncoderWriteStatus StreamEncoderWriteCallback(IntPtr encoder, byte[] buffer, long bytes, int samples, int current_frame, void* client_data)
+ {
+ try
+ {
+ m_stream.Write(buffer, 0, (int)bytes);
+ }
+ catch (Exception)
+ {
+ return FLAC__StreamEncoderWriteStatus.FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
+ }
+ return FLAC__StreamEncoderWriteStatus.FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+ }
+
+ internal FLAC__StreamEncoderSeekStatus StreamEncoderSeekCallback(IntPtr encoder, long absolute_byte_offset, void* client_data)
+ {
+ if (!m_stream.CanSeek) return FLAC__StreamEncoderSeekStatus.FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED;
+ try
+ {
+ m_stream.Position = absolute_byte_offset;
+ }
+ catch (Exception)
+ {
+ return FLAC__StreamEncoderSeekStatus.FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR;
+ }
+ return FLAC__StreamEncoderSeekStatus.FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
+ }
+
+ internal FLAC__StreamEncoderTellStatus StreamEncoderTellCallback(IntPtr encoder, out long absolute_byte_offset, void* client_data)
+ {
+ if (!m_stream.CanSeek)
+ {
+ absolute_byte_offset = -1;
+ return FLAC__StreamEncoderTellStatus.FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED;
+ }
+ try
+ {
+ absolute_byte_offset = m_stream.Position;
+ }
+ catch (Exception)
+ {
+ absolute_byte_offset = -1;
+ return FLAC__StreamEncoderTellStatus.FLAC__STREAM_ENCODER_TELL_STATUS_ERROR;
+ }
+ return FLAC__StreamEncoderTellStatus.FLAC__STREAM_ENCODER_TELL_STATUS_OK;
+ }
+
+ void Initialize()
+ {
+ if (m_stream == null)
+ m_stream = new FileStream(m_path, FileMode.Create, FileAccess.Write, FileShare.Read, 0x10000);
+
+ var metadata = stackalloc FLAC__StreamMetadata*[4];
+ int metadataCount = 0;
+ FLAC__StreamMetadata* padding, seektable, vorbiscomment;
+
+ if (m_finalSampleCount != 0)
+ {
+ seektable = FLACDLL.FLAC__metadata_object_new(FLAC__MetadataType.FLAC__METADATA_TYPE_SEEKTABLE);
+ FLACDLL.FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(
+ seektable, m_settings.PCM.SampleRate * 10, m_finalSampleCount);
+ FLACDLL.FLAC__metadata_object_seektable_template_sort(seektable, 1);
+ metadata[metadataCount++] = seektable;
+ }
+
+ vorbiscomment = FLACDLL.FLAC__metadata_object_new(FLAC__MetadataType.FLAC__METADATA_TYPE_VORBIS_COMMENT);
+ metadata[metadataCount++] = vorbiscomment;
+
+ if (m_settings.Padding != 0)
+ {
+ padding = FLACDLL.FLAC__metadata_object_new(FLAC__MetadataType.FLAC__METADATA_TYPE_PADDING);
+ padding->length = (uint)m_settings.Padding;
+ metadata[metadataCount++] = padding;
+ }
+
+ FLACDLL.FLAC__stream_encoder_set_metadata(m_encoder, metadata, metadataCount);
+ FLACDLL.FLAC__stream_encoder_set_verify(m_encoder, m_settings.Verify ? 1 : 0);
+ FLACDLL.FLAC__stream_encoder_set_do_md5(m_encoder, m_settings.MD5Sum ? 1 : 0);
+ FLACDLL.FLAC__stream_encoder_set_compression_level(m_encoder, m_settings.EncoderModeIndex);
+ if (m_finalSampleCount != 0)
+ FLACDLL.FLAC__stream_encoder_set_total_samples_estimate(m_encoder, m_finalSampleCount);
+ if (m_settings.BlockSize > 0)
+ FLACDLL.FLAC__stream_encoder_set_blocksize(m_encoder, m_settings.BlockSize);
+
+ FLAC__StreamEncoderInitStatus st = FLACDLL.FLAC__stream_encoder_init_stream(
+ m_encoder, m_write_callback, m_stream.CanSeek ? m_seek_callback : null,
+ m_stream.CanSeek ? m_tell_callback : null, null, null);
+ if (st != FLAC__StreamEncoderInitStatus.FLAC__STREAM_ENCODER_INIT_STATUS_OK)
+ throw new Exception(string.Format("unable to initialize the encoder: {0}", st));
+
+ m_initialized = true;
+ }
+
+ WriterSettings m_settings;
+ Stream m_stream;
+ bool m_streamGiven;
+ IntPtr m_encoder;
+ bool m_initialized;
+ string m_path;
+ Int64 m_finalSampleCount, m_samplesWritten;
+ FLACDLL.FLAC__StreamEncoderWriteCallback m_write_callback;
+ FLACDLL.FLAC__StreamEncoderSeekCallback m_seek_callback;
+ FLACDLL.FLAC__StreamEncoderTellCallback m_tell_callback;
+ }
+}
diff --git a/CUETools.Codecs.libFLAC/libFLAC.cs b/CUETools.Codecs.libFLAC/libFLAC.cs
new file mode 100644
index 0000000..8bebc4d
--- /dev/null
+++ b/CUETools.Codecs.libFLAC/libFLAC.cs
@@ -0,0 +1,221 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace CUETools.Codecs.libFLAC
+{
+ internal struct FLAC__FrameHeader
+ {
+ internal int blocksize;
+ internal int sample_rate;
+ internal int channels;
+ internal FLAC__ChannelAssignment channel_assignment;
+ internal int bits_per_sample;
+ internal FLAC__FrameNumberType number_type;
+ internal ulong sample_number; // can be uint frame_number depending on number_type
+ internal byte crc;
+ };
+
+ //internal struct FLAC__Subframe
+ //{
+ // FLAC__SubframeType type;
+ // [FieldOffset(4)]
+ // FLAC__Subframe_Constant data_constant;
+ // [FieldOffset(4)]
+ // FLAC__Subframe_Fixed data_fixed;
+ // [FieldOffset(4)]
+ // FLAC__Subframe_LPC data_lpc;
+ // [FieldOffset(4)]
+ // FLAC__Subframe_Verbatim data_verbatim;
+ // uint wasted_bits;
+ //};
+
+ internal unsafe struct FLAC__Frame
+ {
+ internal FLAC__FrameHeader header;
+ //fixed FLAC__Subframe subframes[FLACDLL.FLAC__MAX_CHANNELS];
+ //FLAC__FrameFooter footer;
+ };
+
+ [StructLayout(LayoutKind.Explicit), Serializable]
+ internal struct FLAC__StreamMetadata
+ {
+ [FieldOffset(0)]
+ internal FLAC__MetadataType type;
+ [FieldOffset(4)]
+ internal int is_last;
+ [FieldOffset(8)]
+ internal uint length;
+ [FieldOffset(16)]
+ internal FLAC__StreamMetadata_StreamInfo stream_info;
+ // [FieldOffset(16)]
+ // internal FLAC__StreamMetadata_Padding padding;
+ // [FieldOffset(16)]
+ // internal FLAC__StreamMetadata_Application application;
+ // [FieldOffset(16)]
+ // internal FLAC__StreamMetadata_SeekTable seek_table;
+ // [FieldOffset(16)]
+ // internal FLAC__StreamMetadata_VorbisComment vorbis_comment;
+ // [FieldOffset(16)]
+ // internal FLAC__StreamMetadata_CueSheet cue_sheet;
+ // [FieldOffset(16)]
+ // internal FLAC__StreamMetadata_Picture picture;
+ // [FieldOffset(16)]
+ // internal FLAC__StreamMetadata_Unknown unknown;
+ };
+
+ [StructLayout(LayoutKind.Sequential), Serializable]
+ internal unsafe struct FLAC__StreamMetadata_StreamInfo
+ {
+ internal int min_blocksize, max_blocksize;
+ internal int min_framesize, max_framesize;
+ internal int sample_rate;
+ internal int channels;
+ internal int bits_per_sample;
+ internal long total_samples;
+ internal fixed byte md5sum[16];
+ };
+
+ internal enum FLAC__ChannelAssignment
+ {
+ FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0,
+ FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1,
+ FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2,
+ FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3
+ };
+
+ internal enum FLAC__FrameNumberType
+ {
+ FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER,
+ FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER
+ };
+
+ internal enum FLAC__StreamDecoderInitStatus
+ {
+ FLAC__STREAM_DECODER_INIT_STATUS_OK = 0,
+ FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER,
+ FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS,
+ FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR,
+ FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE,
+ FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED
+ };
+
+ internal enum FLAC__StreamDecoderWriteStatus
+ {
+ FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE,
+ FLAC__STREAM_DECODER_WRITE_STATUS_ABORT
+ };
+
+ internal enum FLAC__StreamDecoderReadStatus
+ {
+ FLAC__STREAM_DECODER_READ_STATUS_CONTINUE,
+ FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM,
+ FLAC__STREAM_DECODER_READ_STATUS_ABORT
+ };
+
+ internal enum FLAC__StreamDecoderSeekStatus
+ {
+ FLAC__STREAM_DECODER_SEEK_STATUS_OK,
+ FLAC__STREAM_DECODER_SEEK_STATUS_ERROR,
+ FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED
+ };
+
+ internal enum FLAC__StreamDecoderTellStatus
+ {
+ FLAC__STREAM_DECODER_TELL_STATUS_OK,
+ FLAC__STREAM_DECODER_TELL_STATUS_ERROR,
+ FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED
+ };
+
+ internal enum FLAC__StreamDecoderLengthStatus
+ {
+ FLAC__STREAM_DECODER_LENGTH_STATUS_OK,
+ FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR,
+ FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED
+ };
+
+ internal enum FLAC__StreamDecoderErrorStatus
+ {
+ FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC,
+ FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER,
+ FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH,
+ FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM
+ };
+
+ internal enum FLAC__StreamDecoderState
+ {
+ FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0,
+ FLAC__STREAM_DECODER_READ_METADATA,
+ FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC,
+ FLAC__STREAM_DECODER_READ_FRAME,
+ FLAC__STREAM_DECODER_END_OF_STREAM,
+ FLAC__STREAM_DECODER_OGG_ERROR,
+ FLAC__STREAM_DECODER_SEEK_ERROR,
+ FLAC__STREAM_DECODER_ABORTED,
+ FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR,
+ FLAC__STREAM_DECODER_UNINITIALIZED
+ };
+
+ internal enum FLAC__StreamEncoderState : int
+ {
+ FLAC__STREAM_ENCODER_OK = 0,
+ FLAC__STREAM_ENCODER_UNINITIALIZED,
+ FLAC__STREAM_ENCODER_OGG_ERROR,
+ FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR,
+ FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA,
+ FLAC__STREAM_ENCODER_CLIENT_ERROR,
+ FLAC__STREAM_ENCODER_IO_ERROR,
+ FLAC__STREAM_ENCODER_FRAMING_ERROR,
+ FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR
+ };
+
+ internal enum FLAC__StreamEncoderInitStatus
+ {
+ FLAC__STREAM_ENCODER_INIT_STATUS_OK = 0,
+ FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR,
+ FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER,
+ FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS,
+ FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS,
+ FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE,
+ FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE,
+ FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE,
+ FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER,
+ FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION,
+ FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER,
+ FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE,
+ FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA,
+ FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED
+ };
+
+ internal enum FLAC__MetadataType : int
+ {
+ FLAC__METADATA_TYPE_STREAMINFO = 0,
+ FLAC__METADATA_TYPE_PADDING = 1,
+ FLAC__METADATA_TYPE_APPLICATION = 2,
+ FLAC__METADATA_TYPE_SEEKTABLE = 3,
+ FLAC__METADATA_TYPE_VORBIS_COMMENT = 4,
+ FLAC__METADATA_TYPE_CUESHEET = 5,
+ FLAC__METADATA_TYPE_PICTURE = 6,
+ FLAC__METADATA_TYPE_UNDEFINED = 7,
+ FLAC__MAX_METADATA_TYPE = 126,
+ };
+
+ internal enum FLAC__StreamEncoderWriteStatus
+ {
+ FLAC__STREAM_ENCODER_WRITE_STATUS_OK = 0,
+ FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR
+ };
+
+ internal enum FLAC__StreamEncoderSeekStatus
+ {
+ FLAC__STREAM_ENCODER_SEEK_STATUS_OK,
+ FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR,
+ FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED
+ };
+
+ internal enum FLAC__StreamEncoderTellStatus
+ {
+ FLAC__STREAM_ENCODER_TELL_STATUS_OK,
+ FLAC__STREAM_ENCODER_TELL_STATUS_ERROR,
+ FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED
+ };
+}
diff --git a/ThirdParty/Win32/libFLAC_dynamic.dll b/ThirdParty/Win32/libFLAC_dynamic.dll
new file mode 100644
index 0000000..71c72df
Binary files /dev/null and b/ThirdParty/Win32/libFLAC_dynamic.dll differ
diff --git a/ThirdParty/x64/libFLAC_dynamic.dll b/ThirdParty/x64/libFLAC_dynamic.dll
new file mode 100644
index 0000000..e859fa8
Binary files /dev/null and b/ThirdParty/x64/libFLAC_dynamic.dll differ