WMADecoder now supports multichannel audio

Better WAVE_FORMAT_EXTENSIBLE & channelMask support in WMA & WAV
This commit is contained in:
Grigory Chudov
2013-04-14 16:54:19 -04:00
parent 40f6482077
commit 563f066646
9 changed files with 159 additions and 87 deletions

View File

@@ -60,6 +60,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="IStreamWrapper.cs" /> <Compile Include="IStreamWrapper.cs" />
<Compile Include="WaveFormatExtensible.cs" />
<Compile Include="WMAReader.cs" /> <Compile Include="WMAReader.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs"> <Compile Include="Properties\Resources.Designer.cs">

View File

@@ -70,7 +70,7 @@ namespace CUETools.Codecs.WMA
for (int dwIndex = 0; dwIndex < dwStreamCount; dwIndex++) for (int dwIndex = 0; dwIndex < dwStreamCount; dwIndex++)
{ {
IWMStreamConfig pConfig = null; IWMStreamConfig pConfig = null;
pProfile.GetStream(0, out pConfig); pProfile.GetStream(dwIndex, out pConfig);
try try
{ {
Guid guid; Guid guid;
@@ -84,14 +84,15 @@ namespace CUETools.Codecs.WMA
var pIWMMediaProps = pConfig as IWMMediaProps; var pIWMMediaProps = pConfig as IWMMediaProps;
int cbType = 0; int cbType = 0;
pIWMMediaProps.GetMediaType(null, ref cbType); pIWMMediaProps.GetMediaType(null, ref cbType);
var mt = new AMMediaType(); var pMediaType = new AMMediaType();
mt.formatSize = cbType; pMediaType.formatSize = cbType;
pIWMMediaProps.GetMediaType(mt, ref cbType); pIWMMediaProps.GetMediaType(pMediaType, ref cbType);
if (mt.formatType != FormatType.WaveEx) if (pMediaType.formatType != FormatType.WaveEx)
continue; continue;
if (mt.subType != MediaSubType.WMAudio_Lossless) if (pMediaType.subType != MediaSubType.WMAudio_Lossless)
continue; continue;
m_wStreamNum = wStreamNum; m_wStreamNum = wStreamNum;
pcm = WaveFormatExtensible.FromMediaType(pMediaType).GetConfig();
break; break;
} }
finally finally
@@ -110,7 +111,7 @@ namespace CUETools.Codecs.WMA
m_syncReader.GetOutputNumberForStream(m_wStreamNum, out m_dwAudioOutputNum); m_syncReader.GetOutputNumberForStream(m_wStreamNum, out m_dwAudioOutputNum);
IWMOutputMediaProps pProps; IWMOutputMediaProps pProps;
m_syncReader.GetOutputProps(m_dwAudioOutputNum, out pProps); m_syncReader.GetOutputProps(m_dwAudioOutputNum, out pProps);
var m_pWfx = new WaveFormatEx();
try try
{ {
StringBuilder sName = null; StringBuilder sName = null;
@@ -129,6 +130,12 @@ namespace CUETools.Codecs.WMA
sName = new StringBuilder(iName); sName = new StringBuilder(iName);
pProps.GetConnectionName(sName, ref iName); pProps.GetConnectionName(sName, ref iName);
if (pcm.ChannelCount > 2)
{
m_syncReader.SetOutputSetting(m_dwAudioOutputNum, Constants.g_wszEnableDiscreteOutput, AttrDataType.BOOL, new byte[] { 1, 0, 0, 0 }, 4);
m_syncReader.SetOutputSetting(m_dwAudioOutputNum, Constants.g_wszSpeakerConfig, AttrDataType.DWORD, new byte[] { 0, 0, 0, 0 }, 4);
}
pMediaType = new AMMediaType(); pMediaType = new AMMediaType();
pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType)); pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType));
@@ -143,7 +150,15 @@ namespace CUETools.Codecs.WMA
throw new Exception("not Audio"); throw new Exception("not Audio");
if (FormatType.WaveEx != pMediaType.formatType) if (FormatType.WaveEx != pMediaType.formatType)
throw new Exception("not WaveEx"); throw new Exception("not WaveEx");
Marshal.PtrToStructure(pMediaType.formatPtr, m_pWfx); var wfe = new WaveFormatExtensible(pcm);
Marshal.FreeCoTaskMem(pMediaType.formatPtr);
pMediaType.formatPtr = IntPtr.Zero;
pMediaType.formatSize = 0;
pMediaType.formatPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(wfe));
pMediaType.formatSize = Marshal.SizeOf(wfe);
Marshal.StructureToPtr(wfe, pMediaType.formatPtr, false);
pProps.SetMediaType(pMediaType);
m_syncReader.SetOutputProps(m_dwAudioOutputNum, pProps);
} }
finally finally
{ {
@@ -202,8 +217,6 @@ namespace CUETools.Codecs.WMA
//{ //{
//} //}
pcm = new AudioPCMConfig(m_pWfx.wBitsPerSample, m_pWfx.nChannels, m_pWfx.nSamplesPerSec);
//int cbMax; //int cbMax;
//m_syncReader.GetMaxOutputSampleSize(m_dwAudioOutputNum, out cbMax); //m_syncReader.GetMaxOutputSampleSize(m_dwAudioOutputNum, out cbMax);
} }

