Implemented metadata, crc, part of frame and bitreader.

This commit is contained in:
2013-12-17 11:54:42 +02:00
parent 8c2f59c549
commit 1730d84b33
32 changed files with 3178 additions and 336 deletions

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1576AB6D-F8F4-42FC-BDC7-67D08648AEA6}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Flac.Test</RootNamespace>
<AssemblyName>Flac.Test</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -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")]

28
Flac/Flac.sln Normal file
View File

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

53
Flac/Flac/ChannelData.cs Normal file
View File

@ -0,0 +1,53 @@
using Flac.Frame;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac
{
/// <summary>
/// FLAC channel data.
/// This class holds the data for the channels in a FLAC frame.
/// </summary>
public class ChannelData
{
/// <summary>
/// The output signal.
/// </summary>
public int[] Output
{
get;
private set;
}
/// <summary>
/// The risidual signal.
/// </summary>
public int[] Residual
{
get;
private set;
}
/// <summary>
/// The Entropy signal.
/// </summary>
public EntropyPartitionedRiceContents PartitionedRiceContents
{
get;
private set;
}
/// <summary>
/// Creates a new instance of ChannelData
/// </summary>
/// <param name="size">The block size</param>
public ChannelData(int size)
{
Output = new int[size];
Residual = new int[size];
PartitionedRiceContents = new EntropyPartitionedRiceContents();
}
}
}

76
Flac/Flac/Flac.csproj Normal file
View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{ECB7B567-6A67-43F4-89CA-64E5D9F07B97}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Flac</RootNamespace>
<AssemblyName>Flac</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ChannelData.cs" />
<Compile Include="Frame\BadHeaderException.cs" />
<Compile Include="Frame\Channel.cs" />
<Compile Include="Frame\ChannelConstant.cs" />
<Compile Include="Frame\ChannelFixed.cs" />
<Compile Include="Frame\EntropyCodingMethod.cs" />
<Compile Include="Frame\EntropyPartitionedRice.cs" />
<Compile Include="Frame\EntropyPartitionedRiceContents.cs" />
<Compile Include="Frame\Header.cs" />
<Compile Include="IO\BitReader.cs" />
<Compile Include="Metadata\Application.cs" />
<Compile Include="Metadata\CueIndex.cs" />
<Compile Include="Metadata\CueSheet.cs" />
<Compile Include="Metadata\CueTrack.cs" />
<Compile Include="Metadata\Metadata.cs" />
<Compile Include="Metadata\Padding.cs" />
<Compile Include="Metadata\Picture.cs" />
<Compile Include="Metadata\SeekPoint.cs" />
<Compile Include="Metadata\SeekTable.cs" />
<Compile Include="Metadata\StreamInfo.cs" />
<Compile Include="Metadata\Unknown.cs" />
<Compile Include="Metadata\ValidationException.cs" />
<Compile Include="Metadata\VorbisComment.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Util\CRC16.cs" />
<Compile Include="Util\CRC8.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Flac.Frame
{
/// <summary>
/// Bad header exception
/// </summary>
public class BadHeaderException : IOException
{
/// <summary>
/// Creates a new instance of bad header exception.
/// </summary>
public BadHeaderException()
: base()
{
}
/// <summary>
/// Creates a new instance of bad header exception with specified message.
/// </summary>
/// <param name="message">The message</param>
public BadHeaderException(string message)
: base(message)
{
}
/// <summary>
/// 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.
/// </summary>
/// <param name="message">The message</param>
/// <param name="innerException">The inner exception</param>
public BadHeaderException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Frame
{
/// <summary>
/// Base class for FLAC subframe (channel) classes.
/// </summary>
public abstract class Channel
{
#region Constants
protected const int TypeLength = 2;
protected const int PartitionedRiceOrderLength = 4;
#endregion
protected enum EntropyCodingMethod
{
PartitionedRice = 0
}
/// <summary>
/// The FLAC Frame Header.
/// </summary>
protected Header Header { get; set; }
/// <summary>
/// The number of wasted bits in the frame.
/// </summary>
public int WastedBits { get; protected set; }
/// <summary>
/// Creates a new instance of channel.
/// </summary>
/// <param name="header">The FLAC Frame Header.</param>
/// <param name="wastedBits">The number of wasted bits in the frame.</param>
protected Channel(Header header, int wastedBits)
{
Header = header;
WastedBits = wastedBits;
}
}
}

View File

@ -0,0 +1,39 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Frame
{
/// <summary>
/// FLAC constant subframe (channel) data.
/// This class represents a FLAC subframe (channel) for a Constant data
/// </summary>
public class ChannelConstant : Channel
{
/// <summary>
/// The constant signal value.
/// </summary>
private int value;
/// <summary>
/// Creates a new instance of ChannelConstant
/// </summary>
/// <param name="read">The bit reader</param>
/// <param name="header">The FLAC Frame Header</param>
/// <param name="channelData">The decoded channel data (output)</param>
/// <param name="bps">The bits-per-second</param>
/// <param name="wastedBits">The bits wasted in the frame</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
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;
}
}
}

View File

@ -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
{
/// <summary>
/// Fixed FLAC subframe (channel).
/// </summary>
public class ChannelFixed : Channel
{
private const int MaxFixedOrder = 4;
/// <summary>
/// The residual coding method.
/// </summary>
private Frame.EntropyCodingMethod entropyCodingMethod;
/// <summary>
/// The polynomial order.
/// </summary>
private int order;
/// <summary>
/// Warmup samples to prime the predictor, length == order.
/// </summary>
private int[] warmup = new int[MaxFixedOrder];
/// <summary>
/// The residual signal, length == (blocksize minus order) samples.
/// </summary>
private int[] residual;
/// <summary>
/// Creates a new instance of ChannelConstant
/// </summary>
/// <param name="read">The bit reader</param>
/// <param name="header">The FLAC Frame Header</param>
/// <param name="channelData">The decoded channel data (output)</param>
/// <param name="bps">The bits-per-second</param>
/// <param name="wastedBits">The bits wasted in the frame</param>
/// <param name="order">The predicate order</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
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!");
}
}
}
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Frame
{
public interface EntropyCodingMethod
{
}
}

