diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/Flac/Flac.Test/Flac.Test.csproj b/Flac/Flac.Test/Flac.Test.csproj new file mode 100644 index 0000000..1aea248 --- /dev/null +++ b/Flac/Flac.Test/Flac.Test.csproj @@ -0,0 +1,85 @@ + + + + Debug + AnyCPU + {1576AB6D-F8F4-42FC-BDC7-67D08648AEA6} + Library + Properties + Flac.Test + Flac.Test + v3.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/Flac/Flac.Test/Properties/AssemblyInfo.cs b/Flac/Flac.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..33d525c --- /dev/null +++ b/Flac/Flac.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Flac.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Flac.Test")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2376dc52-6d6f-40ac-a34c-44a1523983ac")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Flac/Flac.sln b/Flac/Flac.sln new file mode 100644 index 0000000..d3539a7 --- /dev/null +++ b/Flac/Flac.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flac", "Flac\Flac.csproj", "{ECB7B567-6A67-43F4-89CA-64E5D9F07B97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flac.Test", "Flac.Test\Flac.Test.csproj", "{1576AB6D-F8F4-42FC-BDC7-67D08648AEA6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ECB7B567-6A67-43F4-89CA-64E5D9F07B97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECB7B567-6A67-43F4-89CA-64E5D9F07B97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECB7B567-6A67-43F4-89CA-64E5D9F07B97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECB7B567-6A67-43F4-89CA-64E5D9F07B97}.Release|Any CPU.Build.0 = Release|Any CPU + {1576AB6D-F8F4-42FC-BDC7-67D08648AEA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1576AB6D-F8F4-42FC-BDC7-67D08648AEA6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1576AB6D-F8F4-42FC-BDC7-67D08648AEA6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1576AB6D-F8F4-42FC-BDC7-67D08648AEA6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Flac/Flac/ChannelData.cs b/Flac/Flac/ChannelData.cs new file mode 100644 index 0000000..b7ac194 --- /dev/null +++ b/Flac/Flac/ChannelData.cs @@ -0,0 +1,53 @@ +using Flac.Frame; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac +{ + /// + /// FLAC channel data. + /// This class holds the data for the channels in a FLAC frame. + /// + public class ChannelData + { + /// + /// The output signal. + /// + public int[] Output + { + get; + private set; + } + + /// + /// The risidual signal. + /// + public int[] Residual + { + get; + private set; + } + + /// + /// The Entropy signal. + /// + public EntropyPartitionedRiceContents PartitionedRiceContents + { + get; + private set; + } + + /// + /// Creates a new instance of ChannelData + /// + /// The block size + public ChannelData(int size) + { + Output = new int[size]; + Residual = new int[size]; + PartitionedRiceContents = new EntropyPartitionedRiceContents(); + } + } +} diff --git a/Flac/Flac/Flac.csproj b/Flac/Flac/Flac.csproj new file mode 100644 index 0000000..3b5c0ae --- /dev/null +++ b/Flac/Flac/Flac.csproj @@ -0,0 +1,76 @@ + + + + + Debug + AnyCPU + {ECB7B567-6A67-43F4-89CA-64E5D9F07B97} + Library + Properties + Flac + Flac + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Flac/Flac/Frame/BadHeaderException.cs b/Flac/Flac/Frame/BadHeaderException.cs new file mode 100644 index 0000000..3a3db4b --- /dev/null +++ b/Flac/Flac/Frame/BadHeaderException.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Flac.Frame +{ + /// + /// Bad header exception + /// + public class BadHeaderException : IOException + { + /// + /// Creates a new instance of bad header exception. + /// + public BadHeaderException() + : base() + { + } + + /// + /// Creates a new instance of bad header exception with specified message. + /// + /// The message + public BadHeaderException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of bad header exception with specified message + /// and a reference to the inner exception that is the cause of this exception. + /// + /// The message + /// The inner exception + public BadHeaderException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/Flac/Flac/Frame/Channel.cs b/Flac/Flac/Frame/Channel.cs new file mode 100644 index 0000000..4791bd3 --- /dev/null +++ b/Flac/Flac/Frame/Channel.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Frame +{ + /// + /// Base class for FLAC subframe (channel) classes. + /// + public abstract class Channel + { + #region Constants + protected const int TypeLength = 2; + protected const int PartitionedRiceOrderLength = 4; + #endregion + + protected enum EntropyCodingMethod + { + PartitionedRice = 0 + } + + /// + /// The FLAC Frame Header. + /// + protected Header Header { get; set; } + + /// + /// The number of wasted bits in the frame. + /// + public int WastedBits { get; protected set; } + + /// + /// Creates a new instance of channel. + /// + /// The FLAC Frame Header. + /// The number of wasted bits in the frame. + protected Channel(Header header, int wastedBits) + { + Header = header; + WastedBits = wastedBits; + } + } +} diff --git a/Flac/Flac/Frame/ChannelConstant.cs b/Flac/Flac/Frame/ChannelConstant.cs new file mode 100644 index 0000000..513a920 --- /dev/null +++ b/Flac/Flac/Frame/ChannelConstant.cs @@ -0,0 +1,39 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Frame +{ + /// + /// FLAC constant subframe (channel) data. + /// This class represents a FLAC subframe (channel) for a Constant data + /// + public class ChannelConstant : Channel + { + /// + /// The constant signal value. + /// + private int value; + + /// + /// Creates a new instance of ChannelConstant + /// + /// The bit reader + /// The FLAC Frame Header + /// The decoded channel data (output) + /// The bits-per-second + /// The bits wasted in the frame + /// Thrown if error reading from BitReader + public ChannelConstant(BitReader read, Header header, ChannelData channelData, int bps, int wastedBits) + : base(header, wastedBits) + { + value = (int)read.ReadRawUInt32(bps); + + // Decode the subframe + for (int i = 0; i < header.BlockSize; i++) + channelData.Output[i] = value; + } + } +} diff --git a/Flac/Flac/Frame/ChannelFixed.cs b/Flac/Flac/Frame/ChannelFixed.cs new file mode 100644 index 0000000..ab5fd9f --- /dev/null +++ b/Flac/Flac/Frame/ChannelFixed.cs @@ -0,0 +1,76 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Flac.Frame +{ + /// + /// Fixed FLAC subframe (channel). + /// + public class ChannelFixed : Channel + { + private const int MaxFixedOrder = 4; + + /// + /// The residual coding method. + /// + private Frame.EntropyCodingMethod entropyCodingMethod; + + /// + /// The polynomial order. + /// + private int order; + + /// + /// Warmup samples to prime the predictor, length == order. + /// + private int[] warmup = new int[MaxFixedOrder]; + + /// + /// The residual signal, length == (blocksize minus order) samples. + /// + private int[] residual; + + /// + /// Creates a new instance of ChannelConstant + /// + /// The bit reader + /// The FLAC Frame Header + /// The decoded channel data (output) + /// The bits-per-second + /// The bits wasted in the frame + /// The predicate order + /// Thrown if error reading from BitReader + public ChannelFixed(BitReader read, Header header, ChannelData channelData, int bps, int wastedBits, int order) + : base(header, wastedBits) + { + residual = channelData.Residual; + this.order = order; + + // Read warmup samples + for (int u = 0; u < order; u++) + warmup[u] = (int)read.ReadRawUInt32(bps); + + // Read entropy coding method info + int type = (int)read.ReadRawUInt32(TypeLength); + EntropyPartitionedRice pr; + + switch (type) + { + case (int)EntropyCodingMethod.PartitionedRice: + pr = new EntropyPartitionedRice(); + entropyCodingMethod = pr; + pr.Order = (int)read.ReadRawUInt32(PartitionedRiceOrderLength); + pr.Contents = channelData.PartitionedRiceContents; + pr.ReadResidual(read, order, pr.Order, header, channelData.Residual); + break; + + default: + throw new IOException("Unparseable stream!"); + } + } + } +} diff --git a/Flac/Flac/Frame/EntropyCodingMethod.cs b/Flac/Flac/Frame/EntropyCodingMethod.cs new file mode 100644 index 0000000..a86b02b --- /dev/null +++ b/Flac/Flac/Frame/EntropyCodingMethod.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Frame +{ + public interface EntropyCodingMethod + { + } +} diff --git a/Flac/Flac/Frame/EntropyPartitionedRice.cs b/Flac/Flac/Frame/EntropyPartitionedRice.cs new file mode 100644 index 0000000..c8b7c7a --- /dev/null +++ b/Flac/Flac/Frame/EntropyPartitionedRice.cs @@ -0,0 +1,86 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Frame +{ + /// + /// This class holds the Entropy Partitioned Rice contents. + /// + public class EntropyPartitionedRice : EntropyCodingMethod + { + #region Constants + private const int ParameterLength = 4; // bits + private const int RawLength = 5; // bits + private const UInt32 EscapeParameter = 15; + #endregion + + /// + /// The partition order, i.e. # of contexts = 2 ^ order. + /// + public int Order { get; set; } + + /// + /// The context's Rice parameters and/or raw bits. + /// + public EntropyPartitionedRiceContents Contents + { + get; + set; + } + + /// + /// Read compressed signal residual data. + /// + /// The bit reader + /// The predicate order + /// The partition order + /// The FLAC Frame Header + /// The residual signal (output) + /// Thrown if error reading from BitReader + public void ReadResidual(BitReader read, int predictorOrder, int partitionOrder, Header header, int[] residual) + { + int sample = 0; + int partitions = 1 << partitionOrder; + + int partitionSamples; + if (partitionOrder > 0) + partitionSamples = (header.BlockSize >> partitionOrder); + else + partitionSamples = (header.BlockSize - predictorOrder); + + Contents = new EntropyPartitionedRiceContents(); + Contents.EnsureSize(Math.Max(6, partitionOrder)); + Contents.Parameters = new int[partitions]; // why allocate again? + + for (int partition = 0; partition < partitions; partition++) + { + int riceParameter = (int)read.ReadRawUInt32(ParameterLength); + Contents.Parameters[partition] = riceParameter; + + if (riceParameter < EscapeParameter) + { + int u = (partitionOrder == 0 || partition > 0) ? + (partitionSamples) : + (partitionSamples - predictorOrder); + + read.ReadRiceSignedBlock(residual, sample, u, riceParameter); + sample += u; + } + + else + { + riceParameter = (int)read.ReadRawUInt32(RawLength); + Contents.RawBits[partition] = riceParameter; + + int u = (partitionOrder == 0 || partition > 0) ? (0) : (predictorOrder); + + for (; u < partitionSamples; u++, sample++) + residual[sample] = (int)read.ReadRawUInt32(riceParameter); + } + } + } + } +} diff --git a/Flac/Flac/Frame/EntropyPartitionedRiceContents.cs b/Flac/Flac/Frame/EntropyPartitionedRiceContents.cs new file mode 100644 index 0000000..2ff1ad0 --- /dev/null +++ b/Flac/Flac/Frame/EntropyPartitionedRiceContents.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Frame +{ + public class EntropyPartitionedRiceContents + { + public int[] Parameters { get; set; } + public int[] RawBits { get; set; } + + /// + /// The capacity of the parameters and raw_bits arrays specified as an order. + /// i.e. the number of array elements allocated is 2 ^ capacity_by_order. + /// + private int capacityByOrder = 0; + + /// + /// Ensure enough menory has been allocated. + /// + /// The maximum partition order + public void EnsureSize(int maxPartitionOrder) + { + if (capacityByOrder >= maxPartitionOrder) + return; + + Parameters = new int[(1 << maxPartitionOrder)]; + RawBits = new int[(1 << maxPartitionOrder)]; + + capacityByOrder = maxPartitionOrder; + } + } +} diff --git a/Flac/Flac/Frame/Header.cs b/Flac/Flac/Frame/Header.cs new file mode 100644 index 0000000..fbea925 --- /dev/null +++ b/Flac/Flac/Frame/Header.cs @@ -0,0 +1,70 @@ +using Flac.IO; +using Flac.Metadata; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Frame +{ + /// + /// Frame header class. + /// + public class Header + { + /// + /// Gets or sets the number of samples per subframe. + /// + public int BlockSize { get; set; } + + /// + /// Gets or sets the sample rate in Hz. + /// + public int SampleRate { get; set; } + + /// + /// Gets or sets the number of channels (== number of subframes). + /// + public int Channels { get; set; } + + /// + /// Gets or sets the channel assignment for the frame. + /// + public int ChannelAssignment { get; set; } + + /// + /// Gets or sets the sample resolution. + /// + public int BitsPerSample { get; set; } + + /// + /// Gets or sets the frame number or sample number of first sample in frame. + /// Use the number_type value to determine which to use. + /// + public int FrameNumber { get; set; } + + /// + /// The sample number for the first sample in the frame. + /// + public int SampleNumber { get; set; } + + /// + /// CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0) + /// of the raw frame header bytes, meaning everything before the CRC byte + /// including the sync code. + /// + protected byte crc; + + /// + /// + /// + /// + /// + /// + public Header(BitReader @is, byte[] headerWarmup, StreamInfo streamInfo) + { + FrameNumber = -1; + SampleNumber = -1; + } + } +} diff --git a/Flac/Flac/IO/BitReader.cs b/Flac/Flac/IO/BitReader.cs new file mode 100644 index 0000000..2b4ed84 --- /dev/null +++ b/Flac/Flac/IO/BitReader.cs @@ -0,0 +1,788 @@ +using Flac.Util; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; + +namespace Flac.IO +{ + public class BitReader + { + #region Constants + private const int DefaultCapacity = 2048; + private const int BitsPerByte = 8; + private const int BitsPerByteLog2 = 3; + private const byte ByteTopBitOne = (byte)0x80; + #endregion + + #region Private members + private byte[] buffer; + private int putByte = 0; + private int getByte = 0; + private int getBit = 0; + private int availBits = 0; + private int totalBitsRead = 0; + private UInt16 readCrc16 = 0; + private Stream stream; + #endregion + + #region Constructor + /// + /// Creates a new instance of BitReader + /// + /// Input data stream + public BitReader(Stream stream) + { + buffer = new byte[DefaultCapacity]; + + this.stream = stream; + } + #endregion + + /// + /// Reads data from stream. + /// + /// Number of bytes read + private int ReadFromStream() + { + // first shift the unconsumed buffer data toward the front as much as possible + if (getByte > 0 && putByte > getByte) + { + Array.Copy(buffer, getByte, buffer, 0, putByte - getByte); + } + putByte -= getByte; + getByte = 0; + + // Set the target for reading, taking into account byte alignment + int bytes = buffer.Length - putByte; + + // Read some data + bytes = stream.Read(buffer, putByte, bytes); + if (bytes <= 0) + throw new IOException("Failed to read from buffer."); + + // Now we have to handle partial byte cases + putByte += bytes; + availBits += bytes << 3; + return bytes; + } + + /// + /// Reset the bit stream. + /// + public void Reset() + { + getByte = 0; + getBit = 0; + putByte = 0; + availBits = 0; + } + + /// + /// Reset the read CRC-16 value. + /// + /// The initial CRC-16 value + public void ResetReadCrc16(UInt16 seed) + { + readCrc16 = seed; + } + + /// + /// Gets the the read CRC-16 value. + /// + public UInt16 ReadCrc16 + { + get + { + return readCrc16; + } + } + + /// + /// Test if the Bit Stream consumed bits is byte aligned. + /// + public bool IsConsumedByteAligned + { + get + { + return ((getBit & 0x7) == 0); + } + } + + /// + /// Gets the number of bits to read to align the byte. + /// + public int BitsLeftForByteAlignment + { + get + { + return 8 - (getBit & 7); + } + } + + /// + /// Gets the number of bytes left to read. + /// + public int InputBitsUnconsumed + { + get + { + return availBits >> 3; + } + } + + /// + /// Skips over bits in bit stream without updating CRC. + /// + /// Number of bits to skip + /// Thrown if error reading from input stream + public void SkipBitsNoCrc(int bits) + { + if (bits > 0) + { + int bitsToAlign = getBit & 7; + if (bitsToAlign != 0) + { + int bitsToTake = Math.Min(8 - bitsToAlign, bits); + ReadRawUInt32(bitsToTake); + bits -= bitsToTake; + } + + int bytesNeeded = bits / 8; + if (bytesNeeded > 0) + { + ReadByteBlockAlignedNoCrc(null, bytesNeeded); + bits %= 8; + } + + if (bits > 0) + { + ReadRawUInt32(bits); + } + } + } + + /// + /// Reads a single bit. + /// + /// The bit + /// Thrown if error reading input stream + public int ReadBit() + { + while (availBits <= 0) + { + ReadFromStream(); + } + + int val = ((buffer[getByte] & (0x80 >> getBit)) != 0) ? 1 : 0; + getBit++; + if (getBit == BitsPerByte) + { + readCrc16 = CRC16.Update(buffer[getByte], readCrc16); + getByte++; + getBit = 0; + } + availBits--; + totalBitsRead++; + return val; + } + + /// + /// Read a bit into an integer value. + /// The bits of the input integer are shifted left and the + /// bit is placed into bit 0. + /// + /// The integer to shift and add read bit + /// The updated integer value + /// Thrown if error reading input stream + public UInt32 ReadBitToUInt32(UInt32 val) + { + while (availBits <= 0) + { + ReadFromStream(); + } + + val <<= 1; + val |= ((buffer[getByte] & (0x80 >> getBit)) != 0) ? 1u : 0u; + getBit++; + if (getBit == BitsPerByte) + { + readCrc16 = CRC16.Update(buffer[getByte], readCrc16); + getByte++; + getBit = 0; + } + availBits--; + totalBitsRead++; + return val; + } + + /// + /// Peeks at the next bit and add it to the input integer. + /// The bits of the input integer are shifted left and the + /// bit is placed into bit 0. + /// + /// The input integer + /// The bit to peek at + /// The updated integer value + /// Thrown if error reading input stream + public UInt32 PeekBitToUInt32(UInt32 val, int bit) + { + while (availBits <= 0) + { + ReadFromStream(); + } + + val <<= 1; + if ((getBit + bit) >= BitsPerByte) + { + bit = (getBit + bit) % BitsPerByte; + val |= ((buffer[getByte + 1] & (0x80 >> bit)) != 0) ? 1u : 0u; + } + else + { + val |= ((buffer[getByte] & (0x80 >> (getBit + bit))) != 0) ? 1u : 0u; + } + return val; + } + + /// + /// Read a bit into a long value. + /// The bits of the input integer are shifted left and the + /// bit is placed into bit 0. + /// + /// The long to shift and add read bit + /// The updated long value + /// Thrown if error reading input stream + public UInt64 ReadBitToUInt64(UInt64 val) + { + while (availBits <= 0) + { + ReadFromStream(); + } + + val <<= 1; + val |= ((buffer[getByte] & (0x80 >> getBit)) != 0) ? 1u : 0u; + getBit++; + if (getBit == BitsPerByte) + { + readCrc16 = CRC16.Update(buffer[getByte], readCrc16); + getByte++; + getBit = 0; + } + availBits--; + totalBitsRead++; + return val; + } + + /// + /// Read bits into an unsigned integer. + /// + /// The number of bits to read + /// The bits as an unsigned integer + /// Thrown if error reading input stream + public UInt32 ReadRawUInt32(int bits) + { + UInt32 val = 0; + for (int i = 0; i < bits; i++) + { + val = ReadBitToUInt32(val); + } + return val; + } + + /// + /// Peek at bits into an unsigned integer without advancing the input stream. + /// + /// The number of bits to read + /// The bits as an unsigned integer + /// Thrown if error reading input stream + public UInt32 PeekRawUInt32(int bits) + { + UInt32 val = 0; + for (int i = 0; i < bits; i++) + { + val = PeekBitToUInt32(val, i); + } + return val; + } + + /// + /// Read bits into an unsigned long. + /// + /// The number of bits to read + /// The bits as an unsigned long + /// Thrown if error reading input stream + public UInt64 ReadRawUInt64(int bits) + { + UInt64 val = 0; + for (int i = 0; i < bits; i++) + { + val = ReadBitToUInt64(val); + } + return val; + } + + /// + /// Read bits into an unsigned little endian integer. + /// + /// The bits as an unsigned integer + /// Thrown if error reading input stream + public UInt32 ReadRawUInt32LittleEndian() + { + UInt32 val = ReadRawUInt32(8); + UInt32 a = ReadRawUInt32(8); + UInt32 b = ReadRawUInt32(8); + UInt32 c = ReadRawUInt32(8); + + return (val | (a << 8) | (b << 16) | (c << 24)); + } + + /// + /// Read a block of bytes (aligned) without updating the CRC value. + /// + /// The array to receive the bytes. If null, no bytes are returned + /// The number of bytes to read + /// Thrown if error reading input stream + public void ReadByteBlockAlignedNoCrc(byte[] val, int nvals) + { + int destlength = nvals; + while (nvals > 0) + { + int chunk = Math.Min(nvals, putByte - getByte); + if (chunk == 0) + { + ReadFromStream(); + } + else + { + if (val != null) + Array.Copy(buffer, getByte, val, destlength - nvals, chunk); + + nvals -= chunk; + getByte += chunk; + availBits -= (chunk << BitsPerByteLog2); + totalBitsRead += (chunk << BitsPerByteLog2); + } + } + } + + /// + /// Read and count the number of zero bits. + /// + /// The number of zero bits read + /// Thrown if error reading input stream + public int ReadUnaryUnsigned() + { + int val = 0; + int bit = ReadBit(); + while (bit == 0) + { + val++; + bit = ReadBit(); + } + return val; + } + + /// + /// Read a Rice Signal Block. + /// + /// The values to be returned + /// The starting position in the vals array + /// The number of values to return + /// The Rice parameter + /// Thrown if error reading input stream + public void ReadRiceSignedBlock(int[] vals, int pos, int nvals, int parameter) + { + int j, valI = 0; + int cbits = 0, uval = 0, msbs = 0, lsbsLeft = 0; + byte blurb, saveBlurb; + int state = 0; // 0 = getting unary MSBs, 1 = getting binary LSBs + int i = getByte; + + long startBits = getByte * 8 + getBit; + + if (nvals == 0) + return; + + // We unroll the main loop to take care of partially consumed blurbs here. + if (getBit > 0) + { + saveBlurb = blurb = buffer[i]; + cbits = getBit; + blurb <<= cbits; + while (true) + { + if (state == 0) + { + if (blurb != 0) + { + for (j = 0; (blurb & ByteTopBitOne) == 0; j++) + blurb <<= 1; + msbs += j; + + // dispose of the unary end bit + blurb <<= 1; + j++; + cbits += j; + uval = 0; + lsbsLeft = parameter; + state++; + //totalBitsRead += msbs; + if (cbits == BitsPerByte) + { + cbits = 0; + readCrc16 = CRC16.Update(saveBlurb, readCrc16); + break; + } + } + else + { + msbs += BitsPerByte - cbits; + cbits = 0; + readCrc16 = CRC16.Update(saveBlurb, readCrc16); + //totalBitsRead += msbs; + break; + } + } + else + { + int availableBits = BitsPerByte - cbits; + if (lsbsLeft >= availableBits) + { + uval <<= availableBits; + uval |= ((blurb & 0xff) >> cbits); + cbits = 0; + readCrc16 = CRC16.Update(saveBlurb, readCrc16); + //totalBitsRead += availableBits; + if (lsbsLeft == availableBits) + { + // compose the value + uval |= (msbs << parameter); + if ((uval & 1) != 0) + vals[pos + valI++] = -((int)(uval >> 1)) - 1; + else + vals[pos + valI++] = (int)(uval >> 1); + if (valI == nvals) + break; + msbs = 0; + state = 0; + } + lsbsLeft -= availableBits; + break; + } + else + { + uval <<= lsbsLeft; + uval |= ((blurb & 0xff) >> (BitsPerByte - lsbsLeft)); + blurb <<= lsbsLeft; + cbits += lsbsLeft; + //totalBitsRead += lsbsLeft; + // compose the value + uval |= (msbs << parameter); + if ((uval & 1) != 0) + vals[pos + valI++] = -((int)(uval >> 1)) - 1; + else + vals[pos + valI++] = (int)(uval >> 1); + if (valI == nvals) + { + // back up one if we exited the for loop because we + // read all nvals but the end came in the middle of + // a blurb + i--; + break; + } + msbs = 0; + state = 0; + } + } + } + i++; + getByte = i; + getBit = cbits; + //totalConsumedBits = (i << BITS_PER_BLURB_LOG2) | cbits; + //totalBitsRead += (BITS_PER_BLURB) | cbits; + } + + // Now that we are blurb-aligned the logic is slightly simpler + while (valI < nvals) + { + for (; i < putByte && valI < nvals; i++) + { + saveBlurb = blurb = buffer[i]; + cbits = 0; + while (true) + { + if (state == 0) + { + if (blurb != 0) + { + for (j = 0; (blurb & ByteTopBitOne) == 0; j++) + blurb <<= 1; + msbs += j; + // dispose of the unary end bit + blurb <<= 1; + j++; + cbits += j; + uval = 0; + lsbsLeft = parameter; + state++; + //totalBitsRead += msbs; + if (cbits == BitsPerByte) + { + cbits = 0; + readCrc16 = CRC16.Update(saveBlurb, readCrc16); + break; + } + } + else + { + msbs += BitsPerByte - cbits; + cbits = 0; + readCrc16 = CRC16.Update(saveBlurb, readCrc16); + //totalBitsRead += msbs; + break; + } + } + else + { + int availableBits = BitsPerByte - cbits; + if (lsbsLeft >= availableBits) + { + uval <<= availableBits; + uval |= ((blurb & 0xff) >> cbits); + cbits = 0; + readCrc16 = CRC16.Update(saveBlurb, readCrc16); + //totalBitsRead += availableBits; + if (lsbsLeft == availableBits) + { + // compose the value + uval |= (msbs << parameter); + if ((uval & 1) != 0) + vals[pos + valI++] = -((int)(uval >> 1)) - 1; + else + vals[pos + valI++] = (int)(uval >> 1); + if (valI == nvals) + break; + msbs = 0; + state = 0; + } + lsbsLeft -= availableBits; + break; + } + else + { + uval <<= lsbsLeft; + uval |= ((blurb & 0xff) >> (BitsPerByte - lsbsLeft)); + blurb <<= lsbsLeft; + cbits += lsbsLeft; + //totalBitsRead += lsbsLeft; + // compose the value + uval |= (msbs << parameter); + if ((uval & 1) != 0) + vals[pos + valI++] = -((int)(uval >> 1)) - 1; + else + vals[pos + valI++] = (int)(uval >> 1); + if (valI == nvals) + { + // back up one if we exited the for loop because + // we read all nvals but the end came in the + // middle of a blurb + i--; + break; + } + msbs = 0; + state = 0; + } + } + } + } + getByte = i; + getBit = cbits; + //totalConsumedBits = (i << BITS_PER_BLURB_LOG2) | cbits; + //totalBitsRead += (BITS_PER_BLURB) | cbits; + if (valI < nvals) + { + long endBits = getByte * 8 + getBit; + totalBitsRead += (int)(endBits - startBits); + availBits -= (int)(endBits - startBits); + ReadFromStream(); + // these must be zero because we can only get here if we got to + // the end of the buffer + i = 0; + startBits = getByte * 8 + getBit; + } + } + + long endBits2 = getByte * 8 + getBit; + totalBitsRead += (int)(endBits2 - startBits); + availBits -= (int)(endBits2 - startBits); + } + + /// + /// Read UTF8 integer. + /// On return, if *val == 0xffffffff then the utf-8 sequence was invalid, but + /// the return value will be true. + /// + /// The raw bytes read (output). If null, no bytes are returned + /// The raw bytes count. + /// The integer read + /// Thrown if error reading input stream + public UInt32 ReadUtf8UInt32(byte[] raw, out int rawCount) + { + UInt32 val, x; + UInt32 v = 0, i; + + x = ReadRawUInt32(8); + rawCount = 0; + + if (raw != null) + raw[rawCount++] = (byte)x; + + if ((x & 0x80) == 0) + { // 0xxxxxxx + v = x; + i = 0; + } + else if (((x & 0xC0) != 0) && ((x & 0x20) == 0)) + { // 110xxxxx + v = x & 0x1F; + i = 1; + } + else if (((x & 0xE0) != 0) && ((x & 0x10) == 0)) + { // 1110xxxx + v = x & 0x0F; + i = 2; + } + else if (((x & 0xF0) != 0) && ((x & 0x08) == 0)) + { // 11110xxx + v = x & 0x07; + i = 3; + } + else if (((x & 0xF8) != 0) && ((x & 0x04) == 0)) + { // 111110xx + v = x & 0x03; + i = 4; + } + else if (((x & 0xFC) != 0) && ((x & 0x02) == 0)) + { // 1111110x + v = x & 0x01; + i = 5; + } + else + { + val = 0xffffffff; + return val; + } + for (; i > 0; i--) + { + x = PeekRawUInt32(8); + if (((x & 0x80) == 0) || ((x & 0x40) != 0)) + { // 10xxxxxx + val = 0xffffffff; + return val; + } + x = ReadRawUInt32(8); + if (raw != null) + raw[rawCount++] = (byte)x; + + v <<= 6; + v |= (x & 0x3F); + } + val = v; + return val; + } + + /// + /// Read UTF8 long. + /// On return, if *val == 0xffffffffffffffff then the utf-8 sequence was invalid, but + /// the return value will be true. + /// + /// The raw bytes read (output). If null, no bytes are returned + /// The raw bytes count. + /// The long read + /// Thrown if error reading input stream + public UInt64 ReadUtf8UInt64(byte[] raw, out int rawCount) + { + UInt64 val, v = 0; + UInt32 x, i; + + x = ReadRawUInt32(8); + rawCount = 0; + + if (raw != null) + raw[rawCount++] = (byte)x; + + if (((x & 0x80) == 0)) + { // 0xxxxxxx + v = x; + i = 0; + } + else if (((x & 0xC0) != 0) && ((x & 0x20) == 0)) + { // 110xxxxx + v = x & 0x1F; + i = 1; + } + else if (((x & 0xE0) != 0) && ((x & 0x10) == 0)) + { // 1110xxxx + v = x & 0x0F; + i = 2; + } + else if (((x & 0xF0) != 0) && ((x & 0x08) == 0)) + { // 11110xxx + v = x & 0x07; + i = 3; + } + else if (((x & 0xF8) != 0) && ((x & 0x04) == 0)) + { // 111110xx + v = x & 0x03; + i = 4; + } + else if (((x & 0xFC) != 0) && ((x & 0x02) == 0)) + { // 1111110x + v = x & 0x01; + i = 5; + } + else if (((x & 0xFE) != 0) && ((x & 0x01) == 0)) + { // 11111110 + v = 0; + i = 6; + } + else + { + val = 0xffffffffffffffffL; + return val; + } + for (; i > 0; i--) + { + x = PeekRawUInt32(8); + if (((x & 0x80) == 0) || ((x & 0x40) != 0)) + { // 10xxxxxx + val = 0xffffffffffffffffL; + return val; + } + x = ReadRawUInt32(8); + + if (raw != null) + raw[rawCount++] = (byte)x; + v <<= 6; + v |= (x & 0x3F); + } + + val = v; + return val; + } + + /// + /// Gets total bytes read. + /// + public int TotalBytesRead + { + get + { + return (totalBitsRead + 7) / 8; + } + } + } +} diff --git a/Flac/Flac/Metadata/Application.cs b/Flac/Flac/Metadata/Application.cs new file mode 100644 index 0000000..38210de --- /dev/null +++ b/Flac/Flac/Metadata/Application.cs @@ -0,0 +1,39 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// Application Metadata block. + /// + public class Application : Metadata + { + private const int IdLength = 32; // bits + + private byte[] id = new byte[4]; + private byte[] data; + + /// + /// Creates a new instance of application class. + /// + /// The BitReader + /// Length of the record + /// True if this is the last Metadata block in the chain + /// Thrown if error reading from BitReader + public Application(BitReader read, int length, bool isLast) + : base(isLast) + { + read.ReadByteBlockAlignedNoCrc(id, IdLength / 8); + length -= IdLength / 8; + + if (length > 0) + { + data = new byte[length]; + read.ReadByteBlockAlignedNoCrc(data, length); + } + } + } +} diff --git a/Flac/Flac/Metadata/CueIndex.cs b/Flac/Flac/Metadata/CueIndex.cs new file mode 100644 index 0000000..f0e46c4 --- /dev/null +++ b/Flac/Flac/Metadata/CueIndex.cs @@ -0,0 +1,40 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// An entry into the cue track. + /// + public class CueIndex + { + private const int OffsetLength = 64; // bits + private const int NumberLength = 8; // bits + private const int ReservedLength = 3 * 8; // bits + + /// + /// Offset in samples, relative to the track offset, of the index point. + /// + public UInt64 Offset { get; protected set; } + + /// + /// The index point number. + /// + public byte Number { get; protected set; } + + /// + /// Creates a new instance of CueIndex. + /// + /// The BitReader + /// Thrown if error reading from BitReader. + public CueIndex(BitReader read) + { + Offset = read.ReadRawUInt64(OffsetLength); + Number = (byte)read.ReadRawUInt32(NumberLength); + read.SkipBitsNoCrc(ReservedLength); + } + } +} diff --git a/Flac/Flac/Metadata/CueSheet.cs b/Flac/Flac/Metadata/CueSheet.cs new file mode 100644 index 0000000..5c53935 --- /dev/null +++ b/Flac/Flac/Metadata/CueSheet.cs @@ -0,0 +1,137 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// CueSheet Metadata block. + /// + public class CueSheet : Metadata + { + #region Constants + private const int MediaCatalogNumberLength = 128 * 8; // bits + private const int LeadInLength = 64; // bits + private const int IsCdLength = 1; // bits + private const int ReservedLength = 7 + 258 * 8; // bits + private const int TrackCountLength = 8; // bits + #endregion + + /// + /// Media catalog number. + /// in ASCII printable characters 0x20-0x7e. In + /// general, the media catalog number may be 0 to 128 bytes long; any + /// unused characters should be right-padded with NUL characters. + /// + protected byte[] mediaCatalogNumber = new byte[129]; + + /// + /// The number of lead-in samples. + /// + protected UInt64 leadIn = 0; + + /// + /// True if CueSheet corresponds to a Compact Disc, else false + /// + protected bool isCD = false; + + /// + /// The number of tracks. + /// + protected UInt32 trackCount = 0; + + /// + /// NULL if num_tracks == 0, else pointer to array of tracks. + /// + protected CueTrack[] tracks; + + /// + /// Creates a new instance of CueSheet. + /// + /// The BitReader + /// Length of the record + /// True if this is the last Metadata block in the chain + /// Thrown if error reading from BitReader + public CueSheet(BitReader read, int length, bool isLast) + : base(isLast) + { + // Read fields + read.ReadByteBlockAlignedNoCrc(mediaCatalogNumber, MediaCatalogNumberLength / 8); + leadIn = read.ReadRawUInt64(LeadInLength); + isCD = (read.ReadRawUInt32(IsCdLength) != 0); + read.SkipBitsNoCrc(ReservedLength); + trackCount = read.ReadRawUInt32(TrackCountLength); + + // Read cue tracks + if (trackCount > 0) + { + tracks = new CueTrack[trackCount]; + for (int i = 0; i < trackCount; i++) + tracks[i] = new CueTrack(read); + } + } + + /// + /// Verifies the Cue Sheet. + /// + /// True for check CD subset + /// Thrown if invalid Cue Sheet + public void IsLegal(bool checkCdDaSubset) + { + + if (checkCdDaSubset) + { + if (leadIn < 2 * 44100) + throw new ValidationException("CD-DA cue sheet must have a lead-in length of at least 2 seconds"); + + if (leadIn % 588 != 0) + throw new ValidationException("CD-DA cue sheet lead-in length must be evenly divisible by 588 samples"); + } + + if (trackCount == 0) + throw new ValidationException("cue sheet must have at least one track (the lead-out)"); + + if (checkCdDaSubset && tracks[trackCount - 1].Number != 170) + throw new ValidationException("CD-DA cue sheet must have a lead-out track number 170 (0xAA)"); + + for (int i = 0; i < trackCount; i++) + { + if (tracks[i].Number == 0) + throw new ValidationException("cue sheet may not have a track number 0"); + + if (checkCdDaSubset) + { + if (!((tracks[i].Number >= 1 && tracks[i].Number <= 99) + || tracks[i].Number == 170)) + throw new ValidationException("CD-DA cue sheet track number must be 1-99 or 170"); + } + + if (checkCdDaSubset && tracks[i].Offset % 588 != 0) + throw new ValidationException("CD-DA cue sheet track offset must be evenly divisible by 588 samples"); + + if (i < trackCount - 1) + { + if (tracks[i].IndicesCount == 0) + throw new ValidationException("cue sheet track must have at least one index point"); + + if (tracks[i].Indices[0].Number > 1) + throw new ValidationException("cue sheet track's first index number must be 0 or 1"); + } + + for (int j = 0; j < tracks[i].IndicesCount; j++) + { + if (checkCdDaSubset && tracks[i].Indices[j].Offset % 588 != 0) + throw new ValidationException("CD-DA cue sheet track index offset must be evenly divisible by 588 samples"); + + if (j > 0) + { + if (tracks[i].Indices[j].Number != tracks[i].Indices[j - 1].Number + 1) + throw new ValidationException("cue sheet track index numbers must increase by 1"); + } + } + } + } + } +} diff --git a/Flac/Flac/Metadata/CueTrack.cs b/Flac/Flac/Metadata/CueTrack.cs new file mode 100644 index 0000000..eece785 --- /dev/null +++ b/Flac/Flac/Metadata/CueTrack.cs @@ -0,0 +1,85 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// An entry into the cue sheet. + /// + public class CueTrack + { + #region Constants + private const int OffsetLength = 64; + private const int NumberLength = 8; + private const int IsrcLength = 12 * 8; + private const int TypeLength = 1; + private const int PreEmphasisLength = 1; + private const int ReservedLength = 6 + 13 * 8; + private const int IndicesCountLength = 8; + #endregion + + /// + /// Track offset in samples, relative to the beginning of the FLAC audio stream. + /// + public UInt64 Offset { get; protected set; } + + /// + /// The track number. + /// + public byte Number { get; protected set; } + + /// + /// Track ISRC. This is a 12-digit alphanumeric code plus a trailing '\0' + /// + protected byte[] isrc = new byte[13]; + + /// + /// The track type: 0 for audio, 1 for non-audio. + /// + protected UInt32 type; + + /// + /// The pre-emphasis flag: 0 for no pre-emphasis, 1 for pre-emphasis. + /// + protected UInt32 preEmphasis; + + /// + /// The number of track index points. + /// + public byte IndicesCount { get; protected set; } + + /// + /// NULL if num_indices == 0, else pointer to array of index points. + /// + public CueIndex[] Indices { get; protected set; } + + /// + /// Creates new instance of CueTrack. + /// + /// The BitReader + /// Thrown if error reading from BitReader + public CueTrack(BitReader read) + { + // Read fields + Offset = read.ReadRawUInt64(OffsetLength); + Number = (byte)read.ReadRawUInt32(NumberLength); + read.ReadByteBlockAlignedNoCrc(isrc, IsrcLength / 8); + type = read.ReadRawUInt32(TypeLength); + preEmphasis = read.ReadRawUInt32(PreEmphasisLength); + read.SkipBitsNoCrc(ReservedLength); + IndicesCount = (byte)read.ReadRawUInt32(IndicesCountLength); + + // Read indices + if (IndicesCount > 0) + { + Indices = new CueIndex[IndicesCount]; + + for (int i = 0; i < IndicesCount; i++) + Indices[i] = new CueIndex(read); + } + } + } +} diff --git a/Flac/Flac/Metadata/Metadata.cs b/Flac/Flac/Metadata/Metadata.cs new file mode 100644 index 0000000..0a7d4db --- /dev/null +++ b/Flac/Flac/Metadata/Metadata.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// Metadata types. + /// + public enum MetadataType + { + StreamInfo = 0, + Padding = 1, + Application = 2, + SeekTable = 3, + VorbisComment = 4, + CueSheet = 5, + Picture = 6, + Undefined = 7 + } + + /// + /// Root class for all Metadata subclasses. + /// + public abstract class Metadata + { + /// + /// Metadata IsLast field length (bits). + /// + public const int StreamMetadataIsLastLength = 1; + + /// + /// Metadata type field length (bits). + /// + public const int StreamMetadataTypeLength = 7; + + /// + /// Metadata length field length (bits). + /// + public const int StreamMetadataLengthLength = 24; + + /// + /// Test if this is the last metadata block. + /// + public bool IsLast { get; protected set; } + + /// + /// Creates a new instance of Metadata class. + /// + /// True if this is the last metadata block. + public Metadata(bool isLast) + { + IsLast = isLast; + } + } +} diff --git a/Flac/Flac/Metadata/Padding.cs b/Flac/Flac/Metadata/Padding.cs new file mode 100644 index 0000000..6de7d8b --- /dev/null +++ b/Flac/Flac/Metadata/Padding.cs @@ -0,0 +1,30 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// Padding Metadata block. + /// + public class Padding : Metadata + { + private int length; + + /// + /// Creates a new instance of Padding. + /// + /// The BitReader + /// Length of the record + /// True if this is the last Metadata block in the chain + /// Thrown if error reading from BitReader + public Padding(BitReader read, int length, bool isLast) + : base(isLast) + { + this.length = length; + read.ReadByteBlockAlignedNoCrc(null, length); + } + } +} diff --git a/Flac/Flac/Metadata/Picture.cs b/Flac/Flac/Metadata/Picture.cs new file mode 100644 index 0000000..77d0fae --- /dev/null +++ b/Flac/Flac/Metadata/Picture.cs @@ -0,0 +1,95 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// Picture Metadata block. + /// + public class Picture : Metadata + { + #region Constants + private const int PictureTypeLength = 32; + private const int MimeTypeByteCountLength = 32; + private const int DescriptionByteCountLength = 32; + private const int PicturePixelWidthLength = 32; + private const int PicturePixelHeightLength = 32; + private const int PictureBitsPerPixelLength = 32; + private const int PictureColorCountLength = 32; + private const int PictureByteCountLength = 32; + #endregion + + private UInt32 pictureType; + private string mimeType; + private string description; + private int picPixelWidth; + private int picPixelHeight; + private int picBitsPerPixel; + private int picColorCount; + private byte[] image; + + /// + /// Creates a new instance of Picture. + /// + /// The BitReader + /// Length of the record + /// True if this is the last Metadata block in the chain + /// Thrown if error reading from BitReader + public Picture(BitReader read, int length, bool isLast) + : base(isLast) + { + int usedbits = 0; + + // Read picture type + pictureType = read.ReadRawUInt32(PictureTypeLength); + usedbits += PictureTypeLength; + + // Read mime type length + int mimeTypeByteCount = (int)read.ReadRawUInt32(MimeTypeByteCountLength); + usedbits += MimeTypeByteCountLength; + + // Read mime type + byte[] data = new byte[mimeTypeByteCount]; + read.ReadByteBlockAlignedNoCrc(data, mimeTypeByteCount); + usedbits += mimeTypeByteCount * 8; + mimeType = UTF8Encoding.UTF8.GetString(data, 0, mimeTypeByteCount); + + // Description string length + int descriptionByteCount = (int)read.ReadRawUInt32(DescriptionByteCountLength); + usedbits += DescriptionByteCountLength; + + // Description string + data = new byte[descriptionByteCount]; + read.ReadByteBlockAlignedNoCrc(data, descriptionByteCount); + usedbits += descriptionByteCount * 8; + description = UTF8Encoding.UTF8.GetString(data, 0, descriptionByteCount); + + // Picture stuff + picPixelWidth = (int)read.ReadRawUInt32(PicturePixelWidthLength); + picPixelHeight = (int)read.ReadRawUInt32(PicturePixelHeightLength); + usedbits += PicturePixelWidthLength + PicturePixelHeightLength; + + picBitsPerPixel = (int)read.ReadRawUInt32(PictureBitsPerPixelLength); + usedbits += PictureBitsPerPixelLength; + + picColorCount = (int)read.ReadRawUInt32(PictureColorCountLength); + usedbits += PictureColorCountLength; + + // Read data size + int picByteCount = (int)read.ReadRawUInt32(PictureByteCountLength); + usedbits += PictureByteCountLength; + + // Read data + image = new byte[picByteCount]; + read.ReadByteBlockAlignedNoCrc(image, picByteCount); + usedbits += picByteCount * 8; + + // Skip the rest of the block + length -= (usedbits / 8); + read.ReadByteBlockAlignedNoCrc(null, length); + } + } +} diff --git a/Flac/Flac/Metadata/SeekPoint.cs b/Flac/Flac/Metadata/SeekPoint.cs new file mode 100644 index 0000000..307e68d --- /dev/null +++ b/Flac/Flac/Metadata/SeekPoint.cs @@ -0,0 +1,62 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// An entry into the seek table. + /// + public class SeekPoint + { + #region Constants + private const int SampleNumberLength = 64; + private const int StreamOffsetLength = 64; + private const int FrameSamplesLength = 16; + #endregion + + /// + /// The sample number of the target frame. + /// + public long SampleNumber { get; private set; } + + /// + /// The offset, in bytes, of the target frame with + /// respect to beginning of the first frame. + /// + public long StreamOffset { get; set; } + + /// + /// The number of samples in the target frame. + /// + public int FrameSamples { get; private set; } + + /// + /// Creates a new instance of SeekPoint. + /// + /// The BitReader + /// Thrown if error reading from BitReader + public SeekPoint(BitReader read) + { + SampleNumber = (long)read.ReadRawUInt64(SampleNumberLength); + StreamOffset = (long)read.ReadRawUInt64(StreamOffsetLength); + FrameSamples = (int)read.ReadRawUInt32(FrameSamplesLength); + } + + /// + /// Creates a new instance of SeekPoint. + /// + /// The sample number of the target frame + /// The offset, in bytes, of the target frame with + /// respect to beginning of the first frame + /// The number of samples in the target frame + public SeekPoint(long sampleNumber, long streamOffset, int frameSamples) + { + SampleNumber = sampleNumber; + StreamOffset = streamOffset; + FrameSamples = frameSamples; + } + } +} diff --git a/Flac/Flac/Metadata/SeekTable.cs b/Flac/Flac/Metadata/SeekTable.cs new file mode 100644 index 0000000..32487cb --- /dev/null +++ b/Flac/Flac/Metadata/SeekTable.cs @@ -0,0 +1,117 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// SeekTable metadata block. + /// + public class SeekTable : Metadata, IEnumerable + { + #region Constants + private const int LengthBytes = 18; + #endregion + + private SeekPoint[] points; + + /// + /// Gets the seek point count. + /// + public int Length + { + get + { + if (points != null) + return points.Length; + + return 0; + } + } + + /// + /// Gets length in bytes (metadata block size). + /// + public int RawLength + { + get + { + if (points != null) + return points.Length * LengthBytes; + + return 0; + } + } + + /// + /// Gets iterator for this container + /// + /// The iterator + public IEnumerator GetEnumerator() + { + foreach (var i in points) + yield return i; + } + + /// + /// Gets iterator for this container + /// + /// The iterator + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return points.GetEnumerator(); + } + + /// + /// Gets a seek point. + /// + /// Index of seek point. + /// + public SeekPoint this[int index] + { + get + { + return points[index]; + } + } + + + /// + /// Creates a new instance of SeekTable. + /// + /// The BitReader + /// Length of the record + /// True if this is the last Metadata block in the chain + /// Thrown if error reading from BitReader + public SeekTable(BitReader read, int length, bool isLast) + : base(isLast) + { + // Calculate seek point count + int pointCount = length / LengthBytes; + + // Create seek points + points = new SeekPoint[pointCount]; + for (int i = 0; i < points.Length; i++) + points[i] = new SeekPoint(read); + + // Skip rest of bytes + length -= pointCount * LengthBytes; + if (length > 0) + read.ReadByteBlockAlignedNoCrc(null, length); + } + + /// + /// Creates a new instance of SeekTable. + /// + /// Seek points + /// True if this is the last Metadata block in the chain + public SeekTable(SeekPoint[] points, bool isLast) + : base(isLast) + { + this.points = points; + } + + } +} diff --git a/Flac/Flac/Metadata/StreamInfo.cs b/Flac/Flac/Metadata/StreamInfo.cs new file mode 100644 index 0000000..d1307a6 --- /dev/null +++ b/Flac/Flac/Metadata/StreamInfo.cs @@ -0,0 +1,147 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// StreamInfo Metadata block. + /// + public class StreamInfo : Metadata + { + #region Constants + private const int MinBlockSizeLength = 16; // bits + private const int MaxBlockSizeLength = 16; // bits + private const int MinFrameSizeLength = 24; // bits + private const int MaxFrameSizeLength = 24; // bits + private const int SampleRateLength = 20; // bits + private const int ChannelsLength = 3; // bits + private const int BitsPerSampleLength = 5; // bits + private const int TotalSamplesLength = 36; // bits + private const int Md5SumLength = 128; // bits + #endregion + + private byte[] md5Sum = new byte[16]; + + /// + /// Gets the MinBlockSize + /// + public UInt32 MinBlockSize { get; private set; } + + /// + /// Gets the MaxBlockSize + /// + public UInt32 MaxBlockSize { get; private set; } + + /// + /// Gets the MinFrameSize + /// + public UInt32 MinFrameSize { get; private set; } + + /// + /// Gets the MaxFrameSize + /// + public UInt32 MaxFrameSize { get; private set; } + + /// + /// Gets the sample rate. + /// + public UInt32 SampleRate { get; private set; } + + /// + /// Gets the number of channels. + /// + public UInt32 Channels { get; private set; } + + /// + /// Gets the number of bits per sample. + /// + public UInt32 BitsPerSample { get; private set; } + + /// + /// Gets or sets the total number of samples. + /// + public UInt64 TotalSamples { get; set; } + + /// + /// Creates a new instance of stream info class. + /// + /// The BitReader + /// Length of the record + /// True if this is the last Metadata block in the chain. + /// Thrown if error reading from InputBitStream + public StreamInfo(BitReader read, int length, bool isLast) + : base(isLast) + { + int usedBits = 0; + + MinBlockSize = read.ReadRawUInt32(MinBlockSizeLength); + usedBits += MinBlockSizeLength; + + MaxBlockSize = read.ReadRawUInt32(MaxBlockSizeLength); + usedBits += MaxBlockSizeLength; + + MinFrameSize = read.ReadRawUInt32(MinFrameSizeLength); + usedBits += MinFrameSizeLength; + + MaxFrameSize = read.ReadRawUInt32(MaxFrameSizeLength); + usedBits += MaxFrameSizeLength; + + SampleRate = read.ReadRawUInt32(SampleRateLength); + usedBits += SampleRateLength; + + Channels = read.ReadRawUInt32(ChannelsLength) + 1; + usedBits += ChannelsLength; + + BitsPerSample = read.ReadRawUInt32(BitsPerSampleLength) + 1; + usedBits += BitsPerSampleLength; + + TotalSamples = read.ReadRawUInt64(TotalSamplesLength); + usedBits += TotalSamplesLength; + + read.ReadByteBlockAlignedNoCrc(md5Sum, Md5SumLength / 8); + usedBits += Md5SumLength; + + // Skip the rest of the block + length -= (usedBits / 8); + read.ReadByteBlockAlignedNoCrc(null, length); + } + + /// + /// Gets the metadata block size. + /// + public int Length + { + get + { + int bits = MinBlockSizeLength + + MaxBlockSizeLength + + MinFrameSizeLength + + MaxFrameSizeLength + + SampleRateLength + + ChannelsLength + + BitsPerSampleLength + + TotalSamplesLength + + Md5SumLength; + + return (bits + 7) / 8; + } + } + + /// + /// Checks for compatible StreamInfo. + /// Checks if SampleRate, Channels, and BitsPerSample are equal + /// + /// The StreamInfo block to check + /// True if this and info are compatable + public bool Compatible (StreamInfo info) + { + if (SampleRate != info.SampleRate) return false; + if (Channels != info.Channels) return false; + if (BitsPerSample != info.BitsPerSample) return false; + return true; + } + } +} diff --git a/Flac/Flac/Metadata/Unknown.cs b/Flac/Flac/Metadata/Unknown.cs new file mode 100644 index 0000000..72d8e69 --- /dev/null +++ b/Flac/Flac/Metadata/Unknown.cs @@ -0,0 +1,33 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// Unknown metadata block + /// + public class Unknown : Metadata + { + private byte[] data; + + /// + /// Creates a new instance of Unknown. + /// + /// The BitReader + /// Length of the record + /// True if this is the last Metadata block in the chain + /// Thrown if error reading from BitReader + public Unknown(BitReader read, int length, bool isLast) + : base(isLast) + { + if (length > 0) + { + data = new byte[length]; + read.ReadByteBlockAlignedNoCrc(data, length); + } + } + } +} diff --git a/Flac/Flac/Metadata/ValidationException.cs b/Flac/Flac/Metadata/ValidationException.cs new file mode 100644 index 0000000..1007be3 --- /dev/null +++ b/Flac/Flac/Metadata/ValidationException.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// Metadata format validation exception + /// + public class ValidationException : Exception + { + /// + /// Creates a new instance of bad header exception. + /// + public ValidationException() + : base() + { + } + + /// + /// Creates a new instance of bad header exception with specified message. + /// + /// The message + public ValidationException(string message) + : base(message) + { + } + + /// + /// Creates a new instance of bad header exception with specified message + /// and a reference to the inner exception that is the cause of this exception. + /// + /// The message + /// The inner exception + public ValidationException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/Flac/Flac/Metadata/VorbisComment.cs b/Flac/Flac/Metadata/VorbisComment.cs new file mode 100644 index 0000000..f5ed7a3 --- /dev/null +++ b/Flac/Flac/Metadata/VorbisComment.cs @@ -0,0 +1,75 @@ +using Flac.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Metadata +{ + /// + /// Vorbis comment metadata block. + /// + public class VorbisComment : Metadata, IEnumerable> + { + private string vendor; + public Dictionary Comments { get; private set; } + + /// + /// Creates a new instance of VorbisComment. + /// + /// The BitReader + /// Length of the record + /// True if this is the last Metadata block in the chain + /// Thrown if error reading from BitReader + public VorbisComment(BitReader read, int length, bool isLast) + : base(isLast) + { + byte[] data; + + // Read vendor string + int vendorLength = (int)read.ReadRawUInt32LittleEndian(); + data = new byte[vendorLength]; + read.ReadByteBlockAlignedNoCrc(data, vendorLength); + + vendor = UTF8Encoding.UTF8.GetString(data, 0, vendorLength); + + // Read comments + int commentsCount = (int)read.ReadRawUInt32LittleEndian(); + Comments = new Dictionary(commentsCount); + + for (int i = 0; i < commentsCount; i++) + { + // Get comment bytes + int len = (int)read.ReadRawUInt32LittleEndian(); + data = new byte[len]; + read.ReadByteBlockAlignedNoCrc(data, len); + + // Obtain string + string str = UTF8Encoding.UTF8.GetString(data, 0, len); + int equal = str.IndexOf('='); + + // Add comment + if (equal > 0) + this.Comments.Add(str.Substring(0, equal), str.Substring(equal)); + } + } + + /// + /// Gets iterator for this container + /// + /// The iterator + public IEnumerator> GetEnumerator() + { + return Comments.GetEnumerator(); + } + + /// + /// Gets iterator for this container + /// + /// The iterator + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return ((System.Collections.IEnumerable)Comments).GetEnumerator(); + } + } +} diff --git a/Flac/Flac/Properties/AssemblyInfo.cs b/Flac/Flac/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7436e1d --- /dev/null +++ b/Flac/Flac/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Flac")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Flac")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("bf99ae12-eb9b-46c7-9ebe-1e736bc674f4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Flac/Flac/Util/CRC16.cs b/Flac/Flac/Util/CRC16.cs new file mode 100644 index 0000000..461a489 --- /dev/null +++ b/Flac/Flac/Util/CRC16.cs @@ -0,0 +1,319 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Util +{ + /// + /// Utility class to calculate 16-bit CRC. + /// + public static class CRC16 + { + #region private static UInt16[] Crc16Table + + // CRC-16, poly = x^16 + x^15 + x^2 + x^0, init = 0 + private static UInt16[] Crc16Table = + new UInt16[] { + (UInt16) 0x0000, + (UInt16) 0x8005, + (UInt16) 0x800f, + (UInt16) 0x000a, + (UInt16) 0x801b, + (UInt16) 0x001e, + (UInt16) 0x0014, + (UInt16) 0x8011, + (UInt16) 0x8033, + (UInt16) 0x0036, + (UInt16) 0x003c, + (UInt16) 0x8039, + (UInt16) 0x0028, + (UInt16) 0x802d, + (UInt16) 0x8027, + (UInt16) 0x0022, + (UInt16) 0x8063, + (UInt16) 0x0066, + (UInt16) 0x006c, + (UInt16) 0x8069, + (UInt16) 0x0078, + (UInt16) 0x807d, + (UInt16) 0x8077, + (UInt16) 0x0072, + (UInt16) 0x0050, + (UInt16) 0x8055, + (UInt16) 0x805f, + (UInt16) 0x005a, + (UInt16) 0x804b, + (UInt16) 0x004e, + (UInt16) 0x0044, + (UInt16) 0x8041, + (UInt16) 0x80c3, + (UInt16) 0x00c6, + (UInt16) 0x00cc, + (UInt16) 0x80c9, + (UInt16) 0x00d8, + (UInt16) 0x80dd, + (UInt16) 0x80d7, + (UInt16) 0x00d2, + (UInt16) 0x00f0, + (UInt16) 0x80f5, + (UInt16) 0x80ff, + (UInt16) 0x00fa, + (UInt16) 0x80eb, + (UInt16) 0x00ee, + (UInt16) 0x00e4, + (UInt16) 0x80e1, + (UInt16) 0x00a0, + (UInt16) 0x80a5, + (UInt16) 0x80af, + (UInt16) 0x00aa, + (UInt16) 0x80bb, + (UInt16) 0x00be, + (UInt16) 0x00b4, + (UInt16) 0x80b1, + (UInt16) 0x8093, + (UInt16) 0x0096, + (UInt16) 0x009c, + (UInt16) 0x8099, + (UInt16) 0x0088, + (UInt16) 0x808d, + (UInt16) 0x8087, + (UInt16) 0x0082, + (UInt16) 0x8183, + (UInt16) 0x0186, + (UInt16) 0x018c, + (UInt16) 0x8189, + (UInt16) 0x0198, + (UInt16) 0x819d, + (UInt16) 0x8197, + (UInt16) 0x0192, + (UInt16) 0x01b0, + (UInt16) 0x81b5, + (UInt16) 0x81bf, + (UInt16) 0x01ba, + (UInt16) 0x81ab, + (UInt16) 0x01ae, + (UInt16) 0x01a4, + (UInt16) 0x81a1, + (UInt16) 0x01e0, + (UInt16) 0x81e5, + (UInt16) 0x81ef, + (UInt16) 0x01ea, + (UInt16) 0x81fb, + (UInt16) 0x01fe, + (UInt16) 0x01f4, + (UInt16) 0x81f1, + (UInt16) 0x81d3, + (UInt16) 0x01d6, + (UInt16) 0x01dc, + (UInt16) 0x81d9, + (UInt16) 0x01c8, + (UInt16) 0x81cd, + (UInt16) 0x81c7, + (UInt16) 0x01c2, + (UInt16) 0x0140, + (UInt16) 0x8145, + (UInt16) 0x814f, + (UInt16) 0x014a, + (UInt16) 0x815b, + (UInt16) 0x015e, + (UInt16) 0x0154, + (UInt16) 0x8151, + (UInt16) 0x8173, + (UInt16) 0x0176, + (UInt16) 0x017c, + (UInt16) 0x8179, + (UInt16) 0x0168, + (UInt16) 0x816d, + (UInt16) 0x8167, + (UInt16) 0x0162, + (UInt16) 0x8123, + (UInt16) 0x0126, + (UInt16) 0x012c, + (UInt16) 0x8129, + (UInt16) 0x0138, + (UInt16) 0x813d, + (UInt16) 0x8137, + (UInt16) 0x0132, + (UInt16) 0x0110, + (UInt16) 0x8115, + (UInt16) 0x811f, + (UInt16) 0x011a, + (UInt16) 0x810b, + (UInt16) 0x010e, + (UInt16) 0x0104, + (UInt16) 0x8101, + (UInt16) 0x8303, + (UInt16) 0x0306, + (UInt16) 0x030c, + (UInt16) 0x8309, + (UInt16) 0x0318, + (UInt16) 0x831d, + (UInt16) 0x8317, + (UInt16) 0x0312, + (UInt16) 0x0330, + (UInt16) 0x8335, + (UInt16) 0x833f, + (UInt16) 0x033a, + (UInt16) 0x832b, + (UInt16) 0x032e, + (UInt16) 0x0324, + (UInt16) 0x8321, + (UInt16) 0x0360, + (UInt16) 0x8365, + (UInt16) 0x836f, + (UInt16) 0x036a, + (UInt16) 0x837b, + (UInt16) 0x037e, + (UInt16) 0x0374, + (UInt16) 0x8371, + (UInt16) 0x8353, + (UInt16) 0x0356, + (UInt16) 0x035c, + (UInt16) 0x8359, + (UInt16) 0x0348, + (UInt16) 0x834d, + (UInt16) 0x8347, + (UInt16) 0x0342, + (UInt16) 0x03c0, + (UInt16) 0x83c5, + (UInt16) 0x83cf, + (UInt16) 0x03ca, + (UInt16) 0x83db, + (UInt16) 0x03de, + (UInt16) 0x03d4, + (UInt16) 0x83d1, + (UInt16) 0x83f3, + (UInt16) 0x03f6, + (UInt16) 0x03fc, + (UInt16) 0x83f9, + (UInt16) 0x03e8, + (UInt16) 0x83ed, + (UInt16) 0x83e7, + (UInt16) 0x03e2, + (UInt16) 0x83a3, + (UInt16) 0x03a6, + (UInt16) 0x03ac, + (UInt16) 0x83a9, + (UInt16) 0x03b8, + (UInt16) 0x83bd, + (UInt16) 0x83b7, + (UInt16) 0x03b2, + (UInt16) 0x0390, + (UInt16) 0x8395, + (UInt16) 0x839f, + (UInt16) 0x039a, + (UInt16) 0x838b, + (UInt16) 0x038e, + (UInt16) 0x0384, + (UInt16) 0x8381, + (UInt16) 0x0280, + (UInt16) 0x8285, + (UInt16) 0x828f, + (UInt16) 0x028a, + (UInt16) 0x829b, + (UInt16) 0x029e, + (UInt16) 0x0294, + (UInt16) 0x8291, + (UInt16) 0x82b3, + (UInt16) 0x02b6, + (UInt16) 0x02bc, + (UInt16) 0x82b9, + (UInt16) 0x02a8, + (UInt16) 0x82ad, + (UInt16) 0x82a7, + (UInt16) 0x02a2, + (UInt16) 0x82e3, + (UInt16) 0x02e6, + (UInt16) 0x02ec, + (UInt16) 0x82e9, + (UInt16) 0x02f8, + (UInt16) 0x82fd, + (UInt16) 0x82f7, + (UInt16) 0x02f2, + (UInt16) 0x02d0, + (UInt16) 0x82d5, + (UInt16) 0x82df, + (UInt16) 0x02da, + (UInt16) 0x82cb, + (UInt16) 0x02ce, + (UInt16) 0x02c4, + (UInt16) 0x82c1, + (UInt16) 0x8243, + (UInt16) 0x0246, + (UInt16) 0x024c, + (UInt16) 0x8249, + (UInt16) 0x0258, + (UInt16) 0x825d, + (UInt16) 0x8257, + (UInt16) 0x0252, + (UInt16) 0x0270, + (UInt16) 0x8275, + (UInt16) 0x827f, + (UInt16) 0x027a, + (UInt16) 0x826b, + (UInt16) 0x026e, + (UInt16) 0x0264, + (UInt16) 0x8261, + (UInt16) 0x0220, + (UInt16) 0x8225, + (UInt16) 0x822f, + (UInt16) 0x022a, + (UInt16) 0x823b, + (UInt16) 0x023e, + (UInt16) 0x0234, + (UInt16) 0x8231, + (UInt16) 0x8213, + (UInt16) 0x0216, + (UInt16) 0x021c, + (UInt16) 0x8219, + (UInt16) 0x0208, + (UInt16) 0x820d, + (UInt16) 0x8207, + (UInt16) 0x0202 + }; + #endregion + + /// + /// Update the CRC with the byte data. + /// + /// The byte data + /// The starting CRC value + /// The updated CRC value + public static UInt16 Update(byte data, UInt16 crc) + { + crc = (UInt16)((crc << 8) ^ Crc16Table[((crc >> 8) ^ data) & 0xff]); + return crc; + } + + /// + /// Update the CRC with the byte array data. + /// + /// The byte array data + /// The byte array length + /// The starting CRC value + /// The updated CRC value + public static UInt16 UpdateBlock(byte[] data, int len, UInt16 crc) + { + for (int i = 0; i < len; i++) + crc = (UInt16)((crc << 8) ^ Crc16Table[(crc >> 8) ^ data[i]]); + return crc; + } + + /// + /// Calculate the CRC over a byte array. + /// + /// The byte data + /// The byte array length + /// The calculated CRC value + public static UInt16 Calculate(byte[] data, int len) + { + UInt16 crc = 0; + + for (int i = 0; i < len; i++) + crc = (UInt16)((crc << 8) ^ Crc16Table[(crc >> 8) ^ data[i]]); + + return crc; + } + } +} diff --git a/Flac/Flac/Util/CRC8.cs b/Flac/Flac/Util/CRC8.cs new file mode 100644 index 0000000..10f9586 --- /dev/null +++ b/Flac/Flac/Util/CRC8.cs @@ -0,0 +1,319 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Flac.Util +{ + /// + /// Utility class to calculate 8-bit CRC. + /// + public static class CRC8 + { + #region private static UInt16[] Crc16Table + + // CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 + private static byte[] Crc8Table = + new byte[] { + (byte) 0x00, + (byte) 0x07, + (byte) 0x0E, + (byte) 0x09, + (byte) 0x1C, + (byte) 0x1B, + (byte) 0x12, + (byte) 0x15, + (byte) 0x38, + (byte) 0x3F, + (byte) 0x36, + (byte) 0x31, + (byte) 0x24, + (byte) 0x23, + (byte) 0x2A, + (byte) 0x2D, + (byte) 0x70, + (byte) 0x77, + (byte) 0x7E, + (byte) 0x79, + (byte) 0x6C, + (byte) 0x6B, + (byte) 0x62, + (byte) 0x65, + (byte) 0x48, + (byte) 0x4F, + (byte) 0x46, + (byte) 0x41, + (byte) 0x54, + (byte) 0x53, + (byte) 0x5A, + (byte) 0x5D, + (byte) 0xE0, + (byte) 0xE7, + (byte) 0xEE, + (byte) 0xE9, + (byte) 0xFC, + (byte) 0xFB, + (byte) 0xF2, + (byte) 0xF5, + (byte) 0xD8, + (byte) 0xDF, + (byte) 0xD6, + (byte) 0xD1, + (byte) 0xC4, + (byte) 0xC3, + (byte) 0xCA, + (byte) 0xCD, + (byte) 0x90, + (byte) 0x97, + (byte) 0x9E, + (byte) 0x99, + (byte) 0x8C, + (byte) 0x8B, + (byte) 0x82, + (byte) 0x85, + (byte) 0xA8, + (byte) 0xAF, + (byte) 0xA6, + (byte) 0xA1, + (byte) 0xB4, + (byte) 0xB3, + (byte) 0xBA, + (byte) 0xBD, + (byte) 0xC7, + (byte) 0xC0, + (byte) 0xC9, + (byte) 0xCE, + (byte) 0xDB, + (byte) 0xDC, + (byte) 0xD5, + (byte) 0xD2, + (byte) 0xFF, + (byte) 0xF8, + (byte) 0xF1, + (byte) 0xF6, + (byte) 0xE3, + (byte) 0xE4, + (byte) 0xED, + (byte) 0xEA, + (byte) 0xB7, + (byte) 0xB0, + (byte) 0xB9, + (byte) 0xBE, + (byte) 0xAB, + (byte) 0xAC, + (byte) 0xA5, + (byte) 0xA2, + (byte) 0x8F, + (byte) 0x88, + (byte) 0x81, + (byte) 0x86, + (byte) 0x93, + (byte) 0x94, + (byte) 0x9D, + (byte) 0x9A, + (byte) 0x27, + (byte) 0x20, + (byte) 0x29, + (byte) 0x2E, + (byte) 0x3B, + (byte) 0x3C, + (byte) 0x35, + (byte) 0x32, + (byte) 0x1F, + (byte) 0x18, + (byte) 0x11, + (byte) 0x16, + (byte) 0x03, + (byte) 0x04, + (byte) 0x0D, + (byte) 0x0A, + (byte) 0x57, + (byte) 0x50, + (byte) 0x59, + (byte) 0x5E, + (byte) 0x4B, + (byte) 0x4C, + (byte) 0x45, + (byte) 0x42, + (byte) 0x6F, + (byte) 0x68, + (byte) 0x61, + (byte) 0x66, + (byte) 0x73, + (byte) 0x74, + (byte) 0x7D, + (byte) 0x7A, + (byte) 0x89, + (byte) 0x8E, + (byte) 0x87, + (byte) 0x80, + (byte) 0x95, + (byte) 0x92, + (byte) 0x9B, + (byte) 0x9C, + (byte) 0xB1, + (byte) 0xB6, + (byte) 0xBF, + (byte) 0xB8, + (byte) 0xAD, + (byte) 0xAA, + (byte) 0xA3, + (byte) 0xA4, + (byte) 0xF9, + (byte) 0xFE, + (byte) 0xF7, + (byte) 0xF0, + (byte) 0xE5, + (byte) 0xE2, + (byte) 0xEB, + (byte) 0xEC, + (byte) 0xC1, + (byte) 0xC6, + (byte) 0xCF, + (byte) 0xC8, + (byte) 0xDD, + (byte) 0xDA, + (byte) 0xD3, + (byte) 0xD4, + (byte) 0x69, + (byte) 0x6E, + (byte) 0x67, + (byte) 0x60, + (byte) 0x75, + (byte) 0x72, + (byte) 0x7B, + (byte) 0x7C, + (byte) 0x51, + (byte) 0x56, + (byte) 0x5F, + (byte) 0x58, + (byte) 0x4D, + (byte) 0x4A, + (byte) 0x43, + (byte) 0x44, + (byte) 0x19, + (byte) 0x1E, + (byte) 0x17, + (byte) 0x10, + (byte) 0x05, + (byte) 0x02, + (byte) 0x0B, + (byte) 0x0C, + (byte) 0x21, + (byte) 0x26, + (byte) 0x2F, + (byte) 0x28, + (byte) 0x3D, + (byte) 0x3A, + (byte) 0x33, + (byte) 0x34, + (byte) 0x4E, + (byte) 0x49, + (byte) 0x40, + (byte) 0x47, + (byte) 0x52, + (byte) 0x55, + (byte) 0x5C, + (byte) 0x5B, + (byte) 0x76, + (byte) 0x71, + (byte) 0x78, + (byte) 0x7F, + (byte) 0x6A, + (byte) 0x6D, + (byte) 0x64, + (byte) 0x63, + (byte) 0x3E, + (byte) 0x39, + (byte) 0x30, + (byte) 0x37, + (byte) 0x22, + (byte) 0x25, + (byte) 0x2C, + (byte) 0x2B, + (byte) 0x06, + (byte) 0x01, + (byte) 0x08, + (byte) 0x0F, + (byte) 0x1A, + (byte) 0x1D, + (byte) 0x14, + (byte) 0x13, + (byte) 0xAE, + (byte) 0xA9, + (byte) 0xA0, + (byte) 0xA7, + (byte) 0xB2, + (byte) 0xB5, + (byte) 0xBC, + (byte) 0xBB, + (byte) 0x96, + (byte) 0x91, + (byte) 0x98, + (byte) 0x9F, + (byte) 0x8A, + (byte) 0x8D, + (byte) 0x84, + (byte) 0x83, + (byte) 0xDE, + (byte) 0xD9, + (byte) 0xD0, + (byte) 0xD7, + (byte) 0xC2, + (byte) 0xC5, + (byte) 0xCC, + (byte) 0xCB, + (byte) 0xE6, + (byte) 0xE1, + (byte) 0xE8, + (byte) 0xEF, + (byte) 0xFA, + (byte) 0xFD, + (byte) 0xF4, + (byte) 0xF3 + }; + #endregion + + /// + /// Update the CRC with the byte data. + /// + /// The byte data + /// The starting CRC value + /// The updated CRC value + public static byte Update(byte data, byte crc) + { + return Crc8Table[crc ^ data]; + } + + /// + /// Update the CRC with the byte array data. + /// + /// The byte array data + /// The byte array length + /// The starting CRC value + /// The updated CRC value + public static byte UpdateBlock(byte[] data, int len, byte crc) + { + for (int i = 0; i < len; i++) + crc = Crc8Table[crc ^ data[i]]; + + return crc; + } + + /// + /// Calculate the CRC over a byte array. + /// + /// The byte data + /// The byte array length + /// The calculated CRC value + public static byte Calculate(byte[] data, int len) + { + byte crc = 0; + + for (int i = 0; i < len; i++) + crc = Crc8Table[crc ^ data[i]]; + + return crc; + } + } +} diff --git a/LICENSE b/LICENSE index d7f1051..4e77a35 100644 --- a/LICENSE +++ b/LICENSE @@ -1,339 +1,17 @@ -GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + libFLAC - Free Lossless Audio Codec library +Copyright (C) 2000,2001,2002,2003 Josh Coalson - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. - Preamble +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {description} - Copyright (C) {year} {fullname} - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - {signature of Ty Coon}, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the +Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. \ No newline at end of file