#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2009 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
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
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Nuclex.Audio.Formats.Flac {
#if ENABLE_PINVOKE_FLAC_DECODER
internal static class FrameMarshalHelper {
///
/// Marshals a frame from unmanaged memory into the equivalent structure
///
/// Handle to the unmanaged memory containing the frame
/// The marshaled frame structure
internal static Frame MarshalFrame(IntPtr frameHandle) {
Frame frame;
frame.Header = marshalFrameHeader(frameHandle);
// Discovered via Visual C++ 2008 Express SP1. The structure is 33 bytes without
// alignment. 40 is unexpected unless a 'char' following a 'long long' uses 8 bytes.
int offset = 40;
frame.Subframes = new Subframe[Constants.MaximumChannelCount];
for(int channelIndex = 0; channelIndex < Constants.MaximumChannelCount; ++channelIndex) {
frame.Subframes[channelIndex] = marshalSubframe(frameHandle, offset);
// Again, discovered via Visual C++ 2008 Express SP1. Without alignment, the entries
// should be 284 bytes apart each.
offset += 292;
}
// At a maximum channel count of 8, we should now be 40 + 2336 = 2376 bytes away from
// the address we started at
frame.Footer.Crc = (ushort)Marshal.ReadInt16(frameHandle, offset);
return frame;
}
///
/// Converts an integer into a member of the ChannelAssignment enumeration
///
///
/// Integer that will be converted into a member of the ChannelAssignment enumeration
///
///
/// The matching member of the ChannelAssignment enumeration for the specified channel
///
internal static ChannelAssignment ChannelAssignmentFromInt(int channelAssignment) {
switch(channelAssignment) {
case 0: { return ChannelAssignment.Independent; }
case 1: { return ChannelAssignment.LeftAndSide; }
case 2: { return ChannelAssignment.RightAndSide; }
case 3: { return ChannelAssignment.MidAndSide; }
default: { throw new ArgumentException("Invalid channel assignment"); }
}
}
///
/// Converts an integer into a member of the SubframeType enumeration
///
///
/// Integer that will be converted into a member of the SubframeType enumeration
///
///
/// The matching member of the SubframeType enumeration for the specified type
///
internal static SubframeType SubframeTypeFromInt(int subframeType) {
switch(subframeType) {
case 0: { return SubframeType.Constant; }
case 1: { return SubframeType.Verbatim; }
case 2: { return SubframeType.Fixed; }
case 3: { return SubframeType.Lpc; }
default: { throw new ArgumentException("Invalid subframe type"); }
}
}
///
/// Converts an integer into a member of the EntropyCodingMethodType enumeration
///
///
/// Integer that will be converted into a member of
/// the EntropyCodingMethodType enumeration
///
///
/// The matching member of the EntropyCodingMethodType enumeration for the
/// specified type
///
internal static EntropyCodingMethodType EntropyCodingMethodTypeFromInt(
int entropyMethodCodingType
) {
switch(entropyMethodCodingType) {
case 0: { return EntropyCodingMethodType.PartitionedRice; }
case 1: { return EntropyCodingMethodType.PartitionedRice2; }
default: { throw new ArgumentException("Invalid entropy coding method type"); }
}
}
///
/// Marshals the header of a frame from unmanaged memory into the equivalent structure
///
/// Handle to unmanaged memory containing the frame
/// The marshaled frame header
private static FrameHeader marshalFrameHeader(IntPtr frameHandle) {
FrameHeader header;
// The beginning of the structure is straightforward to marshal
header.Blocksize = Marshal.ReadInt32(frameHandle, 0);
header.SampleRate = Marshal.ReadInt32(frameHandle, 4);
header.Channels = Marshal.ReadInt32(frameHandle, 8);
header.ChannelAssignment = ChannelAssignmentFromInt(Marshal.ReadInt32(frameHandle, 12));
header.BitsPerSample = Marshal.ReadInt32(frameHandle, 16);
// Frame and sample number are actually a union. Which one is provided can be
// determined by looking at the number_type field. We resolve this union here in
// order to avoid explicit memory offsets in our managed structures
int numberType = Marshal.ReadInt32(frameHandle, 20);
switch(numberType) {
case 0: {
header.FrameNumber = Marshal.ReadInt32(frameHandle, 24);
header.SampleNumber = -1;
break;
}
case 1: {
header.FrameNumber = -1;
header.SampleNumber = Marshal.ReadInt64(frameHandle, 24);
break;
}
default: {
throw new ArgumentException("Invalid number type for the frame/sample number");
}
}
// Finally, an 8 bit CRC follows the frame header
header.Crc = Marshal.ReadByte(frameHandle, 32);
return header;
}
/// Marhals a FLAC subframe
/// Handle to unmanaged memory containing the frame
/// Offset at which to begin reading this subframe
/// The marshaled subframe
private static Subframe marshalSubframe(IntPtr frameHandle, int offset) {
Subframe subframe;
subframe.Type = SubframeTypeFromInt(Marshal.ReadInt32(frameHandle, offset));
switch(subframe.Type) {
case SubframeType.Constant: {
subframe.Constant = marshalConstantSubframe(frameHandle, offset + 4);
subframe.Fixed = null;
subframe.Lpc = null;
subframe.Verbatim = null;
break;
}
case SubframeType.Fixed: {
subframe.Constant = null;
subframe.Fixed = marshalFixedSubframe(frameHandle, offset + 4);
subframe.Lpc = null;
subframe.Verbatim = null;
break;
}
case SubframeType.Lpc: {
subframe.Constant = null;
subframe.Fixed = null;
subframe.Lpc = marshalLpcSubframe(frameHandle, offset + 4);
subframe.Verbatim = null;
break;
}
case SubframeType.Verbatim: {
subframe.Constant = null;
subframe.Fixed = null;
subframe.Lpc = null;
subframe.Verbatim = marshalVerbatimSubframe(frameHandle, offset + 4);
break;
}
default: {
throw new ArgumentException("Invalid subframe type");
}
}
subframe.WastedBits = Marshal.ReadInt32(frameHandle, offset + 288);
return subframe;
}
/// Marhals a FLAC constant subframe
/// Handle to unmanaged memory containing the frame
/// Offset at which to begin reading this subframe
/// The marshaled constant subframe
private static ConstantSubframe marshalConstantSubframe(IntPtr frameHandle, int offset) {
ConstantSubframe constantSubframe;
constantSubframe.Value = Marshal.ReadInt32(frameHandle, offset);
return constantSubframe;
}
/// Marshals a FLAC fixed subframe
/// Handle to unmanaged memory containing the frame
/// Offset at which to begin reading this subframe
/// The marshaled constant subframe
private static FixedSubframe marshalFixedSubframe(IntPtr frameHandle, int offset) {
FixedSubframe fixedSubframe;
fixedSubframe.EntropyCodingMethod = marshalEntropyCodingMethod(frameHandle, offset);
fixedSubframe.Order = Marshal.ReadInt32(frameHandle, offset + 12);
fixedSubframe.Warmup = new int[Constants.MaximumFixedOrder];
for(int warmupIndex = 0; warmupIndex < Constants.MaximumFixedOrder; ++warmupIndex) {
fixedSubframe.Warmup[warmupIndex] = Marshal.ReadInt32(
frameHandle, offset + warmupIndex * 4 + 16
);
}
fixedSubframe.Residual = null;
return fixedSubframe;
}
/// Marshals a FLAC LPC subframe
/// Handle to unmanaged memory containing the frame
/// Offset at which to begin reading this subframe
/// The marshaled LPC subframe
private static LpcSubframe marshalLpcSubframe(IntPtr frameHandle, int offset) {
LpcSubframe lpcSubframe = new LpcSubframe();
lpcSubframe.EntropyCodingMethod = marshalEntropyCodingMethod(frameHandle, offset);
lpcSubframe.Order = Marshal.ReadInt32(frameHandle, offset + 12);
lpcSubframe.QlpCoefficientPrecision = Marshal.ReadInt32(frameHandle, offset + 16);
lpcSubframe.QuantizationLevel = Marshal.ReadInt32(frameHandle, offset + 20);
offset += 24;
// Marshal the array of QLP Coefficients
lpcSubframe.QlpCoefficients = new int[Constants.MaximumLpcOrder];
for(
int index = 0; index < Constants.MaximumLpcOrder; ++index
) {
lpcSubframe.QlpCoefficients[index] = Marshal.ReadInt32(
frameHandle, offset + index * 4
);
}
offset += Constants.MaximumLpcOrder * 4;
// Marshal the Warmup values for the LPC decoder
lpcSubframe.Warmup = new int[Constants.MaximumLpcOrder];
for(
int index = 0; index < Constants.MaximumLpcOrder; ++index
) {
lpcSubframe.Warmup[index] = Marshal.ReadInt32(
frameHandle, offset + index * 4
);
}
offset += Constants.MaximumLpcOrder * 4;
lpcSubframe.Residual = null;
return lpcSubframe;
}
/// Marshals a FLAC verbatim subframe
/// Handle to unmanaged memory containing the frame
/// Offset at which to begin reading this subframe
/// The marshaled verbatim subframe
private static VerbatimSubframe marshalVerbatimSubframe(IntPtr frameHandle, int offset) {
VerbatimSubframe verbatimSubframe = new VerbatimSubframe();
return verbatimSubframe;
}
/// Marshals a FLAC entropy coding method
///
/// Handle to unmanaged memory containing the entropy coding method
///
///
/// Offset at which to begin reading this entropy coding method
///
/// The marshaled entropy coding method
private static EntropyCodingMethod marshalEntropyCodingMethod(
IntPtr frameHandle, int offset
) {
EntropyCodingMethod entropyCodingMethod = new EntropyCodingMethod();
entropyCodingMethod.Type = EntropyCodingMethodTypeFromInt(
Marshal.ReadInt32(frameHandle, offset)
);
switch(entropyCodingMethod.Type) {
// Rice Partition Coding scheme 1
case EntropyCodingMethodType.PartitionedRice: {
entropyCodingMethod.PartitionedRice.Order = Marshal.ReadInt32(
frameHandle, offset + 4
);
IntPtr contentsAddress = Marshal.ReadIntPtr(frameHandle, offset + 8);
entropyCodingMethod.PartitionedRice.Contents.Parameters = null;
entropyCodingMethod.PartitionedRice.Contents.RawBits = null;
entropyCodingMethod.PartitionedRice.Contents.CapacityByOrder = Marshal.ReadInt32(
contentsAddress, 8
);
break;
}
// Rice Partition Coding scheme 2
case EntropyCodingMethodType.PartitionedRice2: {
entropyCodingMethod.PartitionedRice.Order = Marshal.ReadInt32(
frameHandle, offset + 4
);
IntPtr contentsAddress = Marshal.ReadIntPtr(frameHandle, offset + 8);
entropyCodingMethod.PartitionedRice.Contents.Parameters = null;
entropyCodingMethod.PartitionedRice.Contents.RawBits = null;
entropyCodingMethod.PartitionedRice.Contents.CapacityByOrder = Marshal.ReadInt32(
contentsAddress, 8
);
break;
}
}
return entropyCodingMethod;
}
}
#endif // ENABLE_PINVOKE_FLAC_DECODER
} // namespace Nuclex.Audio.Formats.Flac