View File

@ -0,0 +1,86 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Frame
{
/// <summary>
/// This class holds the Entropy Partitioned Rice contents.
/// </summary>
public class EntropyPartitionedRice : EntropyCodingMethod
{
#region Constants
private const int ParameterLength = 4; // bits
private const int RawLength = 5; // bits
private const UInt32 EscapeParameter = 15;
#endregion
/// <summary>
/// The partition order, i.e. # of contexts = 2 ^ order.
/// </summary>
public int Order { get; set; }
/// <summary>
/// The context's Rice parameters and/or raw bits.
/// </summary>
public EntropyPartitionedRiceContents Contents
{
get;
set;
}
/// <summary>
/// Read compressed signal residual data.
/// </summary>
/// <param name="read">The bit reader</param>
/// <param name="predictorOrder">The predicate order</param>
/// <param name="partitionOrder">The partition order</param>
/// <param name="header">The FLAC Frame Header</param>
/// <param name="residual">The residual signal (output)</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
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);
}
}
}
}
}

View File

@ -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; }
/// <summary>
/// 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.
/// </summary>
private int capacityByOrder = 0;
/// <summary>
/// Ensure enough menory has been allocated.
/// </summary>
/// <param name="maxPartitionOrder">The maximum partition order</param>
public void EnsureSize(int maxPartitionOrder)
{
if (capacityByOrder >= maxPartitionOrder)
return;
Parameters = new int[(1 << maxPartitionOrder)];
RawBits = new int[(1 << maxPartitionOrder)];
capacityByOrder = maxPartitionOrder;
}
}
}

70
Flac/Flac/Frame/Header.cs Normal file
View File

@ -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
{
/// <summary>
/// Frame header class.
/// </summary>
public class Header
{
/// <summary>
/// Gets or sets the number of samples per subframe.
/// </summary>
public int BlockSize { get; set; }
/// <summary>
/// Gets or sets the sample rate in Hz.
/// </summary>
public int SampleRate { get; set; }
/// <summary>
/// Gets or sets the number of channels (== number of subframes).
/// </summary>
public int Channels { get; set; }
/// <summary>
/// Gets or sets the channel assignment for the frame.
/// </summary>
public int ChannelAssignment { get; set; }
/// <summary>
/// Gets or sets the sample resolution.
/// </summary>
public int BitsPerSample { get; set; }
/// <summary>
/// Gets or sets the frame number or sample number of first sample in frame.
/// Use the number_type value to determine which to use.
/// </summary>
public int FrameNumber { get; set; }
/// <summary>
/// The sample number for the first sample in the frame.
/// </summary>
public int SampleNumber { get; set; }
/// <summary>
/// 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.
/// </summary>
protected byte crc;
/// <summary>
///
/// </summary>
/// <param name="is"></param>
/// <param name="headerWarmup"></param>
/// <param name="streamInfo"></param>
public Header(BitReader @is, byte[] headerWarmup, StreamInfo streamInfo)
{
FrameNumber = -1;
SampleNumber = -1;
}
}
}

