Use libFLAC_dynamic.dll instead of using a static library. Update libFLAC to 1.3.2

This commit is contained in:
Grigory Chudov
2018-03-11 11:52:09 -04:00
parent 89845b532b
commit a66bfe28cc
7 changed files with 1031 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net40;net20;netstandard2.0</TargetFrameworks>
<Version>2.1.6.0</Version>
<AssemblyName>CUETools.Codecs.libFLAC</AssemblyName>
<RootNamespace>CUETools.Codecs.libFLAC</RootNamespace>
<Product>CUETools</Product>
<Description>A library for encoding flac using official encoder.</Description>
<Copyright>Copyright (c) 2008-2018 Grigory Chudov</Copyright>
<Authors>Grigory Chudov</Authors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputPath>..\bin\$(Configuration)\plugins</OutputPath>
<RepositoryUrl>https://github.com/gchudov/cuetools.net</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Company />
</PropertyGroup>
<ItemDefinitionGroup>
<ProjectReference>
<Private>False</Private>
</ProjectReference>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\CUETools.Codecs\CUETools.Codecs.csproj" />
</ItemGroup>
</Project>

View File

@@ -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
}
};
}

View File

@@ -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<string>^ tag_values = _tags->GetValues(tagno);
for (int valno = 0; valno < tag_values->Length; valno++)
{
UTF8Encoding^ enc = new UTF8Encoding();
array<Byte>^ 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<Byte>^ bvalue = new array<Byte>((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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
};
}

BIN
ThirdParty/Win32/libFLAC_dynamic.dll vendored Normal file

Binary file not shown.

BIN
ThirdParty/x64/libFLAC_dynamic.dll vendored Normal file

Binary file not shown.