View File

@@ -134,19 +134,17 @@ namespace CUETools.Codecs.WMA
{ {
if (pMediaType.majorType == MediaType.Audio && pMediaType.formatType == FormatType.WaveEx && pMediaType.subType == m_subType) if (pMediaType.majorType == MediaType.Audio && pMediaType.formatType == FormatType.WaveEx && pMediaType.subType == m_subType)
{ {
WaveFormatEx pWfx = new WaveFormatEx(); var pcm = WaveFormatExtensible.FromMediaType(pMediaType).GetConfig();
Marshal.PtrToStructure(pMediaType.formatPtr, pWfx); if (PCM == null || (pcm.ChannelCount == PCM.ChannelCount && pcm.SampleRate == PCM.SampleRate && pcm.BitsPerSample >= PCM.BitsPerSample))
var info = new WMAFormatInfo() yield return new WMAFormatInfo()
{ {
codec = iCodec, codec = iCodec,
codecName = codecName.ToString(), codecName = codecName.ToString(),
format = iFormat, format = iFormat,
formatName = szDesc.ToString(), formatName = szDesc.ToString(),
subType = pMediaType.subType, subType = pMediaType.subType,
pcm = new AudioPCMConfig(pWfx.wBitsPerSample, pWfx.nChannels, pWfx.nSamplesPerSec) pcm = pcm
}; };
if (PCM == null || (pWfx.nChannels == PCM.ChannelCount && pWfx.wBitsPerSample >= PCM.BitsPerSample && pWfx.nSamplesPerSec == PCM.SampleRate))
yield return info;
} }
} }
finally finally
@@ -268,27 +266,6 @@ namespace CUETools.Codecs.WMA
private bool writingBegan = false; private bool writingBegan = false;
private long sampleCount, finalSampleCount; private long sampleCount, finalSampleCount;
const ushort WAVE_FORMAT_EXTENSIBLE = 0xFFFE;
const ushort WAVE_FORMAT_PCM = 1;
/// <summary>
/// From WAVEFORMATEXTENSIBLE
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct WaveFormatExtensible
{
public short wFormatTag; /* format type */
public short nChannels; /* number of channels (i.e. mono, stereo, etc.) */
public int nSamplesPerSec; /* sample rate */
public int nAvgBytesPerSec; /* for buffer estimation */
public short nBlockAlign; /* block size of data */
public short wBitsPerSample;
public short cbSize;
public short wValidBitsPerSample;
public int dwChannelMask;
public Guid SubFormat;
}
public long FinalSampleCount public long FinalSampleCount
{ {
set set
@@ -335,27 +312,7 @@ namespace CUETools.Codecs.WMA
pInput.GetMediaType(pMediaType, ref cbType); pInput.GetMediaType(pMediaType, ref cbType);
try try
{ {
var wfe = new WaveFormatExtensible(); var wfe = new WaveFormatExtensible(m_settings.PCM);
wfe.nChannels = (short)m_settings.PCM.ChannelCount;
wfe.nSamplesPerSec = m_settings.PCM.SampleRate;
wfe.nBlockAlign = (short)m_settings.PCM.BlockAlign;
wfe.wBitsPerSample = (short)m_settings.PCM.BitsPerSample;
wfe.nAvgBytesPerSec = wfe.nSamplesPerSec * wfe.nBlockAlign;
if ((m_settings.PCM.BitsPerSample == 8 || m_settings.PCM.BitsPerSample == 16 || m_settings.PCM.BitsPerSample == 24) &&
(m_settings.PCM.ChannelCount == 1 || m_settings.PCM.ChannelCount == 2))
{
wfe.wFormatTag = unchecked((short)WAVE_FORMAT_PCM);
wfe.cbSize = 0;
}
else
{
wfe.wFormatTag = unchecked((short)WAVE_FORMAT_EXTENSIBLE);
wfe.cbSize = 22;
wfe.wValidBitsPerSample = wfe.wBitsPerSample;
wfe.nBlockAlign = (short)((wfe.wBitsPerSample / 8) * wfe.nChannels);
wfe.dwChannelMask = (int)m_settings.PCM.ChannelMask;
wfe.SubFormat = MediaSubType.PCM;
}
Marshal.FreeCoTaskMem(pMediaType.formatPtr); Marshal.FreeCoTaskMem(pMediaType.formatPtr);
pMediaType.formatPtr = IntPtr.Zero; pMediaType.formatPtr = IntPtr.Zero;
pMediaType.formatSize = 0; pMediaType.formatSize = 0;

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using WindowsMediaLib.Defs;
namespace CUETools.Codecs.WMA
{
/// <summary>
/// From WAVEFORMATEXTENSIBLE
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct WaveFormatExtensible
{
public const ushort WAVE_FORMAT_EXTENSIBLE = 0xFFFE;
public const ushort WAVE_FORMAT_PCM = 1;
public WaveFormatExtensible(AudioPCMConfig pcm)
{
this.nChannels = (short)pcm.ChannelCount;
this.nSamplesPerSec = pcm.SampleRate;
this.nBlockAlign = (short)pcm.BlockAlign;
this.wBitsPerSample = (short)pcm.BitsPerSample;
if ((pcm.BitsPerSample == 8 || pcm.BitsPerSample == 16 || pcm.BitsPerSample == 24) &&
(pcm.ChannelCount == 1 || pcm.ChannelCount == 2))
{
this.wFormatTag = unchecked((short)WAVE_FORMAT_PCM);
this.cbSize = 0;
// just to make compiler happy
this.wValidBitsPerSample = 0;
this.dwChannelMask = 0;
this.SubFormat = Guid.Empty;
}
else
{
this.wFormatTag = unchecked((short)WAVE_FORMAT_EXTENSIBLE);
this.cbSize = 22;
this.wValidBitsPerSample = this.wBitsPerSample;
this.nBlockAlign = (short)((this.wBitsPerSample / 8) * this.nChannels);
this.dwChannelMask = (int)pcm.ChannelMask;
this.SubFormat = MediaSubType.PCM;
}
this.nAvgBytesPerSec = this.nSamplesPerSec * this.nBlockAlign;
}
public static WaveFormatExtensible FromMediaType(AMMediaType pMediaType)
{
if (MediaType.Audio != pMediaType.majorType)
throw new Exception("not Audio");
if (FormatType.WaveEx != pMediaType.formatType || pMediaType.formatSize < 18)
throw new Exception("not WaveEx");
WaveFormatEx pWfx = new WaveFormatEx();
Marshal.PtrToStructure(pMediaType.formatPtr, pWfx);
if (pWfx.wFormatTag == unchecked((short)WAVE_FORMAT_EXTENSIBLE) && pWfx.cbSize >= 22)
{
var pWfe = new WaveFormatExtensible();
Marshal.PtrToStructure(pMediaType.formatPtr, pWfe);
return pWfe;
}
return new WaveFormatExtensible() {
nChannels = pWfx.nChannels,
nSamplesPerSec = pWfx.nSamplesPerSec,
nBlockAlign = pWfx.nBlockAlign,
wBitsPerSample = pWfx.wBitsPerSample,
nAvgBytesPerSec = pWfx.nAvgBytesPerSec,
wFormatTag = pWfx.wFormatTag,
cbSize = 0
};
}
public AudioPCMConfig GetConfig()
{
return new AudioPCMConfig(
wBitsPerSample,
nChannels,
nSamplesPerSec,
(AudioPCMConfig.SpeakerConfig)(wFormatTag == unchecked((short)WAVE_FORMAT_EXTENSIBLE) && cbSize >= 22 ? dwChannelMask : 0));
}
public short wFormatTag; /* format type */
public short nChannels; /* number of channels (i.e. mono, stereo, etc.) */
public int nSamplesPerSec; /* sample rate */
public int nAvgBytesPerSec; /* for buffer estimation */
public short nBlockAlign; /* block size of data */
public short wBitsPerSample;
public short cbSize;
public short wValidBitsPerSample;
public int dwChannelMask;
public Guid SubFormat;
}
}

View File

@@ -24,6 +24,7 @@
SPEAKER_TOP_BACK_CENTER = 0x10000, SPEAKER_TOP_BACK_CENTER = 0x10000,
SPEAKER_TOP_BACK_RIGHT = 0x20000, SPEAKER_TOP_BACK_RIGHT = 0x20000,
DIRECTOUT = 0,
KSAUDIO_SPEAKER_MONO = (SPEAKER_FRONT_CENTER), KSAUDIO_SPEAKER_MONO = (SPEAKER_FRONT_CENTER),
KSAUDIO_SPEAKER_STEREO = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT), KSAUDIO_SPEAKER_STEREO = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT),
KSAUDIO_SPEAKER_QUAD = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT), KSAUDIO_SPEAKER_QUAD = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT),
@@ -45,9 +46,9 @@
public int BlockAlign { get { return _channelCount * ((_bitsPerSample + 7) / 8); } } public int BlockAlign { get { return _channelCount * ((_bitsPerSample + 7) / 8); } }
public SpeakerConfig ChannelMask { get { return _channelMask; } } public SpeakerConfig ChannelMask { get { return _channelMask; } }
public bool IsRedBook { get { return _bitsPerSample == 16 && _channelCount == 2 && _sampleRate == 44100; } } public bool IsRedBook { get { return _bitsPerSample == 16 && _channelCount == 2 && _sampleRate == 44100; } }
public SpeakerConfig GetDefaultChannelMask() public static SpeakerConfig GetDefaultChannelMask(int channelCount)
{ {
switch (_channelCount) switch (channelCount)
{ {
case 1: case 1:
return SpeakerConfig.KSAUDIO_SPEAKER_MONO; return SpeakerConfig.KSAUDIO_SPEAKER_MONO;
@@ -68,15 +69,15 @@
case 8: case 8:
return SpeakerConfig.KSAUDIO_SPEAKER_7POINT1_SURROUND; return SpeakerConfig.KSAUDIO_SPEAKER_7POINT1_SURROUND;
} }
return (SpeakerConfig)((1 << _channelCount) - 1); return (SpeakerConfig)((1 << channelCount) - 1);
} }
public AudioPCMConfig(int bitsPerSample, int channelCount, int sampleRate) public AudioPCMConfig(int bitsPerSample, int channelCount, int sampleRate, SpeakerConfig channelMask = SpeakerConfig.DIRECTOUT)
{ {
_bitsPerSample = bitsPerSample; _bitsPerSample = bitsPerSample;
_channelCount = channelCount; _channelCount = channelCount;
_sampleRate = sampleRate; _sampleRate = sampleRate;
_channelMask = GetDefaultChannelMask(); _channelMask = channelMask == 0 ? GetDefaultChannelMask(channelCount) : channelMask;
} }
} }
} }