788
Flac/Flac/IO/BitReader.cs Normal file
View File

@ -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
/// <summary>
/// Creates a new instance of BitReader
/// </summary>
/// <param name="stream">Input data stream</param>
public BitReader(Stream stream)
{
buffer = new byte[DefaultCapacity];
this.stream = stream;
}
#endregion
/// <summary>
/// Reads data from stream.
/// </summary>
/// <returns>Number of bytes read</returns>
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;
}
/// <summary>
/// Reset the bit stream.
/// </summary>
public void Reset()
{
getByte = 0;
getBit = 0;
putByte = 0;
availBits = 0;
}
/// <summary>
/// Reset the read CRC-16 value.
/// </summary>
/// <param name="seed">The initial CRC-16 value</param>
public void ResetReadCrc16(UInt16 seed)
{
readCrc16 = seed;
}
/// <summary>
/// Gets the the read CRC-16 value.
/// </summary>
public UInt16 ReadCrc16
{
get
{
return readCrc16;
}
}
/// <summary>
/// Test if the Bit Stream consumed bits is byte aligned.
/// </summary>
public bool IsConsumedByteAligned
{
get
{
return ((getBit & 0x7) == 0);
}
}
/// <summary>
/// Gets the number of bits to read to align the byte.
/// </summary>
public int BitsLeftForByteAlignment
{
get
{
return 8 - (getBit & 7);
}
}
/// <summary>
/// Gets the number of bytes left to read.
/// </summary>
public int InputBitsUnconsumed
{
get
{
return availBits >> 3;
}
}
/// <summary>
/// Skips over bits in bit stream without updating CRC.
/// </summary>
/// <param name="bits">Number of bits to skip</param>
/// <exception cref="IOException">Thrown if error reading from input stream</exception>
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);
}
}
}
/// <summary>
/// Reads a single bit.
/// </summary>
/// <returns>The bit</returns>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
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;
}
/// <summary>
/// Read a bit into an integer value.
/// The bits of the input integer are shifted left and the
/// bit is placed into bit 0.
/// </summary>
/// <param name="val">The integer to shift and add read bit</param>
/// <returns>The updated integer value</returns>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="val">The input integer</param>
/// <param name="bit">The bit to peek at</param>
/// <returns>The updated integer value</returns>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
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;
}
/// <summary>
/// Read a bit into a long value.
/// The bits of the input integer are shifted left and the
/// bit is placed into bit 0.
/// </summary>
/// <param name="val">The long to shift and add read bit</param>
/// <returns>The updated long value</returns>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
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;
}
/// <summary>
/// Read bits into an unsigned integer.
/// </summary>
/// <param name="bits">The number of bits to read</param>
/// <returns>The bits as an unsigned integer</returns>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
public UInt32 ReadRawUInt32(int bits)
{
UInt32 val = 0;
for (int i = 0; i < bits; i++)
{
val = ReadBitToUInt32(val);
}
return val;
}
/// <summary>
/// Peek at bits into an unsigned integer without advancing the input stream.
/// </summary>
/// <param name="bits">The number of bits to read</param>
/// <returns>The bits as an unsigned integer</returns>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
public UInt32 PeekRawUInt32(int bits)
{
UInt32 val = 0;
for (int i = 0; i < bits; i++)
{
val = PeekBitToUInt32(val, i);
}
return val;
}
/// <summary>
/// Read bits into an unsigned long.
/// </summary>
/// <param name="bits">The number of bits to read</param>
/// <returns>The bits as an unsigned long</returns>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
public UInt64 ReadRawUInt64(int bits)
{
UInt64 val = 0;
for (int i = 0; i < bits; i++)
{
val = ReadBitToUInt64(val);
}
return val;
}
/// <summary>
/// Read bits into an unsigned little endian integer.
/// </summary>
/// <returns>The bits as an unsigned integer</returns>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
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));
}
/// <summary>
/// Read a block of bytes (aligned) without updating the CRC value.
/// </summary>
/// <param name="val">The array to receive the bytes. If null, no bytes are returned</param>
/// <param name="nvals">The number of bytes to read</param>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
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);
}
}
}
/// <summary>
/// Read and count the number of zero bits.
/// </summary>
/// <returns>The number of zero bits read</returns>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
public int ReadUnaryUnsigned()
{
int val = 0;
int bit = ReadBit();
while (bit == 0)
{
val++;
bit = ReadBit();
}
return val;
}
/// <summary>
/// Read a Rice Signal Block.
/// </summary>
/// <param name="vals">The values to be returned</param>
/// <param name="pos">The starting position in the vals array</param>
/// <param name="nvals">The number of values to return</param>
/// <param name="parameter">The Rice parameter</param>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
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);
}
/// <summary>
/// Read UTF8 integer.
/// On return, if *val == 0xffffffff then the utf-8 sequence was invalid, but
/// the return value will be true.
/// </summary>
/// <param name="raw">The raw bytes read (output). If null, no bytes are returned</param>
/// <param name="rawCount">The raw bytes count.</param>
/// <returns>The integer read</returns>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
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;
}
/// <summary>
/// Read UTF8 long.
/// On return, if *val == 0xffffffffffffffff then the utf-8 sequence was invalid, but
/// the return value will be true.
/// </summary>
/// <param name="raw">The raw bytes read (output). If null, no bytes are returned</param>
/// <param name="rawCount">The raw bytes count.</param>
/// <returns>The long read</returns>
/// <exception cref="IOException">Thrown if error reading input stream</exception>
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;
}
/// <summary>
/// Gets total bytes read.
/// </summary>
public int TotalBytesRead
{
get
{
return (totalBitsRead + 7) / 8;
}
}
}
}

