diff --git a/CUETools.Codecs.FLAC/CUETools.Codecs.FLAC.cpp b/CUETools.Codecs.FLAC/CUETools.Codecs.FLAC.cpp new file mode 100644 index 0000000..eb88772 --- /dev/null +++ b/CUETools.Codecs.FLAC/CUETools.Codecs.FLAC.cpp @@ -0,0 +1,692 @@ +// **************************************************************************** +// +// Copyright (c) 2006-2007 Moitah (moitah@yahoo.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the author nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// **************************************************************************** + +using namespace System; +using namespace System::Text; +using namespace System::IO; +using namespace System::Collections::Generic; +using namespace System::Collections::Specialized; +using namespace System::Runtime::InteropServices; +using namespace CUETools::Codecs; + +#include "FLAC\all.h" +#include + +struct FLAC__Metadata_Iterator +{ + int dummy; +}; + +struct FLAC__Metadata_Chain +{ + int dummy; +}; + +namespace CUETools { namespace Codecs { namespace FLAC { + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate FLAC__StreamDecoderWriteStatus DecoderWriteDelegate(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate void DecoderMetadataDelegate(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate void DecoderErrorDelegate(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate FLAC__StreamDecoderReadStatus DecoderReadDelegate (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate FLAC__StreamDecoderSeekStatus DecoderSeekDelegate (const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate FLAC__StreamDecoderTellStatus DecoderTellDelegate (const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate FLAC__StreamDecoderLengthStatus DecoderLengthDelegate (const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); + [UnmanagedFunctionPointer(CallingConvention::Cdecl)] + public delegate FLAC__bool DecoderEofDelegate (const FLAC__StreamDecoder *decoder, void *client_data); + + public ref class FLACReader : public IAudioSource + { + public: + FLACReader(String^ path, Stream^ IO) + { + _tags = gcnew NameValueCollection(); + + _writeDel = gcnew DecoderWriteDelegate(this, &FLACReader::WriteCallback); + _metadataDel = gcnew DecoderMetadataDelegate(this, &FLACReader::MetadataCallback); + _errorDel = gcnew DecoderErrorDelegate(this, &FLACReader::ErrorCallback); + _readDel = gcnew DecoderReadDelegate(this, &FLACReader::ReadCallback); + _seekDel = gcnew DecoderSeekDelegate(this, &FLACReader::SeekCallback); + _tellDel = gcnew DecoderTellDelegate(this, &FLACReader::TellCallback); + _lengthDel = gcnew DecoderLengthDelegate(this, &FLACReader::LengthCallback); + _eofDel = gcnew DecoderEofDelegate(this, &FLACReader::EofCallback); + + _decoderActive = false; + + _sampleOffset = 0; + _sampleBuffer = nullptr; + _path = path; + _bufferOffset = 0; + _bufferLength = 0; + + _IO = (IO != nullptr) ? IO : gcnew FileStream (path, FileMode::Open, FileAccess::Read, FileShare::Read); + + _decoder = FLAC__stream_decoder_new(); + + if (!FLAC__stream_decoder_set_metadata_respond (_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT)) + throw gcnew Exception("Unable to setup the decoder."); + + if (FLAC__stream_decoder_init_stream(_decoder, + (FLAC__StreamDecoderReadCallback)Marshal::GetFunctionPointerForDelegate(_readDel).ToPointer(), + _IO->CanSeek?(FLAC__StreamDecoderSeekCallback)Marshal::GetFunctionPointerForDelegate(_seekDel).ToPointer():NULL, + _IO->CanSeek?(FLAC__StreamDecoderTellCallback)Marshal::GetFunctionPointerForDelegate(_tellDel).ToPointer():NULL, + _IO->CanSeek?(FLAC__StreamDecoderLengthCallback)Marshal::GetFunctionPointerForDelegate(_lengthDel).ToPointer():NULL, + _IO->CanSeek?(FLAC__StreamDecoderEofCallback)Marshal::GetFunctionPointerForDelegate(_eofDel).ToPointer():NULL, + (FLAC__StreamDecoderWriteCallback)Marshal::GetFunctionPointerForDelegate(_writeDel).ToPointer(), + (FLAC__StreamDecoderMetadataCallback)Marshal::GetFunctionPointerForDelegate(_metadataDel).ToPointer(), + (FLAC__StreamDecoderErrorCallback)Marshal::GetFunctionPointerForDelegate(_errorDel).ToPointer(), + NULL) != FLAC__STREAM_DECODER_INIT_STATUS_OK) + { + throw gcnew Exception("Unable to initialize the decoder."); + } + + _decoderActive = true; + + if (!FLAC__stream_decoder_process_until_end_of_metadata(_decoder)) { + throw gcnew Exception("Unable to retrieve metadata."); + } + + } + + ~FLACReader () + { + Close (); + } + + virtual property Int32 BitsPerSample { + Int32 get() { + return _bitsPerSample; + } + } + + virtual property Int32 ChannelCount { + Int32 get() { + return _channelCount; + } + } + + virtual property Int32 SampleRate { + Int32 get() { + return _sampleRate; + } + } + + virtual property UInt64 Length { + UInt64 get() { + return _sampleCount; + } + } + + virtual property UInt64 Position { + UInt64 get() + { + return _sampleOffset - SamplesInBuffer; + } + void set(UInt64 offset) + { + _sampleOffset = offset; + _bufferOffset = 0; + _bufferLength = 0; + if (!FLAC__stream_decoder_seek_absolute(_decoder, offset)) { + throw gcnew Exception("Unable to seek."); + } + } + } + + virtual property String^ Path { + String^ get() { + return _path; + } + } + + virtual property NameValueCollection^ Tags { + NameValueCollection^ get () { + return _tags; + } + void set (NameValueCollection ^tags) { + _tags = tags; + } + } + + virtual 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 = gcnew 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 gcnew Exception("Unable to add tags, must be valid utf8."); + } + if(!FLAC__metadata_object_vorbiscomment_append_comment(vorbiscomment, entry, /*copy=*/false)) { + throw gcnew 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; + } + + virtual property UInt64 Remaining { + UInt64 get() { + return _sampleCount - _sampleOffset + SamplesInBuffer; + } + } + + virtual void Close() { + if (_decoderActive) + { + FLAC__stream_decoder_finish(_decoder); + FLAC__stream_decoder_delete(_decoder); + _decoderActive = false; + } + if (_IO != nullptr) + { + _IO->Close (); + _IO = nullptr; + } + } + + virtual UInt32 Read([Out] array^ buff, UInt32 sampleCount) + { + UInt32 buffOffset = 0; + UInt32 samplesNeeded = sampleCount; + + while (samplesNeeded != 0) + { + if (SamplesInBuffer == 0) + { + _bufferOffset = 0; + _bufferLength = 0; + do + { + if (!FLAC__stream_decoder_process_single(_decoder)) + throw gcnew Exception("An error occurred while decoding."); + } while (_bufferLength == 0); + _sampleOffset += _bufferLength; + } + UInt32 copyCount = Math::Min(samplesNeeded, SamplesInBuffer); + Array::Copy(_sampleBuffer, _bufferOffset * _channelCount, buff, buffOffset * _channelCount, copyCount * _channelCount); + samplesNeeded -= copyCount; + buffOffset += copyCount; + _bufferOffset += copyCount; + } + return sampleCount; + } + + private: + DecoderWriteDelegate^ _writeDel; + DecoderMetadataDelegate^ _metadataDel; + DecoderErrorDelegate^ _errorDel; + DecoderReadDelegate^ _readDel; + DecoderSeekDelegate^ _seekDel; + DecoderTellDelegate^ _tellDel; + DecoderLengthDelegate^ _lengthDel; + DecoderEofDelegate^ _eofDel; + FLAC__StreamDecoder *_decoder; + Int64 _sampleCount, _sampleOffset; + Int32 _bitsPerSample, _channelCount, _sampleRate; + array^ _sampleBuffer; + array^ _readBuffer; + NameValueCollection^ _tags; + String^ _path; + bool _decoderActive; + Stream^ _IO; + UInt32 _bufferOffset, _bufferLength; + + property UInt32 SamplesInBuffer { + UInt32 get () + { + return (UInt32) (_bufferLength - _bufferOffset); + } + } + + FLAC__StreamDecoderWriteStatus WriteCallback(const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) + { + Int32 sampleCount = frame->header.blocksize; + + if (_bufferLength > 0) { + throw gcnew Exception("Received unrequested samples."); + } + + if ((frame->header.bits_per_sample != _bitsPerSample) || + (frame->header.channels != _channelCount) || + (frame->header.sample_rate != _sampleRate)) + { + throw gcnew Exception("Format changes within a file are not allowed."); + } + + if ((_sampleBuffer == nullptr) || (_sampleBuffer->GetLength(0) < sampleCount)) { + _sampleBuffer = gcnew array(sampleCount, _channelCount); + } + + for (Int32 iChan = 0; iChan < _channelCount; iChan++) { + interior_ptr pMyBuffer = &_sampleBuffer[0, iChan]; + const FLAC__int32 *pFLACBuffer = buffer[iChan]; + const FLAC__int32 *pFLACBufferEnd = pFLACBuffer + sampleCount; + + while (pFLACBuffer < pFLACBufferEnd) { + *pMyBuffer = *pFLACBuffer; + pMyBuffer += _channelCount; + pFLACBuffer++; + } + } + + _bufferLength = sampleCount; + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } + + void MetadataCallback(const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, void *client_data) + { + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) + { + _bitsPerSample = metadata->data.stream_info.bits_per_sample; + _channelCount = metadata->data.stream_info.channels; + _sampleRate = metadata->data.stream_info.sample_rate; + _sampleCount = metadata->data.stream_info.total_samples; + } + if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) + { + for (unsigned tagno = 0; tagno < metadata->data.vorbis_comment.num_comments; tagno ++) + { + char * field_name, * field_value; + if(!FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(metadata->data.vorbis_comment.comments[tagno], &field_name, &field_value)) + throw gcnew Exception("Unable to parse vorbis comment."); + String^ name = Marshal::PtrToStringAnsi ((IntPtr) field_name); + free (field_name); + array^ bvalue = gcnew array((int) strlen (field_value)); + Marshal::Copy ((IntPtr) field_value, bvalue, 0, (int) strlen (field_value)); + free (field_value); + UTF8Encoding^ enc = gcnew UTF8Encoding(); + String ^value = enc->GetString (bvalue); + _tags->Add (name, value); + } + } + } + + void ErrorCallback(const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, void *client_data) + { + switch (status) { + case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: + throw gcnew Exception("Synchronization was lost."); + case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: + throw gcnew Exception("Encountered a corrupted frame header."); + case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: + throw gcnew Exception("Frame CRC mismatch."); + default: + throw gcnew Exception("An unknown error has occurred."); + } + } + + FLAC__StreamDecoderReadStatus ReadCallback (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + { + if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */ + + if (_readBuffer == nullptr || _readBuffer->Length < *bytes) + _readBuffer = gcnew array(*bytes < 0x4000 ? 0x4000 : *bytes); + + *bytes = _IO->Read (_readBuffer, 0, *bytes); + //if(ferror(decoder->private_->file)) + //return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + //else + if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + + Marshal::Copy (_readBuffer, 0, (IntPtr)buffer, *bytes); + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + + FLAC__StreamDecoderSeekStatus SeekCallback (const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) + { + //if (!_IO->CanSeek) + // return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + _IO->Position = absolute_byte_offset; + //if( < 0) + //return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + } + + FLAC__StreamDecoderTellStatus TellCallback (const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + { + *absolute_byte_offset = _IO->Position; + // FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } + + FLAC__StreamDecoderLengthStatus LengthCallback (const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) + { + //if (!_IO->CanSeek) + // return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + // FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + *stream_length = _IO->Length; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } + + FLAC__bool EofCallback (const FLAC__StreamDecoder *decoder, void *client_data) + { + return _IO->Position == _IO->Length; + } + }; + + public ref class FLACWriter : IAudioDest + { + public: + FLACWriter(String^ path, Int32 bitsPerSample, Int32 channelCount, Int32 sampleRate) + { + if (bitsPerSample < 16 || bitsPerSample > 24) + throw gcnew Exception("Bits per sample must be 16..24."); + + _initialized = false; + _path = path; + _finalSampleCount = 0; + _samplesWritten = 0; + _bitsPerSample = bitsPerSample; + _channelCount = channelCount; + _sampleRate = sampleRate; + _compressionLevel = 5; + _paddingLength = 8192; + _verify = false; + _blockSize = 0; + _tags = gcnew NameValueCollection(); + + _encoder = FLAC__stream_encoder_new(); + + FLAC__stream_encoder_set_bits_per_sample(_encoder, bitsPerSample); + FLAC__stream_encoder_set_channels(_encoder, channelCount); + FLAC__stream_encoder_set_sample_rate(_encoder, sampleRate); + } + + virtual void Close() { + FLAC__stream_encoder_finish(_encoder); + + for (int i = 0; i < _metadataCount; i++) { + FLAC__metadata_object_delete(_metadataList[i]); + } + delete[] _metadataList; + _metadataList = 0; + _metadataCount = 0; + + FLAC__stream_encoder_delete(_encoder); + + if ((_finalSampleCount != 0) && (_samplesWritten != _finalSampleCount)) { + throw gcnew Exception("Samples written differs from the expected sample count."); + } + + _tags->Clear (); + } + + virtual void Delete() + { + try { Close (); } catch (Exception^) {} + File::Delete(_path); + } + + virtual property Int64 FinalSampleCount { + Int64 get() { + return _finalSampleCount; + } + void set(Int64 value) { + if (value < 0) { + throw gcnew Exception("Invalid final sample count."); + } + if (_initialized) { + throw gcnew Exception("Final sample count cannot be changed after encoding begins."); + } + _finalSampleCount = value; + } + } + + virtual property Int64 BlockSize + { + void set(Int64 value) + { + _blockSize = value; + } + } + + virtual property int BitsPerSample + { + int get() { return _bitsPerSample; } + } + + virtual bool SetTags (NameValueCollection^ tags) + { + _tags = tags; + return true; + } + + virtual property String^ Path { + String^ get() { + return _path; + } + } + + virtual void Write(array^ sampleBuffer, UInt32 sampleCount) { + if (!_initialized) Initialize(); + + pin_ptr pSampleBuffer = &sampleBuffer[0, 0]; + + if (!FLAC__stream_encoder_process_interleaved(_encoder, + (const FLAC__int32*)pSampleBuffer, sampleCount)) + { + throw gcnew Exception("An error occurred while encoding."); + } + + _samplesWritten += sampleCount; + } + + property Int32 CompressionLevel { + Int32 get() { + return _compressionLevel; + } + void set(Int32 value) { + if ((value < 0) || (value > 8)) { + throw gcnew Exception("Invalid compression level."); + } + _compressionLevel = value; + } + } + + property Boolean Verify { + Boolean get() { + return _verify; + } + void set(Boolean value) { + _verify = value; + } + } + + property Int32 PaddingLength { + Int32 get() { + return _paddingLength; + } + void set(Int32 value) { + if (value < 0) { + throw gcnew Exception("Invalid padding length."); + } + _paddingLength = value; + } + } + + private: + FLAC__StreamEncoder *_encoder; + bool _initialized; + String^ _path; + Int64 _finalSampleCount, _samplesWritten, _blockSize; + Int32 _bitsPerSample, _channelCount, _sampleRate; + Int32 _compressionLevel; + Int32 _paddingLength; + Boolean _verify; + FLAC__StreamMetadata **_metadataList; + int _metadataCount; + NameValueCollection^ _tags; + + void Initialize() { + FLAC__StreamMetadata *padding, *seektable, *vorbiscomment; + IntPtr pathChars; + FILE *hFile; + + _metadataList = new FLAC__StreamMetadata*[8]; + _metadataCount = 0; + + if (_finalSampleCount != 0) { + seektable = FLAC__metadata_object_new(FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__metadata_object_seektable_template_append_spaced_points_by_samples( + seektable, _sampleRate * 10, _finalSampleCount); + FLAC__metadata_object_seektable_template_sort(seektable, true); + _metadataList[_metadataCount++] = seektable; + } + + 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 = gcnew 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 gcnew Exception("Unable to add tags, must be valid utf8."); + } + if(!FLAC__metadata_object_vorbiscomment_append_comment(vorbiscomment, entry, /*copy=*/false)) { + throw gcnew Exception("Unable to add tags."); + } + delete [] value; + } + delete [] tag; + } + _metadataList[_metadataCount++] = vorbiscomment; + + if (_paddingLength != 0) { + padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING); + padding->length = _paddingLength; + _metadataList[_metadataCount++] = padding; + } + + FLAC__stream_encoder_set_metadata(_encoder, _metadataList, _metadataCount); + + FLAC__stream_encoder_set_verify(_encoder, _verify); + + if (_finalSampleCount != 0) { + FLAC__stream_encoder_set_total_samples_estimate(_encoder, _finalSampleCount); + } + + FLAC__stream_encoder_set_compression_level(_encoder, _compressionLevel); + + if (_blockSize > 0) + FLAC__stream_encoder_set_blocksize(_encoder, (unsigned)_blockSize); + + pathChars = Marshal::StringToHGlobalUni(_path); + hFile = _wfopen((const wchar_t*)pathChars.ToPointer(), L"w+b"); + Marshal::FreeHGlobal(pathChars); + + if (FLAC__stream_encoder_init_FILE(_encoder, hFile, NULL, NULL) != + FLAC__STREAM_ENCODER_INIT_STATUS_OK) + { + throw gcnew Exception("Unable to initialize the encoder."); + } + + _initialized = true; + } + }; +} +} +}