View File

@@ -177,13 +177,14 @@ namespace CUETools.Codecs
_br.ReadInt32(); // bytes per second _br.ReadInt32(); // bytes per second
int _blockAlign = _br.ReadInt16(); int _blockAlign = _br.ReadInt16();
int _bitsPerSample = _br.ReadInt16(); int _bitsPerSample = _br.ReadInt16();
int _channelMask = 0;
pos += 16; pos += 16;
if (fmtTag == 0xFFFEU && ckSize >= 34) // WAVE_FORMAT_EXTENSIBLE if (fmtTag == 0xFFFEU && ckSize >= 34) // WAVE_FORMAT_EXTENSIBLE
{ {
_br.ReadInt16(); // CbSize _br.ReadInt16(); // CbSize
_br.ReadInt16(); // ValidBitsPerSample _br.ReadInt16(); // ValidBitsPerSample
int channelMask = _br.ReadInt32(); _channelMask = _br.ReadInt32();
fmtTag = _br.ReadUInt16(); fmtTag = _br.ReadUInt16();
pos += 10; pos += 10;
} }
@@ -191,7 +192,7 @@ namespace CUETools.Codecs
if (fmtTag != 1) // WAVE_FORMAT_PCM if (fmtTag != 1) // WAVE_FORMAT_PCM
throw new Exception("WAVE format tag not WAVE_FORMAT_PCM."); throw new Exception("WAVE format tag not WAVE_FORMAT_PCM.");
pcm = new AudioPCMConfig(_bitsPerSample, _channelCount, _sampleRate); pcm = new AudioPCMConfig(_bitsPerSample, _channelCount, _sampleRate, (AudioPCMConfig.SpeakerConfig)_channelMask);
if (pcm.BlockAlign != _blockAlign) if (pcm.BlockAlign != _blockAlign)
throw new Exception("WAVE has strange BlockAlign"); throw new Exception("WAVE has strange BlockAlign");
} }