View File

@ -0,0 +1,39 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// Application Metadata block.
/// </summary>
public class Application : Metadata
{
private const int IdLength = 32; // bits
private byte[] id = new byte[4];
private byte[] data;
/// <summary>
/// Creates a new instance of application class.
/// </summary>
/// <param name="read">The BitReader</param>
/// <param name="length">Length of the record</param>
/// <param name="isLast">True if this is the last Metadata block in the chain</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
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);
}
}
}
}

View File

@ -0,0 +1,40 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// An entry into the cue track.
/// </summary>
public class CueIndex
{
private const int OffsetLength = 64; // bits
private const int NumberLength = 8; // bits
private const int ReservedLength = 3 * 8; // bits
/// <summary>
/// Offset in samples, relative to the track offset, of the index point.
/// </summary>
public UInt64 Offset { get; protected set; }
/// <summary>
/// The index point number.
/// </summary>
public byte Number { get; protected set; }
/// <summary>
/// Creates a new instance of CueIndex.
/// </summary>
/// <param name="read">The BitReader</param>
/// <exception cref="IOException">Thrown if error reading from BitReader.</exception>
public CueIndex(BitReader read)
{
Offset = read.ReadRawUInt64(OffsetLength);
Number = (byte)read.ReadRawUInt32(NumberLength);
read.SkipBitsNoCrc(ReservedLength);
}
}
}

View File

@ -0,0 +1,137 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// CueSheet Metadata block.
/// </summary>
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
/// <summary>
/// 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.
/// </summary>
protected byte[] mediaCatalogNumber = new byte[129];
/// <summary>
/// The number of lead-in samples.
/// </summary>
protected UInt64 leadIn = 0;
/// <summary>
/// True if CueSheet corresponds to a Compact Disc, else false
/// </summary>
protected bool isCD = false;
/// <summary>
/// The number of tracks.
/// </summary>
protected UInt32 trackCount = 0;
/// <summary>
/// NULL if num_tracks == 0, else pointer to array of tracks.
/// </summary>
protected CueTrack[] tracks;
/// <summary>
/// Creates a new instance of CueSheet.
/// </summary>
/// <param name="read">The BitReader</param>
/// <param name="length">Length of the record</param>
/// <param name="isLast">True if this is the last Metadata block in the chain</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
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);
}
}
/// <summary>
/// Verifies the Cue Sheet.
/// </summary>
/// <param name="checkCdDaSubset">True for check CD subset</param>
/// <exception cref="ValidationException">Thrown if invalid Cue Sheet</exception>
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");
}
}
}
}
}
}

