Refactored the LAME interop.

This commit is contained in:
karamanolev
2011-10-24 08:42:30 +00:00
parent 6f84aadc2e
commit d7f588e19c
22 changed files with 970 additions and 899 deletions

View File

@@ -82,7 +82,7 @@ namespace CUETools.Codecs.Icecast
{ {
encoder = new CUETools.Codecs.LAME.LAMEEncoderCBR("", reqStream, AudioPCMConfig.RedBook); encoder = new CUETools.Codecs.LAME.LAMEEncoderCBR("", reqStream, AudioPCMConfig.RedBook);
(encoder.Settings as CUETools.Codecs.LAME.LAMEEncoderCBRSettings).StereoMode = settings.JointStereo ? (encoder.Settings as CUETools.Codecs.LAME.LAMEEncoderCBRSettings).StereoMode = settings.JointStereo ?
CUETools.Codecs.LAME.MpegMode.JOINT_STEREO : CUETools.Codecs.LAME.MpegMode.STEREO; CUETools.Codecs.LAME.Interop.MpegMode.JOINT_STEREO : CUETools.Codecs.LAME.Interop.MpegMode.STEREO;
(encoder.Settings as CUETools.Codecs.LAME.LAMEEncoderCBRSettings).CustomBitrate = settings.Bitrate; (encoder.Settings as CUETools.Codecs.LAME.LAMEEncoderCBRSettings).CustomBitrate = settings.Bitrate;
} }
} }

View File