View File

@@ -75,7 +75,7 @@ namespace CUETools.Codecs
const uint fccFormat = 0x20746D66; const uint fccFormat = 0x20746D66;
const uint fccData = 0x61746164; const uint fccData = 0x61746164;
bool wavex = Settings.PCM.BitsPerSample != 16 && Settings.PCM.BitsPerSample != 24; bool wavex = (Settings.PCM.BitsPerSample != 16 && Settings.PCM.BitsPerSample != 24) || Settings.PCM.ChannelMask != AudioPCMConfig.GetDefaultChannelMask(Settings.PCM.ChannelCount);
hdrLen += 36 + (wavex ? 24 : 0) + 8; hdrLen += 36 + (wavex ? 24 : 0) + 8;
@@ -83,7 +83,10 @@ namespace CUETools.Codecs
uint dataLenPadded = dataLen + (dataLen & 1); uint dataLenPadded = dataLen + (dataLen & 1);
_bw.Write(fccRIFF); _bw.Write(fccRIFF);
_bw.Write((uint)(dataLenPadded + hdrLen - 8)); if (_finalSampleCount <= 0)
_bw.Write((uint)0xffffffff);
else
_bw.Write((uint)(dataLenPadded + hdrLen - 8));
_bw.Write(fccWAVE); _bw.Write(fccWAVE);
_bw.Write(fccFormat); _bw.Write(fccFormat);
if (wavex) if (wavex)
@@ -105,8 +108,8 @@ namespace CUETools.Codecs
{ {
_bw.Write((ushort)22); // length of WAVEX structure _bw.Write((ushort)22); // length of WAVEX structure
_bw.Write((ushort)Settings.PCM.BitsPerSample); _bw.Write((ushort)Settings.PCM.BitsPerSample);
_bw.Write((uint)3); // speaker positions (3 == stereo) _bw.Write((uint)Settings.PCM.ChannelMask);
_bw.Write((ushort)1); // PCM _bw.Write((ushort)1); // PCM Guid
_bw.Write((ushort)0); _bw.Write((ushort)0);
_bw.Write((ushort)0); _bw.Write((ushort)0);
_bw.Write((ushort)0x10); _bw.Write((ushort)0x10);
@@ -130,7 +133,10 @@ namespace CUETools.Codecs
} }
_bw.Write(fccData); _bw.Write(fccData);
_bw.Write(dataLen); if (_finalSampleCount <= 0)
_bw.Write((uint)0xffffffff);
else
_bw.Write(dataLen);
_headersWritten = true; _headersWritten = true;
} }
@@ -139,19 +145,19 @@ namespace CUETools.Codecs
{ {
if (_finalSampleCount <= 0) if (_finalSampleCount <= 0)
{ {
const long maxFileSize = 0x7FFFFFFEL;
long dataLen = _sampleLen * Settings.PCM.BlockAlign; long dataLen = _sampleLen * Settings.PCM.BlockAlign;
if ((dataLen & 1) == 1)
_bw.Write((byte)0);
if (dataLen + hdrLen > maxFileSize)
dataLen = ((maxFileSize - hdrLen) / Settings.PCM.BlockAlign) * Settings.PCM.BlockAlign;
long dataLenPadded = dataLen + (dataLen & 1); long dataLenPadded = dataLen + (dataLen & 1);
if (dataLenPadded + hdrLen - 8 < 0xffffffff)
{
if ((dataLen & 1) == 1)
_bw.Write((byte)0);
_bw.Seek(4, SeekOrigin.Begin); _bw.Seek(4, SeekOrigin.Begin);
_bw.Write((uint)(dataLenPadded + hdrLen - 8)); _bw.Write((uint)(dataLenPadded + hdrLen - 8));
_bw.Seek((int)hdrLen - 4, SeekOrigin.Begin); _bw.Seek((int)hdrLen - 4, SeekOrigin.Begin);
_bw.Write((uint)dataLen); _bw.Write((uint)dataLen);
}
} }
_bw.Close(); _bw.Close();

View File

@@ -92,7 +92,7 @@ namespace LossyWAVSharp
{ {
WAVReader audioSource = new WAVReader(sourceFile, (sourceFile == "-" ? Console.OpenStandardInput() : null)); WAVReader audioSource = new WAVReader(sourceFile, (sourceFile == "-" ? Console.OpenStandardInput() : null));
if (sourceFile == "-" && stdinName != null) sourceFile = stdinName; if (sourceFile == "-" && stdinName != null) sourceFile = stdinName;
AudioPCMConfig pcm = outputBPS == 0 ? audioSource.PCM : new AudioPCMConfig(outputBPS, audioSource.PCM.ChannelCount, audioSource.PCM.SampleRate); AudioPCMConfig pcm = outputBPS == 0 ? audioSource.PCM : new AudioPCMConfig(outputBPS, audioSource.PCM.ChannelCount, audioSource.PCM.SampleRate, audioSource.PCM.ChannelMask);
WAVWriter audioDest = new WAVWriter(Path.ChangeExtension(sourceFile, ".lossy.wav"), toStdout ? Console.OpenStandardOutput() : null, new WAVWriterSettings(pcm)); WAVWriter audioDest = new WAVWriter(Path.ChangeExtension(sourceFile, ".lossy.wav"), toStdout ? Console.OpenStandardOutput() : null, new WAVWriterSettings(pcm));
WAVWriter lwcdfDest = createCorrection ? new WAVWriter(Path.ChangeExtension(sourceFile, ".lwcdf.wav"), null, new WAVWriterSettings(audioSource.PCM)) : null; WAVWriter lwcdfDest = createCorrection ? new WAVWriter(Path.ChangeExtension(sourceFile, ".lwcdf.wav"), null, new WAVWriterSettings(audioSource.PCM)) : null;
LossyWAVWriter lossyWAV = new LossyWAVWriter(audioDest, lwcdfDest, quality, new AudioEncoderSettings(audioSource.PCM)); LossyWAVWriter lossyWAV = new LossyWAVWriter(audioDest, lwcdfDest, quality, new AudioEncoderSettings(audioSource.PCM));

View File

@@ -473,6 +473,8 @@ return processor.Go();
{ {
using (TextReader reader = new StringReader(settings)) using (TextReader reader = new StringReader(settings))
encoder.settings = encoder.settingsSerializer.Deserialize(reader) as AudioEncoderSettings; encoder.settings = encoder.settingsSerializer.Deserialize(reader) as AudioEncoderSettings;
if (encoder.settings is UserDefinedEncoderSettings && (encoder.settings as UserDefinedEncoderSettings).Path == "")
encoders.Remove(encoder);
} }
catch catch
{ {