View File

@ -0,0 +1,85 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// An entry into the cue sheet.
/// </summary>
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
/// <summary>
/// Track offset in samples, relative to the beginning of the FLAC audio stream.
/// </summary>
public UInt64 Offset { get; protected set; }
/// <summary>
/// The track number.
/// </summary>
public byte Number { get; protected set; }
/// <summary>
/// Track ISRC. This is a 12-digit alphanumeric code plus a trailing '\0'
/// </summary>
protected byte[] isrc = new byte[13];
/// <summary>
/// The track type: 0 for audio, 1 for non-audio.
/// </summary>
protected UInt32 type;
/// <summary>
/// The pre-emphasis flag: 0 for no pre-emphasis, 1 for pre-emphasis.
/// </summary>
protected UInt32 preEmphasis;
/// <summary>
/// The number of track index points.
/// </summary>
public byte IndicesCount { get; protected set; }
/// <summary>
/// NULL if num_indices == 0, else pointer to array of index points.
/// </summary>
public CueIndex[] Indices { get; protected set; }
/// <summary>
/// Creates new instance of CueTrack.
/// </summary>
/// <param name="read">The BitReader</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
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);
}
}
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// Metadata types.
/// </summary>
public enum MetadataType
{
StreamInfo = 0,
Padding = 1,
Application = 2,
SeekTable = 3,
VorbisComment = 4,
CueSheet = 5,
Picture = 6,
Undefined = 7
}
/// <summary>
/// Root class for all Metadata subclasses.
/// </summary>
public abstract class Metadata
{
/// <summary>
/// Metadata IsLast field length (bits).
/// </summary>
public const int StreamMetadataIsLastLength = 1;
/// <summary>
/// Metadata type field length (bits).
/// </summary>
public const int StreamMetadataTypeLength = 7;
/// <summary>
/// Metadata length field length (bits).
/// </summary>
public const int StreamMetadataLengthLength = 24;
/// <summary>
/// Test if this is the last metadata block.
/// </summary>
public bool IsLast { get; protected set; }
/// <summary>
/// Creates a new instance of Metadata class.
/// </summary>
/// <param name="isLast">True if this is the last metadata block.</param>
public Metadata(bool isLast)
{
IsLast = isLast;
}
}
}

View File

@ -0,0 +1,30 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// Padding Metadata block.
/// </summary>
public class Padding : Metadata
{
private int length;
/// <summary>
/// Creates a new instance of Padding.
/// </summary>
/// <param name="read">The BitReader</param>
/// <param name="length">Length of the record</param>
/// <param name="isLast">True if this is the last Metadata block in the chain</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
public Padding(BitReader read, int length, bool isLast)
: base(isLast)
{
this.length = length;
read.ReadByteBlockAlignedNoCrc(null, length);
}
}
}

View File

@ -0,0 +1,95 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// Picture Metadata block.
/// </summary>
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;
/// <summary>
/// Creates a new instance of Picture.
/// </summary>
/// <param name="read">The BitReader</param>
/// <param name="length">Length of the record</param>
/// <param name="isLast">True if this is the last Metadata block in the chain</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
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);
}
}
}

View File

@ -0,0 +1,62 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// An entry into the seek table.
/// </summary>
public class SeekPoint
{
#region Constants
private const int SampleNumberLength = 64;
private const int StreamOffsetLength = 64;
private const int FrameSamplesLength = 16;
#endregion
/// <summary>
/// The sample number of the target frame.
/// </summary>
public long SampleNumber { get; private set; }
/// <summary>
/// The offset, in bytes, of the target frame with
/// respect to beginning of the first frame.
/// </summary>
public long StreamOffset { get; set; }
/// <summary>
/// The number of samples in the target frame.
/// </summary>
public int FrameSamples { get; private set; }
/// <summary>
/// Creates a new instance of SeekPoint.
/// </summary>
/// <param name="read">The BitReader</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
public SeekPoint(BitReader read)
{
SampleNumber = (long)read.ReadRawUInt64(SampleNumberLength);
StreamOffset = (long)read.ReadRawUInt64(StreamOffsetLength);
FrameSamples = (int)read.ReadRawUInt32(FrameSamplesLength);
}
/// <summary>
/// Creates a new instance of SeekPoint.
/// </summary>
/// <param name="sampleNumber">The sample number of the target frame</param>
/// <param name="streamOffset">The offset, in bytes, of the target frame with
/// respect to beginning of the first frame</param>
/// <param name="frameSamples">The number of samples in the target frame</param>
public SeekPoint(long sampleNumber, long streamOffset, int frameSamples)
{
SampleNumber = sampleNumber;
StreamOffset = streamOffset;
FrameSamples = frameSamples;
}
}
}

