Code cleanup; Reader classes renamed to Decoders, Writers to Encoders, every Decoder must have a corresponding Settings class now just like Encoders. UserDefinedEncoders renamed to CommandLineEncoders, etc.

This commit is contained in:
Grigory Chudov
2018-03-24 12:15:49 -04:00
parent ca8bb2fff6
commit e1f8906170
65 changed files with 1713 additions and 1798 deletions

View File

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

View File

@@ -0,0 +1,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,247 @@
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 AudioEncoderSettings m_settings;
private string _path;
private Stream _IO;
private long position = 0, sample_count = -1;
private long bytesWritten = 0;
private bool inited = false;
public virtual AudioEncoderSettings Settings
{
get
{
return m_settings;
}
}
public long Position
{
get { return position; }
}
public long FinalSampleCount
{
set { sample_count = (int)value; }
}
public string Path { get { return _path; } }
public long BytesWritten
{
get { return bytesWritten; }
}
public LAMEEncoder(string path, Stream IO, AudioEncoderSettings settings)
{
if (settings.PCM.BitsPerSample != 16)// && pcm.BitsPerSample != 32)
throw new ArgumentOutOfRangeException("format", "Only 16 & 32 bits samples supported");
m_settings = settings;
_path = path;
_IO = IO;
}
public LAMEEncoder(string path, AudioEncoderSettings settings)
: this(path, null, settings)
{
}
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(Settings.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,40 @@
using System;
using System.IO;
using CUETools.Codecs.LAME.Interop;
namespace CUETools.Codecs.LAME
{
//[AudioEncoderClass("lame CBR", "mp3", false, 2, typeof(LAMEEncoderCBRSettings))]
public class LAMEEncoderCBR : LAMEEncoder
{
private LAMEEncoderCBRSettings m_settings;
public override AudioEncoderSettings Settings
{
get
{
return m_settings;
}
}
public LAMEEncoderCBR(string path, Stream IO, AudioEncoderSettings settings)
: base(path, IO, settings)
{
}
public LAMEEncoderCBR(string path, AudioEncoderSettings settings)
: base(path, null, settings)
{
}
protected override BE_CONFIG MakeConfig()
{
BE_CONFIG Mp3Config = new BE_CONFIG(Settings.PCM, m_settings.CustomBitrate > 0 ? (uint)m_settings.CustomBitrate : LAMEEncoderCBRSettings.bps_table[m_settings.EncoderModeIndex], 5);
Mp3Config.format.lhv1.bWriteVBRHeader = 1;
Mp3Config.format.lhv1.nMode = m_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,24 @@
using System;
using System.ComponentModel;
using CUETools.Codecs.LAME.Interop;
namespace CUETools.Codecs.LAME
{
public class LAMEEncoderCBRSettings : AudioEncoderSettings
{
public override Type EncoderType => typeof(LAMEEncoderCBR);
public static readonly uint[] bps_table = new uint[] { 96, 128, 192, 256, 320 };
[DefaultValue(0)]
public int CustomBitrate { get; set; }
[DefaultValue(MpegMode.STEREO)]
public MpegMode StereoMode { get; set; }
public LAMEEncoderCBRSettings()
: base("96 128 192 256 320", "256")
{
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using System.IO;
using CUETools.Codecs.LAME.Interop;
namespace CUETools.Codecs.LAME
{
//[AudioEncoderClass("lame VBR", "mp3", false, 2, typeof(LAMEEncoderVBRSettings))]
public class LAMEEncoderVBR : LAMEEncoder
{
private LAMEEncoderVBRSettings m_settings;
public override AudioEncoderSettings Settings
{
get
{
return m_settings;
}
}
public LAMEEncoderVBR(string path, Stream IO, AudioEncoderSettings settings)
: base(path, IO, settings)
{
}
public LAMEEncoderVBR(string path, AudioEncoderSettings settings)
: base(path, null, settings)
{
}
protected override BE_CONFIG MakeConfig()
{
BE_CONFIG Mp3Config = new BE_CONFIG(Settings.PCM, 0, (uint)m_settings.Quality);
Mp3Config.format.lhv1.bWriteVBRHeader = 1;
Mp3Config.format.lhv1.nMode = MpegMode.JOINT_STEREO;
Mp3Config.format.lhv1.bEnableVBR = 1;
Mp3Config.format.lhv1.nVBRQuality = 9 - m_settings.EncoderModeIndex;
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;
using System.ComponentModel;
namespace CUETools.Codecs.LAME
{
public class LAMEEncoderVBRSettings : AudioEncoderSettings
{
public override Type EncoderType => typeof(LAMEEncoderVBR);
[DefaultValue(LAMEEncoderVBRProcessingQuality.Normal)]
public LAMEEncoderVBRProcessingQuality Quality { get; set; }
public LAMEEncoderVBRSettings()
: base("V9 V8 V7 V6 V5 V4 V3 V2 V1 V0", "V2")
{
}
}
}