@@ -59,8 +59,22 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Encoder.cs" /> <Compile Include="Interop\ACC.cs" />
<Compile Include="Lame.cs" /> <Compile Include="Interop\BE_CONFIG.cs" />
<Compile Include="Interop\BE_VERSION.cs" />
<Compile Include="Interop\Format.cs" />
<Compile Include="Interop\Lame_encDLL.cs" />
<Compile Include="Interop\LAME_QUALITY_PRESET.cs" />
<Compile Include="Interop\LHV1.cs" />
<Compile Include="Interop\MP3.cs" />
<Compile Include="Interop\MpegMode.cs" />
<Compile Include="Interop\VBRMETHOD.cs" />
<Compile Include="LAMEEncoder.cs" />
<Compile Include="LAMEEncoderCBR.cs" />
<Compile Include="LAMEEncoderCBRSettings.cs" />
<Compile Include="LAMEEncoderVBR.cs" />
<Compile Include="LAMEEncoderVBRProcessingQuality.cs" />
<Compile Include="LAMEEncoderVBRSettings.cs" />
<Compile Include="LameWriter.cs" /> <Compile Include="LameWriter.cs" />
<Compile Include="LameWriterSettings.cs" /> <Compile Include="LameWriterSettings.cs" />
<Compile Include="LameWriterVBR.cs" /> <Compile Include="LameWriterVBR.cs" />
@@ -90,6 +104,9 @@
<Install>true</Install> <Install>true</Install>
</BootstrapperPackage> </BootstrapperPackage>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Interop\License.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -1,438 +0,0 @@
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;
using System.IO;
using CUETools.Codecs;
namespace CUETools.Codecs.LAME
{
public class LAMEEncoder : IAudioDest
{
private bool closed = false;
private BE_CONFIG m_Mp3Config = null;
private uint m_hLameStream = 0;
private uint m_InputSamples = 0;
private uint m_OutBufferSize = 0;
private byte[] m_InBuffer = null;
private int m_InBufferPos = 0;
private byte[] m_OutBuffer = null;
private AudioPCMConfig _pcm;
private string _path;
private Stream _IO;
private long position = 0, sample_count = -1;
private long bytesWritten = 0;
public LAMEEncoder(string path, Stream IO, AudioPCMConfig pcm)
{
if (pcm.BitsPerSample != 16)// && pcm.BitsPerSample != 32)
throw new ArgumentOutOfRangeException("format", "Only 16 & 32 bits samples supported");
_pcm = pcm;
_path = path;
_IO = IO;
}
public LAMEEncoder(string path, AudioPCMConfig pcm)
: this(path, null, pcm)
{
}
public void DeInit(bool flush)
{
if (!inited || closed)
return;
try
{
if (flush)
{
uint EncodedSize = 0;
if (m_InBufferPos > 0)
{
if (Lame_encDll.EncodeChunk(m_hLameStream, m_InBuffer, 0, (uint)m_InBufferPos, m_OutBuffer, ref EncodedSize) == Lame_encDll.BE_ERR_SUCCESSFUL)
{
if (EncodedSize > 0)
{
_IO.Write(m_OutBuffer, 0, (int)EncodedSize);
bytesWritten += EncodedSize;
}
}
}
EncodedSize = 0;
if (Lame_encDll.beDeinitStream(m_hLameStream, m_OutBuffer, ref EncodedSize) == Lame_encDll.BE_ERR_SUCCESSFUL)
{
if (EncodedSize > 0)
{
_IO.Write(m_OutBuffer, 0, (int)EncodedSize);
bytesWritten += EncodedSize;
}
}
}
}
finally
{
Lame_encDll.beCloseStream(m_hLameStream);
_IO.Close();
closed = true;
}
}
public void Close()
{
bool needTag = !closed && _path != null && _path != "";
DeInit(true);
if (needTag)
{
bool utf8Required = Encoding.Default.GetString(Encoding.Default.GetBytes(_path)) != _path;
var tempDir = System.IO.Path.Combine(System.IO.Path.GetPathRoot(_path), "Temp");
var tempName = utf8Required ? System.IO.Path.Combine(tempDir, Guid.NewGuid().ToString()) : _path;
try
{
if (utf8Required && !Directory.Exists(tempDir)) Directory.CreateDirectory(tempDir);
if (utf8Required) File.Move(_path, tempName);
Lame_encDll.beWriteInfoTag(m_hLameStream, tempName);
if (utf8Required) File.Move(tempName, _path);
}
catch
{
if (utf8Required) File.Move(tempName, _path);
}
}
}
public void Delete()
{
if (!closed)
{
DeInit(false);
if (_path != "")
File.Delete(_path);
}
}
protected virtual BE_CONFIG MakeConfig()
{
return new BE_CONFIG(_pcm);
}
private bool inited = false;
private void Init()
{
if (inited)
return;
m_Mp3Config = MakeConfig();
uint LameResult = Lame_encDll.beInitStream(m_Mp3Config, ref m_InputSamples, ref m_OutBufferSize, ref m_hLameStream);
if (LameResult != Lame_encDll.BE_ERR_SUCCESSFUL)
throw new ApplicationException(string.Format("Lame_encDll.beInitStream failed with the error code {0}", LameResult));
m_InBuffer = new byte[m_InputSamples * 2]; //Input buffer is expected as short[]
m_OutBuffer = new byte[Math.Max(65536, m_OutBufferSize)];
if (_IO == null)
_IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read);
inited = true;
}
public unsafe void Write(AudioBuffer buff)
{
buff.Prepare(this);
Init();
byte[] buffer = buff.Bytes;
int index = 0;
int count = buff.ByteLength;
int ToCopy = 0;
uint EncodedSize = 0;
uint LameResult;
uint outBufferIndex = 0;
fixed (byte* pBuffer = buffer, pOutBuffer = m_OutBuffer)
{
while (count > 0)
{
if (m_InBufferPos > 0)
{
ToCopy = Math.Min(count, m_InBuffer.Length - m_InBufferPos);
Buffer.BlockCopy(buffer, index, m_InBuffer, m_InBufferPos, ToCopy);
m_InBufferPos += ToCopy;
index += ToCopy;
count -= ToCopy;
if (m_InBufferPos >= m_InBuffer.Length)
{
m_InBufferPos = 0;
if (outBufferIndex > 0)
{
_IO.Write(m_OutBuffer, 0, (int)outBufferIndex);
bytesWritten += outBufferIndex;
outBufferIndex = 0;
}
if ((LameResult = Lame_encDll.EncodeChunk(m_hLameStream, m_InBuffer, m_OutBuffer, ref EncodedSize)) == Lame_encDll.BE_ERR_SUCCESSFUL)
{
outBufferIndex += EncodedSize;
}
else
{
throw new ApplicationException(string.Format("Lame_encDll.EncodeChunk failed with the error code {0}", LameResult));
}
}
}
else
{
if (count >= m_InBuffer.Length)
{
if (outBufferIndex + m_OutBufferSize > m_OutBuffer.Length)
{
_IO.Write(m_OutBuffer, 0, (int)outBufferIndex);
bytesWritten += outBufferIndex;
outBufferIndex = 0;
}
if ((LameResult = Lame_encDll.EncodeChunk(m_hLameStream, pBuffer + index, (uint)m_InBuffer.Length, pOutBuffer + outBufferIndex, ref EncodedSize)) == Lame_encDll.BE_ERR_SUCCESSFUL)
{
outBufferIndex += EncodedSize;
}
else
{
throw new ApplicationException(string.Format("Lame_encDll.EncodeChunk failed with the error code {0}", LameResult));
}
count -= m_InBuffer.Length;
index += m_InBuffer.Length;
}
else
{
Buffer.BlockCopy(buffer, index, m_InBuffer, 0, count);
m_InBufferPos = count;
index += count;
count = 0;
}
}
}
}
if (outBufferIndex > 0)
{
_IO.Write(m_OutBuffer, 0, (int)outBufferIndex);
bytesWritten += outBufferIndex;
}
}
public virtual int CompressionLevel
{
get
{
return 0;
}
set
{
if (value != 0)
throw new Exception("unsupported compression level");
}
}
public virtual object Settings
{
get
{
return null;
}
set
{
if (value != null && value.GetType() != typeof(object))
throw new Exception("Unsupported options " + value);
}
}
public long Padding
{
set { }
}
public long Position
{
get
{
return position;
}
}
public long FinalSampleCount
{
set { sample_count = (int)value; }
}
public long BlockSize
{
set { }
get { return 0; }
}
public AudioPCMConfig PCM
{
get { return _pcm; }
}
public string Path { get { return _path; } }
public long BytesWritten
{
get
{
return bytesWritten;
}
}
}
public enum LAMEEncoderVBRProcessingQuality : uint
{
Best = 0,
Normal = 5,
}
public class LAMEEncoderVBRSettings
{
public LAMEEncoderVBRSettings()
{
// Iterate through each property and call ResetValue()
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this))
property.ResetValue(this);
}
[DefaultValue(LAMEEncoderVBRProcessingQuality.Normal)]
public LAMEEncoderVBRProcessingQuality Quality { get; set; }
}
[AudioEncoderClass("lame VBR", "mp3", false, "V9 V8 V7 V6 V5 V4 V3 V2 V1 V0", "V2", 2, typeof(LAMEEncoderVBRSettings))]
public class LAMEEncoderVBR : LAMEEncoder
{
private int quality = 0;
public LAMEEncoderVBR(string path, Stream IO, AudioPCMConfig pcm)
: base(path, IO, pcm)
{
}
public LAMEEncoderVBR(string path, AudioPCMConfig pcm)
: base(path, null, pcm)
{
}
protected override BE_CONFIG MakeConfig()
{
BE_CONFIG Mp3Config = new BE_CONFIG(PCM, 0, (uint)_settings.Quality);
Mp3Config.format.lhv1.bWriteVBRHeader = 1;
Mp3Config.format.lhv1.nMode = MpegMode.JOINT_STEREO;
Mp3Config.format.lhv1.bEnableVBR = 1;
Mp3Config.format.lhv1.nVBRQuality = quality;
Mp3Config.format.lhv1.nVbrMethod = VBRMETHOD.VBR_METHOD_NEW; // --vbr-new
return Mp3Config;
}
public override int CompressionLevel
{
get
{
return 9 - quality;
}
set
{
if (value < 0 || value > 9)
throw new Exception("unsupported compression level");
quality = 9 - value;
}
}
LAMEEncoderVBRSettings _settings = new LAMEEncoderVBRSettings();
public override object Settings
{
get
{
return _settings;
}
set
{
if (value as LAMEEncoderVBRSettings == null)
throw new Exception("Unsupported options " + value);
_settings = value as LAMEEncoderVBRSettings;
}
}
}
public class LAMEEncoderCBRSettings
{
public LAMEEncoderCBRSettings() {
// Iterate through each property and call ResetValue()
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this))
property.ResetValue(this);
}
[DefaultValue(0)]
public int CustomBitrate { get; set; }
[DefaultValue(MpegMode.STEREO)]
public MpegMode StereoMode { get; set; }
}
[AudioEncoderClass("lame CBR", "mp3", false, "96 128 192 256 320", "256", 2, typeof(LAMEEncoderCBRSettings))]
public class LAMEEncoderCBR : LAMEEncoder
{
private uint bps;
private static readonly uint[] bps_table = new uint[] {96, 128, 192, 256, 320};
public LAMEEncoderCBR(string path, Stream IO, AudioPCMConfig pcm)
: base(path, IO, pcm)
{
}
public LAMEEncoderCBR(string path, AudioPCMConfig pcm)
: base(path, null, pcm)
{
}
public override int CompressionLevel
{
get
{
for (int i = 0; i < bps_table.Length; i++)
if (bps == bps_table[i])
return i;
return -1;
}
set
{
if (value < 0 || value > bps_table.Length)
throw new Exception("unsupported compression level");
bps = bps_table[value];
}
}
LAMEEncoderCBRSettings _settings = new LAMEEncoderCBRSettings();
public override object Settings
{
get
{
return _settings;
}
set
{
if (value as LAMEEncoderCBRSettings == null)
throw new Exception("Unsupported options " + value);
_settings = value as LAMEEncoderCBRSettings;
}
}
protected override BE_CONFIG MakeConfig()
{
BE_CONFIG Mp3Config = new BE_CONFIG(PCM, _settings.CustomBitrate > 0 ? (uint)_settings.CustomBitrate : bps, 5);
Mp3Config.format.lhv1.bWriteVBRHeader = 1;
Mp3Config.format.lhv1.nMode = _settings.StereoMode;
//Mp3Config.format.lhv1.nVbrMethod = VBRMETHOD.VBR_METHOD_NONE; // --cbr
//Mp3Config.format.lhv1.nPreset = LAME_QUALITY_PRESET.LQP_NORMAL_QUALITY;
return Mp3Config;
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Runtime.InteropServices;
namespace CUETools.Codecs.LAME.Interop
{
[StructLayout(LayoutKind.Sequential), Serializable]
public struct ACC
{
public uint dwSampleRate;
public byte byMode;
public ushort wBitrate;
public byte byEncodingMethod;
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Runtime.InteropServices;
namespace CUETools.Codecs.LAME.Interop
{
[StructLayout(LayoutKind.Sequential), Serializable]
public class BE_CONFIG
{
// encoding formats
public const uint BE_CONFIG_MP3 = 0;
public const uint BE_CONFIG_LAME = 256;
public uint dwConfig;
public Format format;
public BE_CONFIG(AudioPCMConfig format, uint MpeBitRate, uint quality)
{
this.dwConfig = BE_CONFIG_LAME;
this.format = new Format(format, MpeBitRate, quality);
}
public BE_CONFIG(AudioPCMConfig format)
: this(format, 0, 5)
{
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Runtime.InteropServices;
namespace CUETools.Codecs.LAME.Interop
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class BE_VERSION
{
public const uint BE_MAX_HOMEPAGE = 256;
public byte byDLLMajorVersion;
public byte byDLLMinorVersion;
public byte byMajorVersion;
public byte byMinorVersion;
// DLL Release date
public byte byDay;
public byte byMonth;
public ushort wYear;
//Homepage URL
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257/*BE_MAX_HOMEPAGE+1*/)]
public string zHomepage;
public byte byAlphaLevel;
public byte byBetaLevel;
public byte byMMXEnabled;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 125)]
public byte[] btReserved;
public BE_VERSION()
{
btReserved = new byte[125];
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Runtime.InteropServices;
namespace CUETools.Codecs.LAME.Interop
{
[StructLayout(LayoutKind.Explicit), Serializable]
public class Format
{
[FieldOffset(0)]
public MP3 mp3;
[FieldOffset(0)]
public LHV1 lhv1;
[FieldOffset(0)]
public ACC acc;
public Format(AudioPCMConfig format, uint MpeBitRate, uint quality)
{
lhv1 = new LHV1(format, MpeBitRate, quality);
}
}
}

View File

@@ -0,0 +1,34 @@
namespace CUETools.Codecs.LAME.Interop
{
public enum LAME_QUALITY_PRESET : int
{
LQP_NOPRESET = -1,
// QUALITY PRESETS
LQP_NORMAL_QUALITY = 0,
LQP_LOW_QUALITY = 1,
LQP_HIGH_QUALITY = 2,
LQP_VOICE_QUALITY = 3,
LQP_R3MIX = 4,
LQP_VERYHIGH_QUALITY = 5,
LQP_STANDARD = 6,
LQP_FAST_STANDARD = 7,
LQP_EXTREME = 8,
LQP_FAST_EXTREME = 9,
LQP_INSANE = 10,
LQP_ABR = 11,
LQP_CBR = 12,
LQP_MEDIUM = 13,
LQP_FAST_MEDIUM = 14,
// NEW PRESET VALUES
LQP_PHONE = 1000,
LQP_SW = 2000,
LQP_AM = 3000,
LQP_FM = 4000,
LQP_VOICE = 5000,
LQP_RADIO = 6000,
LQP_TAPE = 7000,
LQP_HIFI = 8000,
LQP_CD = 9000,
LQP_STUDIO = 10000
}
}

View File

@@ -0,0 +1,130 @@
using System;
using System.Runtime.InteropServices;
namespace CUETools.Codecs.LAME.Interop
{
[StructLayout(LayoutKind.Sequential, Size = 327), Serializable]
public struct LHV1 // BE_CONFIG_LAME LAME header version 1
{
public const uint MPEG1 = 1;
public const uint MPEG2 = 0;
// STRUCTURE INFORMATION
public uint dwStructVersion;
public uint dwStructSize;
// BASIC ENCODER SETTINGS
public uint dwSampleRate; // SAMPLERATE OF INPUT FILE
public uint dwReSampleRate; // DOWNSAMPLERATE, 0=ENCODER DECIDES
public MpegMode nMode; // STEREO, MONO
public uint dwBitrate; // CBR bitrate, VBR min bitrate
public uint dwMaxBitrate; // CBR ignored, VBR Max bitrate
public LAME_QUALITY_PRESET nPreset; // Quality preset
public uint dwMpegVersion; // MPEG-1 OR MPEG-2
public uint dwPsyModel; // FUTURE USE, SET TO 0
public uint dwEmphasis; // FUTURE USE, SET TO 0
// BIT STREAM SETTINGS
public int bPrivate; // Set Private Bit (TRUE/FALSE)
public int bCRC; // Insert CRC (TRUE/FALSE)
public int bCopyright; // Set Copyright Bit (TRUE/FALSE)
public int bOriginal; // Set Original Bit (TRUE/FALSE)
// VBR STUFF
public int bWriteVBRHeader; // WRITE XING VBR HEADER (TRUE/FALSE)
public int bEnableVBR; // USE VBR ENCODING (TRUE/FALSE)
public int nVBRQuality; // VBR QUALITY 0..9
public uint dwVbrAbr_bps; // Use ABR in stead of nVBRQuality
public VBRMETHOD nVbrMethod;
public int bNoRes; // Disable Bit resorvoir (TRUE/FALSE)
// MISC SETTINGS
public int bStrictIso; // Use strict ISO encoding rules (TRUE/FALSE)
public ushort nQuality; // Quality Setting, HIGH BYTE should be NOT LOW byte, otherwhise quality=5
// FUTURE USE, SET TO 0, align strucutre to 331 bytes
//[ MarshalAs( UnmanagedType.ByValArray, SizeConst=255-4*4-2 )]
//public byte[] btReserved;//[255-4*sizeof(DWORD) - sizeof( WORD )];
public LHV1(AudioPCMConfig format, uint MpeBitRate, uint quality)
{
dwStructVersion = 1;
dwStructSize = (uint)Marshal.SizeOf(typeof(BE_CONFIG));
switch (format.SampleRate)
{
case 16000:
case 22050:
case 24000:
dwMpegVersion = MPEG2;
break;
case 32000:
case 44100:
case 48000:
dwMpegVersion = MPEG1;
break;
default:
throw new ArgumentOutOfRangeException("format", "Unsupported sample rate");
}
dwSampleRate = (uint)format.SampleRate; // INPUT FREQUENCY
dwReSampleRate = 0; // DON'T RESAMPLE
switch (format.ChannelCount)
{
case 1:
nMode = MpegMode.MONO;
break;
case 2:
nMode = MpegMode.STEREO;
break;
default:
throw new ArgumentOutOfRangeException("format", "Invalid number of channels");
}
switch (MpeBitRate)
{
case 0:
case 32:
case 40:
case 48:
case 56:
case 64:
case 80:
case 96:
case 112:
case 128:
case 160: //Allowed bit rates in MPEG1 and MPEG2
break;
case 192:
case 224:
case 256:
case 320: //Allowed only in MPEG1
if (dwMpegVersion != MPEG1)
{
throw new ArgumentOutOfRangeException("MpsBitRate", "Bit rate not compatible with input format");
}
break;
case 8:
case 16:
case 24:
case 144: //Allowed only in MPEG2
if (dwMpegVersion != MPEG2)
{
throw new ArgumentOutOfRangeException("MpsBitRate", "Bit rate not compatible with input format");
}
break;
default:
throw new ArgumentOutOfRangeException("MpsBitRate", "Unsupported bit rate");
}
dwBitrate = MpeBitRate; // MINIMUM BIT RATE
nPreset = LAME_QUALITY_PRESET.LQP_NORMAL_QUALITY; // QUALITY PRESET SETTING
dwPsyModel = 0; // USE DEFAULT PSYCHOACOUSTIC MODEL
dwEmphasis = 0; // NO EMPHASIS TURNED ON
bOriginal = 1; // SET ORIGINAL FLAG
bWriteVBRHeader = 0;
bNoRes = 0; // No Bit resorvoir
bCopyright = 0;
bCRC = 0;
bEnableVBR = 0;
bPrivate = 0;
bStrictIso = 0;
dwMaxBitrate = 0;
dwVbrAbr_bps = 0;
nQuality = (ushort)(quality | ((~quality) << 8));
nVbrMethod = VBRMETHOD.VBR_METHOD_NONE;
nVBRQuality = 0;
}
}
}

View File

@@ -0,0 +1,161 @@
using System;
using System.Runtime.InteropServices;
namespace CUETools.Codecs.LAME.Interop
{
/// <summary>
/// Lame_enc DLL functions
/// </summary>
public class Lame_encDll
{
//Error codes
public const uint BE_ERR_SUCCESSFUL = 0;
public const uint BE_ERR_INVALID_FORMAT = 1;
public const uint BE_ERR_INVALID_FORMAT_PARAMETERS = 2;
public const uint BE_ERR_NO_MORE_HANDLES = 3;
public const uint BE_ERR_INVALID_HANDLE = 4;
/// <summary>
/// This function is the first to call before starting an encoding stream.
/// </summary>
/// <param name="pbeConfig">Encoder settings</param>
/// <param name="dwSamples">Receives the number of samples (not bytes, each sample is a SHORT) to send to each beEncodeChunk() on return.</param>
/// <param name="dwBufferSize">Receives the minimun number of bytes that must have the output(result) buffer</param>
/// <param name="phbeStream">Receives the stream handle on return</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
[DllImport("Lame_enc.dll")]
public static extern uint beInitStream(BE_CONFIG pbeConfig, ref uint dwSamples, ref uint dwBufferSize, ref uint phbeStream);
/// <summary>
/// Encodes a chunk of samples. Please note that if you have set the output to
/// generate mono MP3 files you must feed beEncodeChunk() with mono samples
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="nSamples">Number of samples to be encoded for this call.
/// This should be identical to what is returned by beInitStream(),
/// unless you are encoding the last chunk, which might be smaller.</param>
/// <param name="pInSamples">Array of 16-bit signed samples to be encoded.
/// These should be in stereo when encoding a stereo MP3
/// and mono when encoding a mono MP3</param>
/// <param name="pOutput">Buffer where to write the encoded data.
/// This buffer should be at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns the number of bytes of encoded data written.
/// The amount of data written might vary from chunk to chunk</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
[DllImport("Lame_enc.dll")]
public static extern uint beEncodeChunk(uint hbeStream, uint nSamples, short[] pInSamples, [In, Out] byte[] pOutput, ref uint pdwOutput);
/// <summary>
/// Encodes a chunk of samples. Please note that if you have set the output to
/// generate mono MP3 files you must feed beEncodeChunk() with mono samples
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="nSamples">Number of samples to be encoded for this call.
/// This should be identical to what is returned by beInitStream(),
/// unless you are encoding the last chunk, which might be smaller.</param>
/// <param name="pSamples">Pointer at the 16-bit signed samples to be encoded.
/// InPtr is used to pass any type of array without need of make memory copy,
/// then gaining in performance. Note that nSamples is not the number of bytes,
/// but samples (is sample is a SHORT)</param>
/// <param name="pOutput">Buffer where to write the encoded data.
/// This buffer should be at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns the number of bytes of encoded data written.
/// The amount of data written might vary from chunk to chunk</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
[DllImport("Lame_enc.dll")]
protected static extern uint beEncodeChunk(uint hbeStream, uint nSamples, IntPtr pSamples, IntPtr pOutput, ref uint pdwOutput);
/// <summary>
/// Encodes a chunk of samples. Samples are contained in a byte array
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="buffer">Bytes to encode</param>
/// <param name="index">Position of the first byte to encode</param>
/// <param name="nBytes">Number of bytes to encode (not samples, samples are two byte lenght)</param>
/// <param name="pOutput">Buffer where to write the encoded data.
/// This buffer should be at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns the number of bytes of encoded data written.
/// The amount of data written might vary from chunk to chunk</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
public static unsafe uint EncodeChunk(uint hbeStream, byte* pSamples, uint nBytes, byte* pOutput, ref uint pdwOutput)
{
return beEncodeChunk(hbeStream, nBytes / 2/*Samples*/, (IntPtr)pSamples, (IntPtr)pOutput, ref pdwOutput);
}
/// <summary>
/// Encodes a chunk of samples. Samples are contained in a byte array
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="buffer">Bytes to encode</param>
/// <param name="index">Position of the first byte to encode</param>
/// <param name="nBytes">Number of bytes to encode (not samples, samples are two byte lenght)</param>
/// <param name="pOutput">Buffer where to write the encoded data.
/// This buffer should be at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns the number of bytes of encoded data written.
/// The amount of data written might vary from chunk to chunk</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
public static unsafe uint EncodeChunk(uint hbeStream, byte[] Samples, int index, uint nBytes, byte[] Output, ref uint pdwOutput)
{
fixed (byte* pSamples = &Samples[index], pOutput = Output)
return beEncodeChunk(hbeStream, nBytes / 2/*Samples*/, (IntPtr)pSamples, (IntPtr)pOutput, ref pdwOutput);
}
/// <summary>
/// Encodes a chunk of samples. Samples are contained in a byte array
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="buffer">Bytes to encode</param>
/// <param name="pOutput">Buffer where to write the encoded data.
/// This buffer should be at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns the number of bytes of encoded data written.
/// The amount of data written might vary from chunk to chunk</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
public static uint EncodeChunk(uint hbeStream, byte[] buffer, byte[] pOutput, ref uint pdwOutput)
{
return EncodeChunk(hbeStream, buffer, 0, (uint)buffer.Length, pOutput, ref pdwOutput);
}
/// <summary>
/// This function should be called after encoding the last chunk in order to flush
/// the encoder. It writes any encoded data that still might be left inside the
/// encoder to the output buffer. This function should NOT be called unless
/// you have encoded all of the chunks in your stream.
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="pOutput">Where to write the encoded data. This buffer should be
/// at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns number of bytes of encoded data written.</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
[DllImport("Lame_enc.dll")]
public static extern uint beDeinitStream(uint hbeStream, [In, Out] byte[] pOutput, ref uint pdwOutput);
/// <summary>
/// Last function to be called when finished encoding a stream.
/// Should unlike beDeinitStream() also be called if the encoding is canceled.
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
[DllImport("Lame_enc.dll")]
public static extern uint beCloseStream(uint hbeStream);
/// <summary>
/// Returns information like version numbers (both of the DLL and encoding engine),
/// release date and URL for lame_enc's homepage.
/// All this information should be made available to the user of your product
/// through a dialog box or something similar.
/// </summary>
/// <param name="pbeVersion"Where version number, release date and URL for homepage
/// is returned.</param>
[DllImport("Lame_enc.dll")]
public static extern void beVersion([Out] BE_VERSION pbeVersion);
[DllImport("Lame_enc.dll", CharSet = CharSet.Ansi)]
public static extern void beWriteVBRHeader(string pszMP3FileName);
[DllImport("Lame_enc.dll")]
public static extern uint beEncodeChunkFloatS16NI(uint hbeStream, uint nSamples, [In]float[] buffer_l, [In]float[] buffer_r, [In, Out]byte[] pOutput, ref uint pdwOutput);
[DllImport("Lame_enc.dll")]
public static extern uint beFlushNoGap(uint hbeStream, [In, Out]byte[] pOutput, ref uint pdwOutput);
[DllImport("Lame_enc.dll", CharSet = CharSet.Ansi)]
public static extern uint beWriteInfoTag(uint hbeStream, string lpszFileName);
}
}

View File

@@ -0,0 +1,25 @@
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
REMAINS UNCHANGED.
Email: yetiicb@hotmail.com
Copyright (C) 2002-2003 Idael Cardoso.
LAME ( LAME Ain't an Mp3 Encoder )
You must call the fucntion "beVersion" to obtain information like version
numbers (both of the DLL and encoding engine), release date and URL for
lame_enc's homepage. All this information should be made available to the
user of your product through a dialog box or something similar.
You must see all information about LAME project and legal license infos at
http://www.mp3dev.org/ The official LAME site
About Thomson and/or Fraunhofer patents:
Any use of this product does not convey a license under the relevant
intellectual property of Thomson and/or Fraunhofer Gesellschaft nor imply
any right to use this product in any finished end user or ready-to-use final
product. An independent license for such use is required.
For details, please visit http://www.mp3licensing.com.

View File

@@ -0,0 +1,17 @@
using System;
using System.Runtime.InteropServices;
namespace CUETools.Codecs.LAME.Interop
{
[StructLayout(LayoutKind.Sequential), Serializable]
public struct MP3 //BE_CONFIG_MP3
{
public uint dwSampleRate; // 48000, 44100 and 32000 allowed
public byte byMode; // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO
public ushort wBitrate; // 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256 and 320 allowed
public int bPrivate;
public int bCRC;
public int bCopyright;
public int bOriginal;
}
}

View File

@@ -0,0 +1,13 @@
namespace CUETools.Codecs.LAME.Interop
{
/* MPEG modes */
public enum MpegMode : uint
{
STEREO = 0,
JOINT_STEREO,
DUAL_CHANNEL, /* LAME doesn't supports this! */
MONO,
NOT_SET,
MAX_INDICATOR /* Don't use this! It's used for sanity checks. */
}
}

View File

@@ -0,0 +1,12 @@
namespace CUETools.Codecs.LAME.Interop
{
public enum VBRMETHOD : int
{
VBR_METHOD_NONE = -1,
VBR_METHOD_DEFAULT = 0,
VBR_METHOD_OLD = 1,
VBR_METHOD_NEW = 2,
VBR_METHOD_MTRH = 3,
VBR_METHOD_ABR = 4
}
}

View File

@@ -0,0 +1,281 @@
using System;
using System.IO;
using System.Text;
using CUETools.Codecs.LAME.Interop;
namespace CUETools.Codecs.LAME
{
public class LAMEEncoder : IAudioDest
{
private bool closed = false;
private BE_CONFIG m_Mp3Config = null;
private uint m_hLameStream = 0;
private uint m_InputSamples = 0;
private uint m_OutBufferSize = 0;
private byte[] m_InBuffer = null;
private int m_InBufferPos = 0;
private byte[] m_OutBuffer = null;
private AudioPCMConfig _pcm;
private string _path;
private Stream _IO;
private long position = 0, sample_count = -1;
private long bytesWritten = 0;
private bool inited = false;
public virtual int CompressionLevel
{
get
{
return 0;
}
set
{
if (value != 0)
throw new Exception("unsupported compression level");
}
}
public virtual object Settings
{
get
{
return null;
}
set
{
if (value != null && value.GetType() != typeof(object))
throw new Exception("Unsupported options " + value);
}
}
public long Padding
{
set { }
}
public long Position
{
get { return position; }
}
public long FinalSampleCount
{
set { sample_count = (int)value; }
}
public long BlockSize
{
set { }
get { return 0; }
}
public AudioPCMConfig PCM
{
get { return _pcm; }
}
public string Path { get { return _path; } }
public long BytesWritten
{
get { return bytesWritten; }
}
public LAMEEncoder(string path, Stream IO, AudioPCMConfig pcm)
{
if (pcm.BitsPerSample != 16)// && pcm.BitsPerSample != 32)
throw new ArgumentOutOfRangeException("format", "Only 16 & 32 bits samples supported");
_pcm = pcm;
_path = path;
_IO = IO;
}
public LAMEEncoder(string path, AudioPCMConfig pcm)
: this(path, null, pcm)
{
}
public void DeInit(bool flush)
{
if (!inited || closed)
return;
try
{
if (flush)
{
uint EncodedSize = 0;
if (m_InBufferPos > 0)
{
if (Lame_encDll.EncodeChunk(m_hLameStream, m_InBuffer, 0, (uint)m_InBufferPos, m_OutBuffer, ref EncodedSize) == Lame_encDll.BE_ERR_SUCCESSFUL)
{
if (EncodedSize > 0)
{
_IO.Write(m_OutBuffer, 0, (int)EncodedSize);
bytesWritten += EncodedSize;
}
}
}
EncodedSize = 0;
if (Lame_encDll.beDeinitStream(m_hLameStream, m_OutBuffer, ref EncodedSize) == Lame_encDll.BE_ERR_SUCCESSFUL)
{
if (EncodedSize > 0)
{
_IO.Write(m_OutBuffer, 0, (int)EncodedSize);
bytesWritten += EncodedSize;
}
}
}
}
finally
{
Lame_encDll.beCloseStream(m_hLameStream);
_IO.Close();
closed = true;
}
}
public void Close()
{
bool needTag = !closed && _path != null && _path != "";
DeInit(true);
if (needTag)
{
bool utf8Required = Encoding.Default.GetString(Encoding.Default.GetBytes(_path)) != _path;
var tempDir = System.IO.Path.Combine(System.IO.Path.GetPathRoot(_path), "Temp");
var tempName = utf8Required ? System.IO.Path.Combine(tempDir, Guid.NewGuid().ToString()) : _path;
try
{
if (utf8Required && !Directory.Exists(tempDir)) Directory.CreateDirectory(tempDir);
if (utf8Required) File.Move(_path, tempName);
Lame_encDll.beWriteInfoTag(m_hLameStream, tempName);
if (utf8Required) File.Move(tempName, _path);
}
catch
{
if (utf8Required) File.Move(tempName, _path);
}
}
}
public void Delete()
{
if (!closed)
{
DeInit(false);
if (_path != "")
File.Delete(_path);
}
}
protected virtual BE_CONFIG MakeConfig()
{
return new BE_CONFIG(_pcm);
}
private void Init()
{
if (inited)
return;
m_Mp3Config = MakeConfig();
uint LameResult = Lame_encDll.beInitStream(m_Mp3Config, ref m_InputSamples, ref m_OutBufferSize, ref m_hLameStream);
if (LameResult != Lame_encDll.BE_ERR_SUCCESSFUL)
throw new ApplicationException(string.Format("Lame_encDll.beInitStream failed with the error code {0}", LameResult));
m_InBuffer = new byte[m_InputSamples * 2]; //Input buffer is expected as short[]
m_OutBuffer = new byte[Math.Max(65536, m_OutBufferSize)];
if (_IO == null)
_IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read);
inited = true;
}
public unsafe void Write(AudioBuffer buff)
{
buff.Prepare(this);
Init();
byte[] buffer = buff.Bytes;
int index = 0;
int count = buff.ByteLength;
int ToCopy = 0;
uint EncodedSize = 0;
uint LameResult;
uint outBufferIndex = 0;
fixed (byte* pBuffer = buffer, pOutBuffer = m_OutBuffer)
{
while (count > 0)
{
if (m_InBufferPos > 0)
{
ToCopy = Math.Min(count, m_InBuffer.Length - m_InBufferPos);
Buffer.BlockCopy(buffer, index, m_InBuffer, m_InBufferPos, ToCopy);
m_InBufferPos += ToCopy;
index += ToCopy;
count -= ToCopy;
if (m_InBufferPos >= m_InBuffer.Length)
{
m_InBufferPos = 0;
if (outBufferIndex > 0)
{
_IO.Write(m_OutBuffer, 0, (int)outBufferIndex);
bytesWritten += outBufferIndex;
outBufferIndex = 0;
}
if ((LameResult = Lame_encDll.EncodeChunk(m_hLameStream, m_InBuffer, m_OutBuffer, ref EncodedSize)) == Lame_encDll.BE_ERR_SUCCESSFUL)
{
outBufferIndex += EncodedSize;
}
else
{
throw new ApplicationException(string.Format("Lame_encDll.EncodeChunk failed with the error code {0}", LameResult));
}
}
}
else
{
if (count >= m_InBuffer.Length)
{
if (outBufferIndex + m_OutBufferSize > m_OutBuffer.Length)
{
_IO.Write(m_OutBuffer, 0, (int)outBufferIndex);
bytesWritten += outBufferIndex;
outBufferIndex = 0;
}
if ((LameResult = Lame_encDll.EncodeChunk(m_hLameStream, pBuffer + index, (uint)m_InBuffer.Length, pOutBuffer + outBufferIndex, ref EncodedSize)) == Lame_encDll.BE_ERR_SUCCESSFUL)
{
outBufferIndex += EncodedSize;
}
else
{
throw new ApplicationException(string.Format("Lame_encDll.EncodeChunk failed with the error code {0}", LameResult));
}
count -= m_InBuffer.Length;
index += m_InBuffer.Length;
}
else
{
Buffer.BlockCopy(buffer, index, m_InBuffer, 0, count);
m_InBufferPos = count;
index += count;
count = 0;
}
}
}
}
if (outBufferIndex > 0)
{
_IO.Write(m_OutBuffer, 0, (int)outBufferIndex);
bytesWritten += outBufferIndex;
}
}
}
}

View File

@@ -0,0 +1,70 @@
using System;
using System.IO;
using CUETools.Codecs.LAME.Interop;
namespace CUETools.Codecs.LAME
{
[AudioEncoderClass("lame CBR", "mp3", false, "96 128 192 256 320", "256", 2, typeof(LAMEEncoderCBRSettings))]
public class LAMEEncoderCBR : LAMEEncoder
{
private static readonly uint[] bps_table = new uint[] { 96, 128, 192, 256, 320 };
private uint bps;
private LAMEEncoderCBRSettings _settings = new LAMEEncoderCBRSettings();
public override object Settings
{
get
{
return _settings;
}
set
{
if (value as LAMEEncoderCBRSettings == null)
throw new Exception("Unsupported options " + value);
_settings = value as LAMEEncoderCBRSettings;
}
}
public override int CompressionLevel
{
get
{
for (int i = 0; i < bps_table.Length; i++)
{
if (bps == bps_table[i])
{
return i;
}
}
return -1;
}
set
{
if (value < 0 || value > bps_table.Length)
throw new Exception("unsupported compression level");
bps = bps_table[value];
}
}
public LAMEEncoderCBR(string path, Stream IO, AudioPCMConfig pcm)
: base(path, IO, pcm)
{
}
public LAMEEncoderCBR(string path, AudioPCMConfig pcm)
: base(path, null, pcm)
{
}
protected override BE_CONFIG MakeConfig()
{
BE_CONFIG Mp3Config = new BE_CONFIG(PCM, _settings.CustomBitrate > 0 ? (uint)_settings.CustomBitrate : bps, 5);
Mp3Config.format.lhv1.bWriteVBRHeader = 1;
Mp3Config.format.lhv1.nMode = _settings.StereoMode;
//Mp3Config.format.lhv1.nVbrMethod = VBRMETHOD.VBR_METHOD_NONE; // --cbr
//Mp3Config.format.lhv1.nPreset = LAME_QUALITY_PRESET.LQP_NORMAL_QUALITY;
return Mp3Config;
}
}
}

View File

@@ -0,0 +1,23 @@
using System.ComponentModel;
using CUETools.Codecs.LAME.Interop;
namespace CUETools.Codecs.LAME
{
public class LAMEEncoderCBRSettings
{
[DefaultValue(0)]
public int CustomBitrate { get; set; }
[DefaultValue(MpegMode.STEREO)]
public MpegMode StereoMode { get; set; }
public LAMEEncoderCBRSettings()
{
// Iterate through each property and call ResetValue()
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this))
{
property.ResetValue(this);
}
}
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.IO;
using CUETools.Codecs.LAME.Interop;
namespace CUETools.Codecs.LAME
{
[AudioEncoderClass("lame VBR", "mp3", false, "V9 V8 V7 V6 V5 V4 V3 V2 V1 V0", "V2", 2, typeof(LAMEEncoderVBRSettings))]
public class LAMEEncoderVBR : LAMEEncoder
{
private int quality = 0;
private LAMEEncoderVBRSettings _settings = new LAMEEncoderVBRSettings();
public override int CompressionLevel
{
get
{
return 9 - quality;
}
set
{
if (value < 0 || value > 9)
throw new Exception("unsupported compression level");
quality = 9 - value;
}
}
public override object Settings
{
get
{
return _settings;
}
set
{
if (value as LAMEEncoderVBRSettings == null)
throw new Exception("Unsupported options " + value);
_settings = value as LAMEEncoderVBRSettings;
}
}
public LAMEEncoderVBR(string path, Stream IO, AudioPCMConfig pcm)
: base(path, IO, pcm)
{
}
public LAMEEncoderVBR(string path, AudioPCMConfig pcm)
: base(path, null, pcm)
{
}
protected override BE_CONFIG MakeConfig()
{
BE_CONFIG Mp3Config = new BE_CONFIG(PCM, 0, (uint)_settings.Quality);
Mp3Config.format.lhv1.bWriteVBRHeader = 1;
Mp3Config.format.lhv1.nMode = MpegMode.JOINT_STEREO;
Mp3Config.format.lhv1.bEnableVBR = 1;
Mp3Config.format.lhv1.nVBRQuality = quality;
Mp3Config.format.lhv1.nVbrMethod = VBRMETHOD.VBR_METHOD_NEW; // --vbr-new
return Mp3Config;
}
}
}

View File

@@ -0,0 +1,8 @@
namespace CUETools.Codecs.LAME
{
public enum LAMEEncoderVBRProcessingQuality : uint
{
Best = 0,
Normal = 5,
}
}

View File

@@ -0,0 +1,18 @@
using System.ComponentModel;
namespace CUETools.Codecs.LAME
{
public class LAMEEncoderVBRSettings
{
[DefaultValue(LAMEEncoderVBRProcessingQuality.Normal)]
public LAMEEncoderVBRProcessingQuality Quality { get; set; }
public LAMEEncoderVBRSettings()
{
// Iterate through each property and call ResetValue()
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this)) {
property.ResetValue(this);
}
}
}
}

View File

@@ -1,455 +0,0 @@
//
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
// REMAINS UNCHANGED.
//
// Email: yetiicb@hotmail.com
//
// Copyright (C) 2002-2003 Idael Cardoso.
//
// LAME ( LAME Ain't an Mp3 Encoder )
// You must call the fucntion "beVersion" to obtain information like version
// numbers (both of the DLL and encoding engine), release date and URL for
// lame_enc's homepage. All this information should be made available to the
// user of your product through a dialog box or something similar.
// You must see all information about LAME project and legal license infos at
// http://www.mp3dev.org/ The official LAME site
//
//
// About Thomson and/or Fraunhofer patents:
// Any use of this product does not convey a license under the relevant
// intellectual property of Thomson and/or Fraunhofer Gesellschaft nor imply
// any right to use this product in any finished end user or ready-to-use final
// product. An independent license for such use is required.
// For details, please visit http://www.mp3licensing.com.
//
using System;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using CUETools.Codecs;
namespace CUETools.Codecs.LAME
{
public enum VBRMETHOD : int
{
VBR_METHOD_NONE = -1,
VBR_METHOD_DEFAULT = 0,
VBR_METHOD_OLD = 1,
VBR_METHOD_NEW = 2,
VBR_METHOD_MTRH = 3,
VBR_METHOD_ABR = 4
}
/* MPEG modes */
public enum MpegMode : uint
{
STEREO = 0,
JOINT_STEREO,
DUAL_CHANNEL, /* LAME doesn't supports this! */
MONO,
NOT_SET,
MAX_INDICATOR /* Don't use this! It's used for sanity checks. */
}
public enum LAME_QUALITY_PRESET : int
{
LQP_NOPRESET =-1,
// QUALITY PRESETS
LQP_NORMAL_QUALITY = 0,
LQP_LOW_QUALITY = 1,
LQP_HIGH_QUALITY = 2,
LQP_VOICE_QUALITY = 3,
LQP_R3MIX = 4,
LQP_VERYHIGH_QUALITY = 5,
LQP_STANDARD = 6,
LQP_FAST_STANDARD = 7,
LQP_EXTREME = 8,
LQP_FAST_EXTREME = 9,
LQP_INSANE = 10,
LQP_ABR = 11,
LQP_CBR = 12,
LQP_MEDIUM = 13,
LQP_FAST_MEDIUM = 14,
// NEW PRESET VALUES
LQP_PHONE =1000,
LQP_SW =2000,
LQP_AM =3000,
LQP_FM =4000,
LQP_VOICE =5000,
LQP_RADIO =6000,
LQP_TAPE =7000,
LQP_HIFI =8000,
LQP_CD =9000,
LQP_STUDIO =10000
}
[StructLayout(LayoutKind.Sequential), Serializable]
public struct MP3 //BE_CONFIG_MP3
{
public uint dwSampleRate; // 48000, 44100 and 32000 allowed
public byte byMode; // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO
public ushort wBitrate; // 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256 and 320 allowed
public int bPrivate;
public int bCRC;
public int bCopyright;
public int bOriginal;
}
[StructLayout(LayoutKind.Sequential, Size=327), Serializable]
public struct LHV1 // BE_CONFIG_LAME LAME header version 1
{
public const uint MPEG1 = 1;
public const uint MPEG2 = 0;
// STRUCTURE INFORMATION
public uint dwStructVersion;
public uint dwStructSize;
// BASIC ENCODER SETTINGS
public uint dwSampleRate; // SAMPLERATE OF INPUT FILE
public uint dwReSampleRate; // DOWNSAMPLERATE, 0=ENCODER DECIDES
public MpegMode nMode; // STEREO, MONO
public uint dwBitrate; // CBR bitrate, VBR min bitrate
public uint dwMaxBitrate; // CBR ignored, VBR Max bitrate
public LAME_QUALITY_PRESET nPreset; // Quality preset
public uint dwMpegVersion; // MPEG-1 OR MPEG-2
public uint dwPsyModel; // FUTURE USE, SET TO 0
public uint dwEmphasis; // FUTURE USE, SET TO 0
// BIT STREAM SETTINGS
public int bPrivate; // Set Private Bit (TRUE/FALSE)
public int bCRC; // Insert CRC (TRUE/FALSE)
public int bCopyright; // Set Copyright Bit (TRUE/FALSE)
public int bOriginal; // Set Original Bit (TRUE/FALSE)
// VBR STUFF
public int bWriteVBRHeader; // WRITE XING VBR HEADER (TRUE/FALSE)
public int bEnableVBR; // USE VBR ENCODING (TRUE/FALSE)
public int nVBRQuality; // VBR QUALITY 0..9
public uint dwVbrAbr_bps; // Use ABR in stead of nVBRQuality
public VBRMETHOD nVbrMethod;
public int bNoRes; // Disable Bit resorvoir (TRUE/FALSE)
// MISC SETTINGS
public int bStrictIso; // Use strict ISO encoding rules (TRUE/FALSE)
public ushort nQuality; // Quality Setting, HIGH BYTE should be NOT LOW byte, otherwhise quality=5
// FUTURE USE, SET TO 0, align strucutre to 331 bytes
//[ MarshalAs( UnmanagedType.ByValArray, SizeConst=255-4*4-2 )]
//public byte[] btReserved;//[255-4*sizeof(DWORD) - sizeof( WORD )];
public LHV1(AudioPCMConfig format, uint MpeBitRate, uint quality)
{
dwStructVersion = 1;
dwStructSize = (uint)Marshal.SizeOf(typeof(BE_CONFIG));
switch (format.SampleRate)
{
case 16000 :
case 22050 :
case 24000 :
dwMpegVersion = MPEG2;
break;
case 32000 :
case 44100 :
case 48000 :
dwMpegVersion = MPEG1;
break;
default :
throw new ArgumentOutOfRangeException("format", "Unsupported sample rate");
}
dwSampleRate = (uint)format.SampleRate; // INPUT FREQUENCY
dwReSampleRate = 0; // DON'T RESAMPLE
switch (format.ChannelCount)
{
case 1 :
nMode = MpegMode.MONO;
break;
case 2 :
nMode = MpegMode.STEREO;
break;
default:
throw new ArgumentOutOfRangeException("format", "Invalid number of channels");
}
switch (MpeBitRate)
{
case 0 :
case 32 :
case 40 :
case 48 :
case 56 :
case 64 :
case 80 :
case 96 :
case 112 :
case 128 :
case 160 : //Allowed bit rates in MPEG1 and MPEG2
break;
case 192 :
case 224 :
case 256 :
case 320 : //Allowed only in MPEG1
if (dwMpegVersion != MPEG1)
{
throw new ArgumentOutOfRangeException("MpsBitRate", "Bit rate not compatible with input format");
}
break;
case 8 :
case 16 :
case 24 :
case 144 : //Allowed only in MPEG2
if (dwMpegVersion != MPEG2)
{
throw new ArgumentOutOfRangeException("MpsBitRate", "Bit rate not compatible with input format");
}
break;
default :
throw new ArgumentOutOfRangeException("MpsBitRate", "Unsupported bit rate");
}
dwBitrate = MpeBitRate; // MINIMUM BIT RATE
nPreset = LAME_QUALITY_PRESET.LQP_NORMAL_QUALITY; // QUALITY PRESET SETTING
dwPsyModel = 0; // USE DEFAULT PSYCHOACOUSTIC MODEL
dwEmphasis = 0; // NO EMPHASIS TURNED ON
bOriginal = 1; // SET ORIGINAL FLAG
bWriteVBRHeader = 0;
bNoRes = 0; // No Bit resorvoir
bCopyright = 0;
bCRC = 0;
bEnableVBR = 0;
bPrivate = 0;
bStrictIso = 0;
dwMaxBitrate = 0;
dwVbrAbr_bps = 0;
nQuality = (ushort)(quality | ((~quality) << 8));
nVbrMethod = VBRMETHOD.VBR_METHOD_NONE;
nVBRQuality = 0;
}
}
[StructLayout(LayoutKind.Sequential), Serializable]
public struct ACC
{
public uint dwSampleRate;
public byte byMode;
public ushort wBitrate;
public byte byEncodingMethod;
}
[StructLayout(LayoutKind.Explicit), Serializable]
public class Format
{
[FieldOffset(0)]
public MP3 mp3;
[FieldOffset(0)]
public LHV1 lhv1;
[FieldOffset(0)]
public ACC acc;
public Format(AudioPCMConfig format, uint MpeBitRate, uint quality)
{
lhv1 = new LHV1(format, MpeBitRate, quality);
}
}
[StructLayout(LayoutKind.Sequential), Serializable]
public class BE_CONFIG
{
// encoding formats
public const uint BE_CONFIG_MP3 = 0;
public const uint BE_CONFIG_LAME = 256;
public uint dwConfig;
public Format format;
public BE_CONFIG(AudioPCMConfig format, uint MpeBitRate, uint quality)
{
this.dwConfig = BE_CONFIG_LAME;
this.format = new Format(format, MpeBitRate, quality);
}
public BE_CONFIG(AudioPCMConfig format)
: this(format, 0, 5)
{
}
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public class BE_VERSION
{
public const uint BE_MAX_HOMEPAGE = 256;
public byte byDLLMajorVersion;
public byte byDLLMinorVersion;
public byte byMajorVersion;
public byte byMinorVersion;
// DLL Release date
public byte byDay;
public byte byMonth;
public ushort wYear;
//Homepage URL
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=257/*BE_MAX_HOMEPAGE+1*/)]
public string zHomepage;
public byte byAlphaLevel;
public byte byBetaLevel;
public byte byMMXEnabled;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=125)]
public byte[] btReserved;
public BE_VERSION()
{
btReserved = new byte[125];
}
}
/// <summary>
/// Lame_enc DLL functions
/// </summary>
public class Lame_encDll
{
//Error codes
public const uint BE_ERR_SUCCESSFUL = 0;
public const uint BE_ERR_INVALID_FORMAT = 1;
public const uint BE_ERR_INVALID_FORMAT_PARAMETERS = 2;
public const uint BE_ERR_NO_MORE_HANDLES = 3;
public const uint BE_ERR_INVALID_HANDLE = 4;
/// <summary>
/// This function is the first to call before starting an encoding stream.
/// </summary>
/// <param name="pbeConfig">Encoder settings</param>
/// <param name="dwSamples">Receives the number of samples (not bytes, each sample is a SHORT) to send to each beEncodeChunk() on return.</param>
/// <param name="dwBufferSize">Receives the minimun number of bytes that must have the output(result) buffer</param>
/// <param name="phbeStream">Receives the stream handle on return</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
[DllImport("Lame_enc.dll")]
public static extern uint beInitStream(BE_CONFIG pbeConfig, ref uint dwSamples, ref uint dwBufferSize, ref uint phbeStream);
/// <summary>
/// Encodes a chunk of samples. Please note that if you have set the output to
/// generate mono MP3 files you must feed beEncodeChunk() with mono samples
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="nSamples">Number of samples to be encoded for this call.
/// This should be identical to what is returned by beInitStream(),
/// unless you are encoding the last chunk, which might be smaller.</param>
/// <param name="pInSamples">Array of 16-bit signed samples to be encoded.
/// These should be in stereo when encoding a stereo MP3
/// and mono when encoding a mono MP3</param>
/// <param name="pOutput">Buffer where to write the encoded data.
/// This buffer should be at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns the number of bytes of encoded data written.
/// The amount of data written might vary from chunk to chunk</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
[DllImport("Lame_enc.dll")]
public static extern uint beEncodeChunk(uint hbeStream, uint nSamples, short[] pInSamples, [In, Out] byte[] pOutput, ref uint pdwOutput);
/// <summary>
/// Encodes a chunk of samples. Please note that if you have set the output to
/// generate mono MP3 files you must feed beEncodeChunk() with mono samples
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="nSamples">Number of samples to be encoded for this call.
/// This should be identical to what is returned by beInitStream(),
/// unless you are encoding the last chunk, which might be smaller.</param>
/// <param name="pSamples">Pointer at the 16-bit signed samples to be encoded.
/// InPtr is used to pass any type of array without need of make memory copy,
/// then gaining in performance. Note that nSamples is not the number of bytes,
/// but samples (is sample is a SHORT)</param>
/// <param name="pOutput">Buffer where to write the encoded data.
/// This buffer should be at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns the number of bytes of encoded data written.
/// The amount of data written might vary from chunk to chunk</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
[DllImport("Lame_enc.dll")]
protected static extern uint beEncodeChunk(uint hbeStream, uint nSamples, IntPtr pSamples, IntPtr pOutput, ref uint pdwOutput);
/// <summary>
/// Encodes a chunk of samples. Samples are contained in a byte array
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="buffer">Bytes to encode</param>
/// <param name="index">Position of the first byte to encode</param>
/// <param name="nBytes">Number of bytes to encode (not samples, samples are two byte lenght)</param>
/// <param name="pOutput">Buffer where to write the encoded data.
/// This buffer should be at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns the number of bytes of encoded data written.
/// The amount of data written might vary from chunk to chunk</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
public static unsafe uint EncodeChunk(uint hbeStream, byte *pSamples, uint nBytes, byte* pOutput, ref uint pdwOutput)
{
return beEncodeChunk(hbeStream, nBytes / 2/*Samples*/, (IntPtr)pSamples, (IntPtr)pOutput, ref pdwOutput);
}
/// <summary>
/// Encodes a chunk of samples. Samples are contained in a byte array
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="buffer">Bytes to encode</param>
/// <param name="index">Position of the first byte to encode</param>
/// <param name="nBytes">Number of bytes to encode (not samples, samples are two byte lenght)</param>
/// <param name="pOutput">Buffer where to write the encoded data.
/// This buffer should be at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns the number of bytes of encoded data written.
/// The amount of data written might vary from chunk to chunk</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
public static unsafe uint EncodeChunk(uint hbeStream, byte[] Samples, int index, uint nBytes, byte[] Output, ref uint pdwOutput)
{
fixed(byte *pSamples = &Samples[index], pOutput = Output)
return beEncodeChunk(hbeStream, nBytes / 2/*Samples*/, (IntPtr)pSamples, (IntPtr)pOutput, ref pdwOutput);
}
/// <summary>
/// Encodes a chunk of samples. Samples are contained in a byte array
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="buffer">Bytes to encode</param>
/// <param name="pOutput">Buffer where to write the encoded data.
/// This buffer should be at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns the number of bytes of encoded data written.
/// The amount of data written might vary from chunk to chunk</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
public static uint EncodeChunk(uint hbeStream, byte[] buffer, byte[] pOutput, ref uint pdwOutput)
{
return EncodeChunk(hbeStream, buffer, 0, (uint)buffer.Length, pOutput, ref pdwOutput);
}
/// <summary>
/// This function should be called after encoding the last chunk in order to flush
/// the encoder. It writes any encoded data that still might be left inside the
/// encoder to the output buffer. This function should NOT be called unless
/// you have encoded all of the chunks in your stream.
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <param name="pOutput">Where to write the encoded data. This buffer should be
/// at least of the minimum size returned by beInitStream().</param>
/// <param name="pdwOutput">Returns number of bytes of encoded data written.</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
[DllImport("Lame_enc.dll")]
public static extern uint beDeinitStream(uint hbeStream, [In, Out] byte[] pOutput, ref uint pdwOutput);
/// <summary>
/// Last function to be called when finished encoding a stream.
/// Should unlike beDeinitStream() also be called if the encoding is canceled.
/// </summary>
/// <param name="hbeStream">Handle of the stream.</param>
/// <returns>On success: BE_ERR_SUCCESSFUL</returns>
[DllImport("Lame_enc.dll")]
public static extern uint beCloseStream(uint hbeStream);
/// <summary>
/// Returns information like version numbers (both of the DLL and encoding engine),
/// release date and URL for lame_enc's homepage.
/// All this information should be made available to the user of your product
/// through a dialog box or something similar.
/// </summary>
/// <param name="pbeVersion"Where version number, release date and URL for homepage
/// is returned.</param>
[DllImport("Lame_enc.dll")]
public static extern void beVersion([Out] BE_VERSION pbeVersion);
[DllImport("Lame_enc.dll", CharSet=CharSet.Ansi)]
public static extern void beWriteVBRHeader(string pszMP3FileName);
[DllImport("Lame_enc.dll")]
public static extern uint beEncodeChunkFloatS16NI(uint hbeStream, uint nSamples, [In]float[] buffer_l, [In]float[] buffer_r, [In, Out]byte[] pOutput, ref uint pdwOutput);
[DllImport("Lame_enc.dll")]
public static extern uint beFlushNoGap(uint hbeStream, [In, Out]byte[] pOutput, ref uint pdwOutput);
[DllImport("Lame_enc.dll", CharSet=CharSet.Ansi)]
public static extern uint beWriteInfoTag(uint hbeStream, string lpszFileName);
}
}

View File

@@ -1,8 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using CUETools.Codecs;
namespace CUETools.Codecs.LAME namespace CUETools.Codecs.LAME
{ {