View File

@ -0,0 +1,117 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// SeekTable metadata block.
/// </summary>
public class SeekTable : Metadata, IEnumerable<SeekPoint>
{
#region Constants
private const int LengthBytes = 18;
#endregion
private SeekPoint[] points;
/// <summary>
/// Gets the seek point count.
/// </summary>
public int Length
{
get
{
if (points != null)
return points.Length;
return 0;
}
}
/// <summary>
/// Gets length in bytes (metadata block size).
/// </summary>
public int RawLength
{
get
{
if (points != null)
return points.Length * LengthBytes;
return 0;
}
}
/// <summary>
/// Gets iterator for this container
/// </summary>
/// <returns>The iterator</returns>
public IEnumerator<SeekPoint> GetEnumerator()
{
foreach (var i in points)
yield return i;
}
/// <summary>
/// Gets iterator for this container
/// </summary>
/// <returns>The iterator</returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return points.GetEnumerator();
}
/// <summary>
/// Gets a seek point.
/// </summary>
/// <param name="index">Index of seek point.</param>
/// <returns></returns>
public SeekPoint this[int index]
{
get
{
return points[index];
}
}
/// <summary>
/// Creates a new instance of SeekTable.
/// </summary>
/// <param name="read">The BitReader</param>
/// <param name="length">Length of the record</param>
/// <param name="isLast">True if this is the last Metadata block in the chain</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
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);
}
/// <summary>
/// Creates a new instance of SeekTable.
/// </summary>
/// <param name="points">Seek points</param>
/// <param name="isLast">True if this is the last Metadata block in the chain</param>
public SeekTable(SeekPoint[] points, bool isLast)
: base(isLast)
{
this.points = points;
}
}
}

View File

@ -0,0 +1,147 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// StreamInfo Metadata block.
/// </summary>
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];
/// <summary>
/// Gets the MinBlockSize
/// </summary>
public UInt32 MinBlockSize { get; private set; }
/// <summary>
/// Gets the MaxBlockSize
/// </summary>
public UInt32 MaxBlockSize { get; private set; }
/// <summary>
/// Gets the MinFrameSize
/// </summary>
public UInt32 MinFrameSize { get; private set; }
/// <summary>
/// Gets the MaxFrameSize
/// </summary>
public UInt32 MaxFrameSize { get; private set; }
/// <summary>
/// Gets the sample rate.
/// </summary>
public UInt32 SampleRate { get; private set; }
/// <summary>
/// Gets the number of channels.
/// </summary>
public UInt32 Channels { get; private set; }
/// <summary>
/// Gets the number of bits per sample.
/// </summary>
public UInt32 BitsPerSample { get; private set; }
/// <summary>
/// Gets or sets the total number of samples.
/// </summary>
public UInt64 TotalSamples { get; set; }
/// <summary>
/// Creates a new instance of stream info class.
/// </summary>
/// <param name="read">The BitReader</param>
/// <param name="length">Length of the record</param>
/// <param name="isLast">True if this is the last Metadata block in the chain.</param>
/// <exception cref="IOException">Thrown if error reading from InputBitStream</exception>
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);
}
/// <summary>
/// Gets the metadata block size.
/// </summary>
public int Length
{
get
{
int bits = MinBlockSizeLength
+ MaxBlockSizeLength
+ MinFrameSizeLength
+ MaxFrameSizeLength
+ SampleRateLength
+ ChannelsLength
+ BitsPerSampleLength
+ TotalSamplesLength
+ Md5SumLength;
return (bits + 7) / 8;
}
}
/// <summary>
/// Checks for compatible StreamInfo.
/// Checks if SampleRate, Channels, and BitsPerSample are equal
/// </summary>
/// <param name="info">The StreamInfo block to check</param>
/// <returns>True if this and info are compatable</returns>
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;
}
}
}

View File

@ -0,0 +1,33 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// Unknown metadata block
/// </summary>
public class Unknown : Metadata
{
private byte[] data;
/// <summary>
/// Creates a new instance of Unknown.
/// </summary>
/// <param name="read">The BitReader</param>
/// <param name="length">Length of the record</param>
/// <param name="isLast">True if this is the last Metadata block in the chain</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
public Unknown(BitReader read, int length, bool isLast)
: base(isLast)
{
if (length > 0)
{
data = new byte[length];
read.ReadByteBlockAlignedNoCrc(data, length);
}
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// Metadata format validation exception
/// </summary>
public class ValidationException : Exception
{
/// <summary>
/// Creates a new instance of bad header exception.
/// </summary>
public ValidationException()
: base()
{
}
/// <summary>
/// Creates a new instance of bad header exception with specified message.
/// </summary>
/// <param name="message">The message</param>
public ValidationException(string message)
: base(message)
{
}
/// <summary>
/// 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.
/// </summary>
/// <param name="message">The message</param>
/// <param name="innerException">The inner exception</param>
public ValidationException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,75 @@
using Flac.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Metadata
{
/// <summary>
/// Vorbis comment metadata block.
/// </summary>
public class VorbisComment : Metadata, IEnumerable<KeyValuePair<string, string>>
{
private string vendor;
public Dictionary<string, string> Comments { get; private set; }
/// <summary>
/// Creates a new instance of VorbisComment.
/// </summary>
/// <param name="read">The BitReader</param>
/// <param name="length">Length of the record</param>
/// <param name="isLast">True if this is the last Metadata block in the chain</param>
/// <exception cref="IOException">Thrown if error reading from BitReader</exception>
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<string, string>(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));
}
}
/// <summary>
/// Gets iterator for this container
/// </summary>
/// <returns>The iterator</returns>
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return Comments.GetEnumerator();
}
/// <summary>
/// Gets iterator for this container
/// </summary>
/// <returns>The iterator</returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.IEnumerable)Comments).GetEnumerator();
}
}
}

View File

@ -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")]

319
Flac/Flac/Util/CRC16.cs Normal file
View File

@ -0,0 +1,319 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Util
{
/// <summary>
/// Utility class to calculate 16-bit CRC.
/// </summary>
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
/// <summary>
/// Update the CRC with the byte data.
/// </summary>
/// <param name="data">The byte data</param>
/// <param name="crc">The starting CRC value</param>
/// <returns>The updated CRC value</returns>
public static UInt16 Update(byte data, UInt16 crc)
{
crc = (UInt16)((crc << 8) ^ Crc16Table[((crc >> 8) ^ data) & 0xff]);
return crc;
}
/// <summary>
/// Update the CRC with the byte array data.
/// </summary>
/// <param name="data">The byte array data</param>
/// <param name="len">The byte array length</param>
/// <param name="crc">The starting CRC value</param>
/// <returns>The updated CRC value</returns>
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;
}
/// <summary>
/// Calculate the CRC over a byte array.
/// </summary>
/// <param name="data">The byte data</param>
/// <param name="len">The byte array length</param>
/// <returns>The calculated CRC value</returns>
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;
}
}
}

319
Flac/Flac/Util/CRC8.cs Normal file
View File

@ -0,0 +1,319 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flac.Util
{
/// <summary>
/// Utility class to calculate 8-bit CRC.
/// </summary>
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
/// <summary>
/// Update the CRC with the byte data.
/// </summary>
/// <param name="data">The byte data</param>
/// <param name="crc">The starting CRC value</param>
/// <returns>The updated CRC value</returns>
public static byte Update(byte data, byte crc)
{
return Crc8Table[crc ^ data];
}
/// <summary>
/// Update the CRC with the byte array data.
/// </summary>
/// <param name="data">The byte array data</param>
/// <param name="len">The byte array length</param>
/// <param name="crc">The starting CRC value</param>
/// <returns>The updated CRC value</returns>
public static byte UpdateBlock(byte[] data, int len, byte crc)
{
for (int i = 0; i < len; i++)
crc = Crc8Table[crc ^ data[i]];
return crc;
}
/// <summary>
/// Calculate the CRC over a byte array.
/// </summary>
/// <param name="data">The byte data</param>
/// <param name="len">The byte array length</param>
/// <returns>The calculated CRC value</returns>
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;
}
}
}