mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
Removed AudioEncoderSettings/AudioDecoderSettings classes, all of their functionality is now in IAudioEncoderSettings/IAudioDecoderSettings interfaces.
This commit is contained in:
@@ -1,389 +1,375 @@
|
||||
/**
|
||||
* CUETools.WMA: WMA audio decoder
|
||||
* Copyright (c) 20139 Grigory Chudov
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using WindowsMediaLib;
|
||||
using WindowsMediaLib.Defs;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CUETools.Codecs.WMA
|
||||
{
|
||||
public class DecoderSettings : AudioDecoderSettings
|
||||
{
|
||||
public override string Extension => "wma";
|
||||
|
||||
public override string Name => "windows";
|
||||
|
||||
public override Type DecoderType => typeof(AudioDecoder);
|
||||
|
||||
public override int Priority => 2;
|
||||
|
||||
public DecoderSettings() : base() { }
|
||||
}
|
||||
|
||||
public class AudioDecoder : IAudioSource
|
||||
{
|
||||
IWMSyncReader m_syncReader;
|
||||
INSSBuffer m_pSample;
|
||||
int m_pSampleOffset = 0, m_pSampleSize = 0;
|
||||
short m_wStreamNum = -1;
|
||||
int m_dwAudioOutputNum = -1;
|
||||
|
||||
AudioPCMConfig pcm;
|
||||
|
||||
long m_sampleCount = -1, m_sampleOffset = 0;
|
||||
|
||||
string m_path;
|
||||
Stream m_IO;
|
||||
StreamWrapper m_streamWrapper;
|
||||
|
||||
public AudioDecoder(string path, Stream IO)
|
||||
{
|
||||
m_path = path;
|
||||
isValid(path);
|
||||
bool pfIsProtected;
|
||||
WMUtils.WMIsContentProtected(path, out pfIsProtected);
|
||||
if (pfIsProtected)
|
||||
throw new Exception("DRM present");
|
||||
WMUtils.WMCreateSyncReader(IntPtr.Zero, Rights.None, out m_syncReader);
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
m_IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000);
|
||||
m_streamWrapper = new StreamWrapper(m_IO);
|
||||
m_syncReader.OpenStream(m_streamWrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_syncReader.Open(path);
|
||||
}
|
||||
var pProfile = (m_syncReader as IWMProfile);
|
||||
int dwStreamCount;
|
||||
pProfile.GetStreamCount(out dwStreamCount);
|
||||
for (int dwIndex = 0; dwIndex < dwStreamCount; dwIndex++)
|
||||
{
|
||||
IWMStreamConfig pConfig = null;
|
||||
pProfile.GetStream(dwIndex, out pConfig);
|
||||
try
|
||||
{
|
||||
Guid guid;
|
||||
pConfig.GetStreamType(out guid);
|
||||
if (MediaType.Audio != guid)
|
||||
continue;
|
||||
short wStreamNum;
|
||||
pConfig.GetStreamNumber(out wStreamNum);
|
||||
int dwBitrate = -1;
|
||||
pConfig.GetBitrate(out dwBitrate);
|
||||
var pIWMMediaProps = pConfig as IWMMediaProps;
|
||||
int cbType = 0;
|
||||
pIWMMediaProps.GetMediaType(null, ref cbType);
|
||||
var pMediaType = new AMMediaType();
|
||||
pMediaType.formatSize = cbType;
|
||||
pIWMMediaProps.GetMediaType(pMediaType, ref cbType);
|
||||
if (pMediaType.formatType != FormatType.WaveEx)
|
||||
continue;
|
||||
if (pMediaType.subType != MediaSubType.WMAudio_Lossless)
|
||||
continue;
|
||||
m_wStreamNum = wStreamNum;
|
||||
pcm = WaveFormatExtensible.FromMediaType(pMediaType).GetConfig();
|
||||
break;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pConfig);
|
||||
}
|
||||
}
|
||||
if (m_wStreamNum == -1)
|
||||
throw new Exception("No WMA lossless streams found");
|
||||
|
||||
m_syncReader.SetReadStreamSamples(m_wStreamNum, false);
|
||||
bool pfCompressed;
|
||||
m_syncReader.GetReadStreamSamples(m_wStreamNum, out pfCompressed);
|
||||
if (pfCompressed)
|
||||
throw new Exception("doesn't decompress");
|
||||
m_syncReader.GetOutputNumberForStream(m_wStreamNum, out m_dwAudioOutputNum);
|
||||
IWMOutputMediaProps pProps;
|
||||
m_syncReader.GetOutputProps(m_dwAudioOutputNum, out pProps);
|
||||
|
||||
try
|
||||
{
|
||||
StringBuilder sName = null;
|
||||
AMMediaType pMediaType = null;
|
||||
int cbType = 0;
|
||||
|
||||
cbType = 0;
|
||||
pMediaType = null;
|
||||
pProps.GetMediaType(pMediaType, ref cbType);
|
||||
|
||||
// Get the name of the output we'll be using
|
||||
sName = null;
|
||||
short iName = 0;
|
||||
pProps.GetConnectionName(sName, ref iName);
|
||||
|
||||
sName = new StringBuilder(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.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType));
|
||||
|
||||
//
|
||||
// Get the value for MediaType
|
||||
//
|
||||
pProps.GetMediaType(pMediaType, ref cbType);
|
||||
|
||||
try
|
||||
{
|
||||
if (MediaType.Audio != pMediaType.majorType)
|
||||
throw new Exception("not Audio");
|
||||
if (FormatType.WaveEx != pMediaType.formatType)
|
||||
throw new Exception("not WaveEx");
|
||||
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
|
||||
{
|
||||
WMUtils.FreeWMMediaType(pMediaType);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pProps);
|
||||
}
|
||||
|
||||
//try
|
||||
//{
|
||||
// AttrDataType wmtType;
|
||||
// short cbLength = 0;
|
||||
// short wAnyStream = 0;
|
||||
// var pHeaderInfo = m_syncReader as IWMHeaderInfo;
|
||||
// pHeaderInfo.GetAttributeByName(ref wAnyStream, Constants.g_wszWMDuration, out wmtType, null, ref cbLength);
|
||||
// var pbValue = new byte[cbLength];
|
||||
// pHeaderInfo.GetAttributeByName(ref wAnyStream, Constants.g_wszWMDuration, out wmtType, pbValue, ref cbLength);
|
||||
// var m_cnsFileDuration = BitConverter.ToInt64(pbValue, 0);
|
||||
// _sampleCount = m_cnsFileDuration * m_pWfx.nSamplesPerSec / 10000000;
|
||||
// // NOT ACCURATE ENOUGH (~1ms precision observed)
|
||||
//}
|
||||
//catch (COMException)
|
||||
//{
|
||||
//}
|
||||
|
||||
//try
|
||||
//{
|
||||
// var pHeaderInfo = m_syncReader as IWMHeaderInfo2;
|
||||
// int nCodec;
|
||||
// pHeaderInfo.GetCodecInfoCount(out nCodec);
|
||||
// for (int wIndex = 0; wIndex < nCodec; wIndex++)
|
||||
// {
|
||||
// CodecInfoType enumCodecType;
|
||||
// short cchName = 0;
|
||||
// short cchDescription = 0;
|
||||
// short cbCodecInfo = 0;
|
||||
// pHeaderInfo.GetCodecInfo(wIndex, ref cchName, null,
|
||||
// ref cchDescription, null, out enumCodecType,
|
||||
// ref cbCodecInfo, null);
|
||||
// var pwszName = new StringBuilder(cchName);
|
||||
// var pwszDescription = new StringBuilder(cchDescription);
|
||||
// var pbCodecInfo = new byte[cbCodecInfo];
|
||||
// pHeaderInfo.GetCodecInfo(wIndex, ref cchName, pwszName,
|
||||
// ref cchDescription, pwszDescription, out enumCodecType,
|
||||
// ref cbCodecInfo, pbCodecInfo);
|
||||
// if (enumCodecType == CodecInfoType.Audio)
|
||||
// {
|
||||
// // pbCodecInfo = {99,1} ??/
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//catch (COMException)
|
||||
//{
|
||||
//}
|
||||
|
||||
//int cbMax;
|
||||
//m_syncReader.GetMaxOutputSampleSize(m_dwAudioOutputNum, out cbMax);
|
||||
}
|
||||
|
||||
public AudioDecoderSettings Settings { get { return null; } }
|
||||
|
||||
public void isValid(string filename)
|
||||
{
|
||||
int pdwDataSize = 0;
|
||||
WMUtils.WMValidateData(null, ref pdwDataSize);
|
||||
byte[] data = new byte[pdwDataSize];
|
||||
using (FileStream s = new FileStream(filename, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
if (s.Read(data, 0, pdwDataSize) < pdwDataSize)
|
||||
throw new Exception("partial read"); // TODO
|
||||
}
|
||||
WMUtils.WMValidateData(data, ref pdwDataSize);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
//if (m_streamWrapper != null)
|
||||
// m_streamWrapper.Close();
|
||||
if (m_IO != null)
|
||||
m_IO.Close();
|
||||
if (m_pSample != null)
|
||||
Marshal.ReleaseComObject(m_pSample);
|
||||
if (m_syncReader != null)
|
||||
{
|
||||
m_syncReader.Close();
|
||||
Marshal.ReleaseComObject(m_syncReader);
|
||||
}
|
||||
m_IO = null;
|
||||
m_pSample = null;
|
||||
m_syncReader = null;
|
||||
}
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
public long Remaining
|
||||
{
|
||||
get
|
||||
{
|
||||
return Length - Position;
|
||||
}
|
||||
}
|
||||
|
||||
public long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_sampleOffset / PCM.BlockAlign;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (m_sampleCount < 0 || value > m_sampleCount)
|
||||
throw new Exception("seeking past end of stream");
|
||||
if (value < Position)
|
||||
throw new NotSupportedException();
|
||||
if (value < Position)
|
||||
throw new Exception("cannot seek backwards");
|
||||
var buff = new AudioBuffer(this, 0x10000);
|
||||
while (value > Position && Read(buff, (int)Math.Min(Int32.MaxValue, value - Position)) != 0)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public AudioPCMConfig PCM
|
||||
{
|
||||
get
|
||||
{
|
||||
return pcm;
|
||||
}
|
||||
}
|
||||
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
}
|
||||
|
||||
public int Read(AudioBuffer buff, int maxLength)
|
||||
{
|
||||
buff.Prepare(this, maxLength);
|
||||
|
||||
int buff_offset = 0;
|
||||
int buff_size = buff.ByteLength;
|
||||
|
||||
while (m_pSampleSize < buff_size)
|
||||
{
|
||||
if (m_pSampleSize > 0)
|
||||
{
|
||||
IntPtr pdwBuffer;
|
||||
m_pSample.GetBuffer(out pdwBuffer);
|
||||
Marshal.Copy((IntPtr)(pdwBuffer.ToInt64() + m_pSampleOffset), buff.Bytes, buff_offset, m_pSampleSize);
|
||||
buff_size -= m_pSampleSize;
|
||||
buff_offset += m_pSampleSize;
|
||||
m_sampleOffset += m_pSampleSize;
|
||||
m_pSampleSize = 0;
|
||||
Marshal.ReleaseComObject(m_pSample);
|
||||
m_pSample = null;
|
||||
}
|
||||
|
||||
long cnsSampleTime;
|
||||
long cnsDuration;
|
||||
SampleFlag flags;
|
||||
int dwOutputNum;
|
||||
short wStreamNum;
|
||||
try
|
||||
{
|
||||
m_syncReader.GetNextSample(m_wStreamNum, out m_pSample, out cnsSampleTime, out cnsDuration, out flags, out dwOutputNum, out wStreamNum);
|
||||
}
|
||||
catch (COMException ex)
|
||||
{
|
||||
// EOF
|
||||
if (ex.ErrorCode == NSResults.E_NO_MORE_SAMPLES)
|
||||
{
|
||||
if ((m_sampleOffset % PCM.BlockAlign) != 0)
|
||||
throw new Exception("(m_sampleOffset % PCM.BlockAlign) != 0");
|
||||
m_sampleCount = m_sampleOffset / PCM.BlockAlign;
|
||||
if ((buff_offset % PCM.BlockAlign) != 0)
|
||||
throw new Exception("(buff_offset % PCM.BlockAlign) != 0");
|
||||
return buff.Length = buff_offset / PCM.BlockAlign;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
//if (dwOutputNum != m_dwAudioOutputNum || wStreamNum != m_wStreamNum)
|
||||
//{
|
||||
//}
|
||||
m_pSampleOffset = 0;
|
||||
m_pSample.GetLength(out m_pSampleSize);
|
||||
}
|
||||
|
||||
if (buff_size > 0)
|
||||
{
|
||||
IntPtr pdwBuffer;
|
||||
m_pSample.GetBuffer(out pdwBuffer);
|
||||
Marshal.Copy((IntPtr)(pdwBuffer.ToInt64() + m_pSampleOffset), buff.Bytes, buff_offset, buff_size);
|
||||
m_pSampleOffset += buff_size;
|
||||
m_pSampleSize -= buff_size;
|
||||
m_sampleOffset += buff_size;
|
||||
buff_offset += buff_size;
|
||||
buff_size = 0;
|
||||
}
|
||||
if ((buff_offset % PCM.BlockAlign) != 0)
|
||||
throw new Exception("(buff_offset % PCM.BlockAlign) != 0");
|
||||
return buff.Length = buff_offset / PCM.BlockAlign;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* CUETools.WMA: WMA audio decoder
|
||||
* Copyright (c) 20139 Grigory Chudov
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using WindowsMediaLib;
|
||||
using WindowsMediaLib.Defs;
|
||||
|
||||
namespace CUETools.Codecs.WMA
|
||||
{
|
||||
public class AudioDecoder : IAudioSource
|
||||
{
|
||||
IWMSyncReader m_syncReader;
|
||||
INSSBuffer m_pSample;
|
||||
int m_pSampleOffset = 0, m_pSampleSize = 0;
|
||||
short m_wStreamNum = -1;
|
||||
int m_dwAudioOutputNum = -1;
|
||||
|
||||
AudioPCMConfig pcm;
|
||||
|
||||
long m_sampleCount = -1, m_sampleOffset = 0;
|
||||
|
||||
string m_path;
|
||||
Stream m_IO;
|
||||
StreamWrapper m_streamWrapper;
|
||||
|
||||
public AudioDecoder(string path, Stream IO)
|
||||
{
|
||||
m_path = path;
|
||||
isValid(path);
|
||||
bool pfIsProtected;
|
||||
WMUtils.WMIsContentProtected(path, out pfIsProtected);
|
||||
if (pfIsProtected)
|
||||
throw new Exception("DRM present");
|
||||
WMUtils.WMCreateSyncReader(IntPtr.Zero, Rights.None, out m_syncReader);
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
m_IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000);
|
||||
m_streamWrapper = new StreamWrapper(m_IO);
|
||||
m_syncReader.OpenStream(m_streamWrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_syncReader.Open(path);
|
||||
}
|
||||
var pProfile = (m_syncReader as IWMProfile);
|
||||
int dwStreamCount;
|
||||
pProfile.GetStreamCount(out dwStreamCount);
|
||||
for (int dwIndex = 0; dwIndex < dwStreamCount; dwIndex++)
|
||||
{
|
||||
IWMStreamConfig pConfig = null;
|
||||
pProfile.GetStream(dwIndex, out pConfig);
|
||||
try
|
||||
{
|
||||
Guid guid;
|
||||
pConfig.GetStreamType(out guid);
|
||||
if (MediaType.Audio != guid)
|
||||
continue;
|
||||
short wStreamNum;
|
||||
pConfig.GetStreamNumber(out wStreamNum);
|
||||
int dwBitrate = -1;
|
||||
pConfig.GetBitrate(out dwBitrate);
|
||||
var pIWMMediaProps = pConfig as IWMMediaProps;
|
||||
int cbType = 0;
|
||||
pIWMMediaProps.GetMediaType(null, ref cbType);
|
||||
var pMediaType = new AMMediaType();
|
||||
pMediaType.formatSize = cbType;
|
||||
pIWMMediaProps.GetMediaType(pMediaType, ref cbType);
|
||||
if (pMediaType.formatType != FormatType.WaveEx)
|
||||
continue;
|
||||
if (pMediaType.subType != MediaSubType.WMAudio_Lossless)
|
||||
continue;
|
||||
m_wStreamNum = wStreamNum;
|
||||
pcm = WaveFormatExtensible.FromMediaType(pMediaType).GetConfig();
|
||||
break;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pConfig);
|
||||
}
|
||||
}
|
||||
if (m_wStreamNum == -1)
|
||||
throw new Exception("No WMA lossless streams found");
|
||||
|
||||
m_syncReader.SetReadStreamSamples(m_wStreamNum, false);
|
||||
bool pfCompressed;
|
||||
m_syncReader.GetReadStreamSamples(m_wStreamNum, out pfCompressed);
|
||||
if (pfCompressed)
|
||||
throw new Exception("doesn't decompress");
|
||||
m_syncReader.GetOutputNumberForStream(m_wStreamNum, out m_dwAudioOutputNum);
|
||||
IWMOutputMediaProps pProps;
|
||||
m_syncReader.GetOutputProps(m_dwAudioOutputNum, out pProps);
|
||||
|
||||
try
|
||||
{
|
||||
StringBuilder sName = null;
|
||||
AMMediaType pMediaType = null;
|
||||
int cbType = 0;
|
||||
|
||||
cbType = 0;
|
||||
pMediaType = null;
|
||||
pProps.GetMediaType(pMediaType, ref cbType);
|
||||
|
||||
// Get the name of the output we'll be using
|
||||
sName = null;
|
||||
short iName = 0;
|
||||
pProps.GetConnectionName(sName, ref iName);
|
||||
|
||||
sName = new StringBuilder(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.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType));
|
||||
|
||||
//
|
||||
// Get the value for MediaType
|
||||
//
|
||||
pProps.GetMediaType(pMediaType, ref cbType);
|
||||
|
||||
try
|
||||
{
|
||||
if (MediaType.Audio != pMediaType.majorType)
|
||||
throw new Exception("not Audio");
|
||||
if (FormatType.WaveEx != pMediaType.formatType)
|
||||
throw new Exception("not WaveEx");
|
||||
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
|
||||
{
|
||||
WMUtils.FreeWMMediaType(pMediaType);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pProps);
|
||||
}
|
||||
|
||||
//try
|
||||
//{
|
||||
// AttrDataType wmtType;
|
||||
// short cbLength = 0;
|
||||
// short wAnyStream = 0;
|
||||
// var pHeaderInfo = m_syncReader as IWMHeaderInfo;
|
||||
// pHeaderInfo.GetAttributeByName(ref wAnyStream, Constants.g_wszWMDuration, out wmtType, null, ref cbLength);
|
||||
// var pbValue = new byte[cbLength];
|
||||
// pHeaderInfo.GetAttributeByName(ref wAnyStream, Constants.g_wszWMDuration, out wmtType, pbValue, ref cbLength);
|
||||
// var m_cnsFileDuration = BitConverter.ToInt64(pbValue, 0);
|
||||
// _sampleCount = m_cnsFileDuration * m_pWfx.nSamplesPerSec / 10000000;
|
||||
// // NOT ACCURATE ENOUGH (~1ms precision observed)
|
||||
//}
|
||||
//catch (COMException)
|
||||
//{
|
||||
//}
|
||||
|
||||
//try
|
||||
//{
|
||||
// var pHeaderInfo = m_syncReader as IWMHeaderInfo2;
|
||||
// int nCodec;
|
||||
// pHeaderInfo.GetCodecInfoCount(out nCodec);
|
||||
// for (int wIndex = 0; wIndex < nCodec; wIndex++)
|
||||
// {
|
||||
// CodecInfoType enumCodecType;
|
||||
// short cchName = 0;
|
||||
// short cchDescription = 0;
|
||||
// short cbCodecInfo = 0;
|
||||
// pHeaderInfo.GetCodecInfo(wIndex, ref cchName, null,
|
||||
// ref cchDescription, null, out enumCodecType,
|
||||
// ref cbCodecInfo, null);
|
||||
// var pwszName = new StringBuilder(cchName);
|
||||
// var pwszDescription = new StringBuilder(cchDescription);
|
||||
// var pbCodecInfo = new byte[cbCodecInfo];
|
||||
// pHeaderInfo.GetCodecInfo(wIndex, ref cchName, pwszName,
|
||||
// ref cchDescription, pwszDescription, out enumCodecType,
|
||||
// ref cbCodecInfo, pbCodecInfo);
|
||||
// if (enumCodecType == CodecInfoType.Audio)
|
||||
// {
|
||||
// // pbCodecInfo = {99,1} ??/
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//catch (COMException)
|
||||
//{
|
||||
//}
|
||||
|
||||
//int cbMax;
|
||||
//m_syncReader.GetMaxOutputSampleSize(m_dwAudioOutputNum, out cbMax);
|
||||
}
|
||||
|
||||
public IAudioDecoderSettings Settings => null;
|
||||
|
||||
public void isValid(string filename)
|
||||
{
|
||||
int pdwDataSize = 0;
|
||||
WMUtils.WMValidateData(null, ref pdwDataSize);
|
||||
byte[] data = new byte[pdwDataSize];
|
||||
using (FileStream s = new FileStream(filename, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
if (s.Read(data, 0, pdwDataSize) < pdwDataSize)
|
||||
throw new Exception("partial read"); // TODO
|
||||
}
|
||||
WMUtils.WMValidateData(data, ref pdwDataSize);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
//if (m_streamWrapper != null)
|
||||
// m_streamWrapper.Close();
|
||||
if (m_IO != null)
|
||||
m_IO.Close();
|
||||
if (m_pSample != null)
|
||||
Marshal.ReleaseComObject(m_pSample);
|
||||
if (m_syncReader != null)
|
||||
{
|
||||
m_syncReader.Close();
|
||||
Marshal.ReleaseComObject(m_syncReader);
|
||||
}
|
||||
m_IO = null;
|
||||
m_pSample = null;
|
||||
m_syncReader = null;
|
||||
}
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
public long Remaining
|
||||
{
|
||||
get
|
||||
{
|
||||
return Length - Position;
|
||||
}
|
||||
}
|
||||
|
||||
public long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_sampleOffset / PCM.BlockAlign;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (m_sampleCount < 0 || value > m_sampleCount)
|
||||
throw new Exception("seeking past end of stream");
|
||||
if (value < Position)
|
||||
throw new NotSupportedException();
|
||||
if (value < Position)
|
||||
throw new Exception("cannot seek backwards");
|
||||
var buff = new AudioBuffer(this, 0x10000);
|
||||
while (value > Position && Read(buff, (int)Math.Min(Int32.MaxValue, value - Position)) != 0)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public AudioPCMConfig PCM
|
||||
{
|
||||
get
|
||||
{
|
||||
return pcm;
|
||||
}
|
||||
}
|
||||
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
}
|
||||
|
||||
public int Read(AudioBuffer buff, int maxLength)
|
||||
{
|
||||
buff.Prepare(this, maxLength);
|
||||
|
||||
int buff_offset = 0;
|
||||
int buff_size = buff.ByteLength;
|
||||
|
||||
while (m_pSampleSize < buff_size)
|
||||
{
|
||||
if (m_pSampleSize > 0)
|
||||
{
|
||||
IntPtr pdwBuffer;
|
||||
m_pSample.GetBuffer(out pdwBuffer);
|
||||
Marshal.Copy((IntPtr)(pdwBuffer.ToInt64() + m_pSampleOffset), buff.Bytes, buff_offset, m_pSampleSize);
|
||||
buff_size -= m_pSampleSize;
|
||||
buff_offset += m_pSampleSize;
|
||||
m_sampleOffset += m_pSampleSize;
|
||||
m_pSampleSize = 0;
|
||||
Marshal.ReleaseComObject(m_pSample);
|
||||
m_pSample = null;
|
||||
}
|
||||
|
||||
long cnsSampleTime;
|
||||
long cnsDuration;
|
||||
SampleFlag flags;
|
||||
int dwOutputNum;
|
||||
short wStreamNum;
|
||||
try
|
||||
{
|
||||
m_syncReader.GetNextSample(m_wStreamNum, out m_pSample, out cnsSampleTime, out cnsDuration, out flags, out dwOutputNum, out wStreamNum);
|
||||
}
|
||||
catch (COMException ex)
|
||||
{
|
||||
// EOF
|
||||
if (ex.ErrorCode == NSResults.E_NO_MORE_SAMPLES)
|
||||
{
|
||||
if ((m_sampleOffset % PCM.BlockAlign) != 0)
|
||||
throw new Exception("(m_sampleOffset % PCM.BlockAlign) != 0");
|
||||
m_sampleCount = m_sampleOffset / PCM.BlockAlign;
|
||||
if ((buff_offset % PCM.BlockAlign) != 0)
|
||||
throw new Exception("(buff_offset % PCM.BlockAlign) != 0");
|
||||
return buff.Length = buff_offset / PCM.BlockAlign;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
//if (dwOutputNum != m_dwAudioOutputNum || wStreamNum != m_wStreamNum)
|
||||
//{
|
||||
//}
|
||||
m_pSampleOffset = 0;
|
||||
m_pSample.GetLength(out m_pSampleSize);
|
||||
}
|
||||
|
||||
if (buff_size > 0)
|
||||
{
|
||||
IntPtr pdwBuffer;
|
||||
m_pSample.GetBuffer(out pdwBuffer);
|
||||
Marshal.Copy((IntPtr)(pdwBuffer.ToInt64() + m_pSampleOffset), buff.Bytes, buff_offset, buff_size);
|
||||
m_pSampleOffset += buff_size;
|
||||
m_pSampleSize -= buff_size;
|
||||
m_sampleOffset += buff_size;
|
||||
buff_offset += buff_size;
|
||||
buff_size = 0;
|
||||
}
|
||||
if ((buff_offset % PCM.BlockAlign) != 0)
|
||||
throw new Exception("(buff_offset % PCM.BlockAlign) != 0");
|
||||
return buff.Length = buff_offset / PCM.BlockAlign;
|
||||
}
|
||||
}
|
||||
}
|
||||
160
CUETools.Codecs.WMA/AudioEncoder.cs
Normal file
160
CUETools.Codecs.WMA/AudioEncoder.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using WindowsMediaLib;
|
||||
using WindowsMediaLib.Defs;
|
||||
|
||||
namespace CUETools.Codecs.WMA
|
||||
{
|
||||
public class AudioEncoder : IAudioDest
|
||||
{
|
||||
IWMWriter m_pEncoder;
|
||||
private string outputPath;
|
||||
private bool closed = false;
|
||||
private bool fileCreated = false;
|
||||
private bool writingBegan = false;
|
||||
private long sampleCount, finalSampleCount;
|
||||
|
||||
public long FinalSampleCount
|
||||
{
|
||||
set
|
||||
{
|
||||
this.finalSampleCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Path
|
||||
{
|
||||
get { return this.outputPath; }
|
||||
}
|
||||
|
||||
EncoderSettings m_settings;
|
||||
|
||||
public IAudioEncoderSettings Settings => m_settings;
|
||||
|
||||
public AudioEncoder(string path, EncoderSettings settings)
|
||||
{
|
||||
this.m_settings = settings;
|
||||
this.outputPath = path;
|
||||
|
||||
try
|
||||
{
|
||||
m_pEncoder = settings.GetWriter();
|
||||
int cInputs;
|
||||
m_pEncoder.GetInputCount(out cInputs);
|
||||
if (cInputs < 1) throw new InvalidOperationException();
|
||||
IWMInputMediaProps pInput;
|
||||
m_pEncoder.GetInputProps(0, out pInput);
|
||||
try
|
||||
{
|
||||
int cbType = 0;
|
||||
AMMediaType pMediaType = null;
|
||||
pInput.GetMediaType(pMediaType, ref cbType);
|
||||
pMediaType = new AMMediaType();
|
||||
pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType));
|
||||
pInput.GetMediaType(pMediaType, ref cbType);
|
||||
try
|
||||
{
|
||||
var wfe = new WaveFormatExtensible(m_settings.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);
|
||||
pInput.SetMediaType(pMediaType);
|
||||
m_pEncoder.SetInputProps(0, pInput);
|
||||
}
|
||||
finally
|
||||
{
|
||||
WMUtils.FreeWMMediaType(pMediaType);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pInput);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (m_pEncoder != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(m_pEncoder);
|
||||
m_pEncoder = null;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (!this.closed)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.writingBegan)
|
||||
{
|
||||
m_pEncoder.EndWriting();
|
||||
this.writingBegan = false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (m_pEncoder != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(m_pEncoder);
|
||||
m_pEncoder = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
if (this.outputPath == null)
|
||||
throw new InvalidOperationException("This writer was not created from file.");
|
||||
|
||||
if (!this.closed)
|
||||
{
|
||||
this.Close();
|
||||
|
||||
if (this.fileCreated)
|
||||
{
|
||||
File.Delete(this.outputPath);
|
||||
this.fileCreated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(AudioBuffer buffer)
|
||||
{
|
||||
if (this.closed)
|
||||
throw new InvalidOperationException("Writer already closed.");
|
||||
|
||||
if (!this.fileCreated)
|
||||
{
|
||||
this.m_pEncoder.SetOutputFilename(outputPath);
|
||||
this.fileCreated = true;
|
||||
}
|
||||
if (!this.writingBegan)
|
||||
{
|
||||
this.m_pEncoder.BeginWriting();
|
||||
this.writingBegan = true;
|
||||
}
|
||||
|
||||
buffer.Prepare(this);
|
||||
INSSBuffer pSample;
|
||||
m_pEncoder.AllocateSample(buffer.ByteLength, out pSample);
|
||||
IntPtr pdwBuffer;
|
||||
pSample.GetBuffer(out pdwBuffer);
|
||||
pSample.SetLength(buffer.ByteLength);
|
||||
Marshal.Copy(buffer.Bytes, 0, pdwBuffer, buffer.ByteLength);
|
||||
long cnsSampleTime = sampleCount * 10000000L / Settings.PCM.SampleRate;
|
||||
m_pEncoder.WriteSample(0, cnsSampleTime, SampleFlag.CleanPoint, pSample);
|
||||
Marshal.ReleaseComObject(pSample);
|
||||
sampleCount += buffer.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
CUETools.Codecs.WMA/DecoderSettings.cs
Normal file
34
CUETools.Codecs.WMA/DecoderSettings.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace CUETools.Codecs.WMA
|
||||
{
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class DecoderSettings : IAudioDecoderSettings
|
||||
{
|
||||
#region IAudioDecoderSettings implementation
|
||||
[Browsable(false)]
|
||||
public string Extension => "wma";
|
||||
|
||||
[Browsable(false)]
|
||||
public string Name => "windows";
|
||||
|
||||
[Browsable(false)]
|
||||
public Type DecoderType => typeof(AudioDecoder);
|
||||
|
||||
[Browsable(false)]
|
||||
public int Priority => 2;
|
||||
|
||||
public IAudioDecoderSettings Clone()
|
||||
{
|
||||
return MemberwiseClone() as IAudioDecoderSettings;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public DecoderSettings()
|
||||
{
|
||||
this.Init();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,436 +1,305 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using CUETools.Codecs;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using WindowsMediaLib;
|
||||
using WindowsMediaLib.Defs;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace CUETools.Codecs.WMA
|
||||
{
|
||||
public abstract class EncoderSettings : AudioEncoderSettings
|
||||
{
|
||||
public override Type EncoderType => typeof(AudioEncoder);
|
||||
|
||||
public override bool Lossless => true;
|
||||
|
||||
public EncoderSettings()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
protected Guid m_subType;
|
||||
protected bool m_vbr = true;
|
||||
|
||||
public IWMWriter GetWriter()
|
||||
{
|
||||
IWMProfileManager pProfileManager = null;
|
||||
try
|
||||
{
|
||||
WMUtils.WMCreateProfileManager(out pProfileManager);
|
||||
var pCodecInfo3 = pProfileManager as IWMCodecInfo3;
|
||||
// We have to use the same pProfileManager for enumeration,
|
||||
// because it calls SetCodecEnumerationSetting, so chosenFormat.format
|
||||
// would not point to the same format for a pProfileManager
|
||||
// with different (default) settings, and GetCodecFormat
|
||||
// would return the wrong stream config.
|
||||
var formats = GetFormats(pProfileManager);
|
||||
if (this.EncoderMode != "")
|
||||
formats.RemoveAll(fmt => fmt.modeName != this.EncoderMode);
|
||||
if (formats.Count < 1)
|
||||
throw new NotSupportedException("codec/format not found");
|
||||
if (formats.Count > 1)
|
||||
throw new NotSupportedException("codec/format ambiguous");
|
||||
var chosenFormat = formats[0];
|
||||
IWMStreamConfig pStreamConfig1;
|
||||
pCodecInfo3.GetCodecFormat(MediaType.Audio, chosenFormat.codec, chosenFormat.format, out pStreamConfig1);
|
||||
try
|
||||
{
|
||||
pStreamConfig1.SetStreamNumber(1);
|
||||
IWMProfile pProfile;
|
||||
pProfileManager.CreateEmptyProfile(WMVersion.V9_0, out pProfile);
|
||||
try
|
||||
{
|
||||
pProfile.AddStream(pStreamConfig1);
|
||||
IWMWriter pWriter;
|
||||
WMUtils.WMCreateWriter(IntPtr.Zero, out pWriter);
|
||||
try
|
||||
{
|
||||
pWriter.SetProfile(pProfile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Marshal.ReleaseComObject(pWriter);
|
||||
throw ex;
|
||||
}
|
||||
return pWriter;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pProfile);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pStreamConfig1);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pProfileManager);
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<WMAFormatInfo> EnumerateFormatInfo(IWMProfileManager pProfileManager2)
|
||||
{
|
||||
IWMProfileManager pProfileManager = null;
|
||||
try
|
||||
{
|
||||
if (pProfileManager2 == null)
|
||||
WMUtils.WMCreateProfileManager(out pProfileManager);
|
||||
var pCodecInfo3 = (pProfileManager2 ?? pProfileManager) as IWMCodecInfo3;
|
||||
int cCodecs;
|
||||
pCodecInfo3.GetCodecInfoCount(MediaType.Audio, out cCodecs);
|
||||
for (int iCodec = 0; iCodec < cCodecs; iCodec++)
|
||||
{
|
||||
int szCodecName = 0;
|
||||
pCodecInfo3.GetCodecName(MediaType.Audio, iCodec, null, ref szCodecName);
|
||||
var codecName = new StringBuilder(szCodecName);
|
||||
pCodecInfo3.GetCodecName(MediaType.Audio, iCodec, codecName, ref szCodecName);
|
||||
var attrDataType = new AttrDataType();
|
||||
int dwAttrSize = 0;
|
||||
byte[] pAttrValue = new byte[4];
|
||||
pCodecInfo3.GetCodecProp(MediaType.Audio, iCodec, Constants.g_wszIsVBRSupported, out attrDataType, pAttrValue, ref dwAttrSize);
|
||||
if (pAttrValue[0] != 1)
|
||||
continue;
|
||||
if (m_vbr)
|
||||
{
|
||||
pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszVBREnabled, AttrDataType.BOOL, new byte[] { 1, 0, 0, 0 }, 4);
|
||||
pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszNumPasses, AttrDataType.DWORD, new byte[] { 1, 0, 0, 0 }, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszVBREnabled, AttrDataType.BOOL, new byte[] { 0, 0, 0, 0 }, 4);
|
||||
}
|
||||
|
||||
int cFormat;
|
||||
pCodecInfo3.GetCodecFormatCount(MediaType.Audio, iCodec, out cFormat);
|
||||
for (int iFormat = 0; iFormat < cFormat; iFormat++)
|
||||
{
|
||||
IWMStreamConfig pStreamConfig;
|
||||
int cchDesc = 1024;
|
||||
StringBuilder szDesc = new StringBuilder(cchDesc);
|
||||
pCodecInfo3.GetCodecFormatDesc(MediaType.Audio, iCodec, iFormat, out pStreamConfig, szDesc, ref cchDesc);
|
||||
if (szDesc.ToString().Contains("(A/V)"))
|
||||
continue;
|
||||
try
|
||||
{
|
||||
var pProps = pStreamConfig as IWMMediaProps;
|
||||
int cbType = 0;
|
||||
AMMediaType pMediaType = null;
|
||||
pProps.GetMediaType(pMediaType, ref cbType);
|
||||
pMediaType = new AMMediaType();
|
||||
pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType));
|
||||
pProps.GetMediaType(pMediaType, ref cbType);
|
||||
try
|
||||
{
|
||||
if (pMediaType.majorType == MediaType.Audio && pMediaType.formatType == FormatType.WaveEx && pMediaType.subType == m_subType)
|
||||
{
|
||||
var pcm = WaveFormatExtensible.FromMediaType(pMediaType).GetConfig();
|
||||
if (PCM == null || (pcm.ChannelCount == PCM.ChannelCount && pcm.SampleRate == PCM.SampleRate && pcm.BitsPerSample >= PCM.BitsPerSample))
|
||||
yield return new WMAFormatInfo()
|
||||
{
|
||||
codec = iCodec,
|
||||
codecName = codecName.ToString(),
|
||||
format = iFormat,
|
||||
formatName = szDesc.ToString(),
|
||||
subType = pMediaType.subType,
|
||||
pcm = pcm
|
||||
};
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
WMUtils.FreeWMMediaType(pMediaType);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pStreamConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (pProfileManager != null)
|
||||
Marshal.ReleaseComObject(pProfileManager);
|
||||
}
|
||||
}
|
||||
|
||||
internal List<WMAFormatInfo> GetFormats(IWMProfileManager pProfileManager)
|
||||
{
|
||||
var formats = new List<WMAFormatInfo>(this.EnumerateFormatInfo(pProfileManager));
|
||||
formats.RemoveAll(fmt => formats.Exists(fmt2 => fmt2.pcm.BitsPerSample < fmt.pcm.BitsPerSample && fmt2.pcm.ChannelCount == fmt.pcm.ChannelCount && fmt2.pcm.SampleRate == fmt.pcm.SampleRate));
|
||||
if (formats.Count < 2) return formats;
|
||||
int prefixLen = 0, suffixLen = 0;
|
||||
while (formats.TrueForAll(s => s.formatName.Length > prefixLen &&
|
||||
s.formatName.Substring(0, prefixLen + 1) ==
|
||||
formats[0].formatName.Substring(0, prefixLen + 1)))
|
||||
prefixLen++;
|
||||
while (formats.TrueForAll(s => s.formatName.Length > suffixLen &&
|
||||
s.formatName.Substring(s.formatName.Length - suffixLen - 1) ==
|
||||
formats[0].formatName.Substring(formats[0].formatName.Length - suffixLen - 1)))
|
||||
suffixLen++;
|
||||
formats.ForEach(s => s.modeName = s.formatName.Substring(prefixLen, s.formatName.Length - suffixLen - prefixLen).Trim().Replace(' ', '_'));
|
||||
int ix, iy;
|
||||
formats.Sort((Comparison<WMAFormatInfo>)((x, y) => int.TryParse(x.modeName, out ix) && int.TryParse(y.modeName, out iy) ? ix - iy : x.modeName.CompareTo(y.modeName)));
|
||||
return formats;
|
||||
}
|
||||
|
||||
public override string GetSupportedModes(out string defaultMode)
|
||||
{
|
||||
var fmts = GetFormats(null);
|
||||
defaultMode = fmts.Count > 0 ? fmts[fmts.Count - 1].modeName : "";
|
||||
return string.Join(" ", fmts.ConvertAll(s => s.modeName).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
internal class WMAFormatInfo
|
||||
{
|
||||
public int codec;
|
||||
public string codecName;
|
||||
public int format;
|
||||
public string formatName;
|
||||
public string modeName;
|
||||
public Guid subType;
|
||||
public AudioPCMConfig pcm;
|
||||
}
|
||||
|
||||
public class LosslessEncoderSettings : EncoderSettings
|
||||
{
|
||||
public override string Extension => "wma";
|
||||
|
||||
public override string Name => "wma lossless";
|
||||
|
||||
public override int Priority => 1;
|
||||
|
||||
public override bool Lossless => true;
|
||||
|
||||
public LosslessEncoderSettings()
|
||||
: base()
|
||||
{
|
||||
this.m_subType = MediaSubType.WMAudio_Lossless;
|
||||
}
|
||||
}
|
||||
|
||||
public class LossyEncoderSettings : EncoderSettings
|
||||
{
|
||||
public override string Extension => "wma";
|
||||
|
||||
public override string Name => "wma lossy";
|
||||
|
||||
public override int Priority => 1;
|
||||
|
||||
public LossyEncoderSettings()
|
||||
: base()
|
||||
{
|
||||
this.m_subType = MediaSubType.WMAudioV9;
|
||||
}
|
||||
|
||||
public enum Codec
|
||||
{
|
||||
WMA9,
|
||||
WMA10Pro
|
||||
}
|
||||
|
||||
[DefaultValue(Codec.WMA10Pro)]
|
||||
[JsonProperty]
|
||||
public Codec Version
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_subType == MediaSubType.WMAudioV9 ? Codec.WMA10Pro : Codec.WMA9;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_subType = value == Codec.WMA10Pro ? MediaSubType.WMAudioV9 : MediaSubType.WMAudioV8;
|
||||
}
|
||||
}
|
||||
|
||||
[DefaultValue(true)]
|
||||
[JsonProperty]
|
||||
public bool VBR
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_vbr;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_vbr = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AudioEncoder : IAudioDest
|
||||
{
|
||||
IWMWriter m_pEncoder;
|
||||
private string outputPath;
|
||||
private bool closed = false;
|
||||
private bool fileCreated = false;
|
||||
private bool writingBegan = false;
|
||||
private long sampleCount, finalSampleCount;
|
||||
|
||||
public long FinalSampleCount
|
||||
{
|
||||
set
|
||||
{
|
||||
this.finalSampleCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Path
|
||||
{
|
||||
get { return this.outputPath; }
|
||||
}
|
||||
|
||||
EncoderSettings m_settings;
|
||||
|
||||
public virtual AudioEncoderSettings Settings
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_settings;
|
||||
}
|
||||
}
|
||||
|
||||
public AudioEncoder(string path, EncoderSettings settings)
|
||||
{
|
||||
this.m_settings = settings;
|
||||
this.outputPath = path;
|
||||
|
||||
try
|
||||
{
|
||||
m_pEncoder = settings.GetWriter();
|
||||
int cInputs;
|
||||
m_pEncoder.GetInputCount(out cInputs);
|
||||
if (cInputs < 1) throw new InvalidOperationException();
|
||||
IWMInputMediaProps pInput;
|
||||
m_pEncoder.GetInputProps(0, out pInput);
|
||||
try
|
||||
{
|
||||
int cbType = 0;
|
||||
AMMediaType pMediaType = null;
|
||||
pInput.GetMediaType(pMediaType, ref cbType);
|
||||
pMediaType = new AMMediaType();
|
||||
pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType));
|
||||
pInput.GetMediaType(pMediaType, ref cbType);
|
||||
try
|
||||
{
|
||||
var wfe = new WaveFormatExtensible(m_settings.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);
|
||||
pInput.SetMediaType(pMediaType);
|
||||
m_pEncoder.SetInputProps(0, pInput);
|
||||
}
|
||||
finally
|
||||
{
|
||||
WMUtils.FreeWMMediaType(pMediaType);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pInput);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (m_pEncoder != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(m_pEncoder);
|
||||
m_pEncoder = null;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (!this.closed)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.writingBegan)
|
||||
{
|
||||
m_pEncoder.EndWriting();
|
||||
this.writingBegan = false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (m_pEncoder != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(m_pEncoder);
|
||||
m_pEncoder = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
if (this.outputPath == null)
|
||||
throw new InvalidOperationException("This writer was not created from file.");
|
||||
|
||||
if (!this.closed)
|
||||
{
|
||||
this.Close();
|
||||
|
||||
if (this.fileCreated)
|
||||
{
|
||||
File.Delete(this.outputPath);
|
||||
this.fileCreated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(AudioBuffer buffer)
|
||||
{
|
||||
if (this.closed)
|
||||
throw new InvalidOperationException("Writer already closed.");
|
||||
|
||||
if (!this.fileCreated)
|
||||
{
|
||||
this.m_pEncoder.SetOutputFilename(outputPath);
|
||||
this.fileCreated = true;
|
||||
}
|
||||
if (!this.writingBegan)
|
||||
{
|
||||
this.m_pEncoder.BeginWriting();
|
||||
this.writingBegan = true;
|
||||
}
|
||||
|
||||
buffer.Prepare(this);
|
||||
INSSBuffer pSample;
|
||||
m_pEncoder.AllocateSample(buffer.ByteLength, out pSample);
|
||||
IntPtr pdwBuffer;
|
||||
pSample.GetBuffer(out pdwBuffer);
|
||||
pSample.SetLength(buffer.ByteLength);
|
||||
Marshal.Copy(buffer.Bytes, 0, pdwBuffer, buffer.ByteLength);
|
||||
long cnsSampleTime = sampleCount * 10000000L / Settings.PCM.SampleRate;
|
||||
m_pEncoder.WriteSample(0, cnsSampleTime, SampleFlag.CleanPoint, pSample);
|
||||
Marshal.ReleaseComObject(pSample);
|
||||
sampleCount += buffer.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using WindowsMediaLib;
|
||||
using WindowsMediaLib.Defs;
|
||||
|
||||
namespace CUETools.Codecs.WMA
|
||||
{
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public abstract class EncoderSettings : IAudioEncoderSettings
|
||||
{
|
||||
#region IAudioEncoderSettings implementation
|
||||
[Browsable(false)]
|
||||
public string Extension => "wma";
|
||||
|
||||
[Browsable(false)]
|
||||
public abstract string Name { get; }
|
||||
|
||||
[Browsable(false)]
|
||||
public Type EncoderType => typeof(AudioEncoder);
|
||||
|
||||
[Browsable(false)]
|
||||
public abstract bool Lossless { get; }
|
||||
|
||||
[Browsable(false)]
|
||||
public int Priority => 1;
|
||||
|
||||
[Browsable(false)]
|
||||
public string SupportedModes =>
|
||||
string.Join(" ", GetFormats(null).ConvertAll(s => s.modeName).ToArray());
|
||||
|
||||
[Browsable(false)]
|
||||
public string DefaultMode =>
|
||||
GetFormats(null).ConvertAll(s => s.modeName).FindLast(x => true) ?? "";
|
||||
|
||||
[Browsable(false)]
|
||||
[DefaultValue("")]
|
||||
[JsonProperty]
|
||||
public string EncoderMode { get; set; }
|
||||
|
||||
[Browsable(false)]
|
||||
public AudioPCMConfig PCM { get; set; }
|
||||
|
||||
[Browsable(false)]
|
||||
public int BlockSize { get; set; }
|
||||
|
||||
[Browsable(false)]
|
||||
[DefaultValue(4096)]
|
||||
public int Padding { get; set; }
|
||||
|
||||
public IAudioEncoderSettings Clone()
|
||||
{
|
||||
return MemberwiseClone() as IAudioEncoderSettings;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public EncoderSettings()
|
||||
{
|
||||
this.Init();
|
||||
}
|
||||
|
||||
protected Guid m_subType;
|
||||
protected bool m_vbr = true;
|
||||
|
||||
public IWMWriter GetWriter()
|
||||
{
|
||||
IWMProfileManager pProfileManager = null;
|
||||
try
|
||||
{
|
||||
WMUtils.WMCreateProfileManager(out pProfileManager);
|
||||
var pCodecInfo3 = pProfileManager as IWMCodecInfo3;
|
||||
// We have to use the same pProfileManager for enumeration,
|
||||
// because it calls SetCodecEnumerationSetting, so chosenFormat.format
|
||||
// would not point to the same format for a pProfileManager
|
||||
// with different (default) settings, and GetCodecFormat
|
||||
// would return the wrong stream config.
|
||||
var formats = GetFormats(pProfileManager);
|
||||
if (this.EncoderMode != "")
|
||||
formats.RemoveAll(fmt => fmt.modeName != this.EncoderMode);
|
||||
if (formats.Count < 1)
|
||||
throw new NotSupportedException("codec/format not found");
|
||||
if (formats.Count > 1)
|
||||
throw new NotSupportedException("codec/format ambiguous");
|
||||
var chosenFormat = formats[0];
|
||||
IWMStreamConfig pStreamConfig1;
|
||||
pCodecInfo3.GetCodecFormat(MediaType.Audio, chosenFormat.codec, chosenFormat.format, out pStreamConfig1);
|
||||
try
|
||||
{
|
||||
pStreamConfig1.SetStreamNumber(1);
|
||||
IWMProfile pProfile;
|
||||
pProfileManager.CreateEmptyProfile(WMVersion.V9_0, out pProfile);
|
||||
try
|
||||
{
|
||||
pProfile.AddStream(pStreamConfig1);
|
||||
IWMWriter pWriter;
|
||||
WMUtils.WMCreateWriter(IntPtr.Zero, out pWriter);
|
||||
try
|
||||
{
|
||||
pWriter.SetProfile(pProfile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Marshal.ReleaseComObject(pWriter);
|
||||
throw ex;
|
||||
}
|
||||
return pWriter;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pProfile);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pStreamConfig1);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pProfileManager);
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<WMAFormatInfo> EnumerateFormatInfo(IWMProfileManager pProfileManager2)
|
||||
{
|
||||
IWMProfileManager pProfileManager = null;
|
||||
try
|
||||
{
|
||||
if (pProfileManager2 == null)
|
||||
WMUtils.WMCreateProfileManager(out pProfileManager);
|
||||
var pCodecInfo3 = (pProfileManager2 ?? pProfileManager) as IWMCodecInfo3;
|
||||
int cCodecs;
|
||||
pCodecInfo3.GetCodecInfoCount(MediaType.Audio, out cCodecs);
|
||||
for (int iCodec = 0; iCodec < cCodecs; iCodec++)
|
||||
{
|
||||
int szCodecName = 0;
|
||||
pCodecInfo3.GetCodecName(MediaType.Audio, iCodec, null, ref szCodecName);
|
||||
var codecName = new StringBuilder(szCodecName);
|
||||
pCodecInfo3.GetCodecName(MediaType.Audio, iCodec, codecName, ref szCodecName);
|
||||
var attrDataType = new AttrDataType();
|
||||
int dwAttrSize = 0;
|
||||
byte[] pAttrValue = new byte[4];
|
||||
pCodecInfo3.GetCodecProp(MediaType.Audio, iCodec, Constants.g_wszIsVBRSupported, out attrDataType, pAttrValue, ref dwAttrSize);
|
||||
if (pAttrValue[0] != 1)
|
||||
continue;
|
||||
if (m_vbr)
|
||||
{
|
||||
pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszVBREnabled, AttrDataType.BOOL, new byte[] { 1, 0, 0, 0 }, 4);
|
||||
pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszNumPasses, AttrDataType.DWORD, new byte[] { 1, 0, 0, 0 }, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszVBREnabled, AttrDataType.BOOL, new byte[] { 0, 0, 0, 0 }, 4);
|
||||
}
|
||||
|
||||
int cFormat;
|
||||
pCodecInfo3.GetCodecFormatCount(MediaType.Audio, iCodec, out cFormat);
|
||||
for (int iFormat = 0; iFormat < cFormat; iFormat++)
|
||||
{
|
||||
IWMStreamConfig pStreamConfig;
|
||||
int cchDesc = 1024;
|
||||
StringBuilder szDesc = new StringBuilder(cchDesc);
|
||||
pCodecInfo3.GetCodecFormatDesc(MediaType.Audio, iCodec, iFormat, out pStreamConfig, szDesc, ref cchDesc);
|
||||
if (szDesc.ToString().Contains("(A/V)"))
|
||||
continue;
|
||||
try
|
||||
{
|
||||
var pProps = pStreamConfig as IWMMediaProps;
|
||||
int cbType = 0;
|
||||
AMMediaType pMediaType = null;
|
||||
pProps.GetMediaType(pMediaType, ref cbType);
|
||||
pMediaType = new AMMediaType();
|
||||
pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType));
|
||||
pProps.GetMediaType(pMediaType, ref cbType);
|
||||
try
|
||||
{
|
||||
if (pMediaType.majorType == MediaType.Audio && pMediaType.formatType == FormatType.WaveEx && pMediaType.subType == m_subType)
|
||||
{
|
||||
var pcm = WaveFormatExtensible.FromMediaType(pMediaType).GetConfig();
|
||||
if (PCM == null || (pcm.ChannelCount == PCM.ChannelCount && pcm.SampleRate == PCM.SampleRate && pcm.BitsPerSample >= PCM.BitsPerSample))
|
||||
yield return new WMAFormatInfo()
|
||||
{
|
||||
codec = iCodec,
|
||||
codecName = codecName.ToString(),
|
||||
format = iFormat,
|
||||
formatName = szDesc.ToString(),
|
||||
subType = pMediaType.subType,
|
||||
pcm = pcm
|
||||
};
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
WMUtils.FreeWMMediaType(pMediaType);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.ReleaseComObject(pStreamConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (pProfileManager != null)
|
||||
Marshal.ReleaseComObject(pProfileManager);
|
||||
}
|
||||
}
|
||||
|
||||
internal List<WMAFormatInfo> GetFormats(IWMProfileManager pProfileManager)
|
||||
{
|
||||
var formats = new List<WMAFormatInfo>(this.EnumerateFormatInfo(pProfileManager));
|
||||
formats.RemoveAll(fmt => formats.Exists(fmt2 => fmt2.pcm.BitsPerSample < fmt.pcm.BitsPerSample && fmt2.pcm.ChannelCount == fmt.pcm.ChannelCount && fmt2.pcm.SampleRate == fmt.pcm.SampleRate));
|
||||
if (formats.Count < 2) return formats;
|
||||
int prefixLen = 0, suffixLen = 0;
|
||||
while (formats.TrueForAll(s => s.formatName.Length > prefixLen &&
|
||||
s.formatName.Substring(0, prefixLen + 1) ==
|
||||
formats[0].formatName.Substring(0, prefixLen + 1)))
|
||||
prefixLen++;
|
||||
while (formats.TrueForAll(s => s.formatName.Length > suffixLen &&
|
||||
s.formatName.Substring(s.formatName.Length - suffixLen - 1) ==
|
||||
formats[0].formatName.Substring(formats[0].formatName.Length - suffixLen - 1)))
|
||||
suffixLen++;
|
||||
formats.ForEach(s => s.modeName = s.formatName.Substring(prefixLen, s.formatName.Length - suffixLen - prefixLen).Trim().Replace(' ', '_'));
|
||||
int ix, iy;
|
||||
formats.Sort((Comparison<WMAFormatInfo>)((x, y) => int.TryParse(x.modeName, out ix) && int.TryParse(y.modeName, out iy) ? ix - iy : x.modeName.CompareTo(y.modeName)));
|
||||
return formats;
|
||||
}
|
||||
}
|
||||
|
||||
internal class WMAFormatInfo
|
||||
{
|
||||
public int codec;
|
||||
public string codecName;
|
||||
public int format;
|
||||
public string formatName;
|
||||
public string modeName;
|
||||
public Guid subType;
|
||||
public AudioPCMConfig pcm;
|
||||
}
|
||||
|
||||
public class LosslessEncoderSettings : EncoderSettings
|
||||
{
|
||||
public override string Name => "wma lossless";
|
||||
|
||||
public override bool Lossless => true;
|
||||
|
||||
public LosslessEncoderSettings()
|
||||
: base()
|
||||
{
|
||||
this.m_subType = MediaSubType.WMAudio_Lossless;
|
||||
}
|
||||
}
|
||||
|
||||
public class LossyEncoderSettings : EncoderSettings
|
||||
{
|
||||
public override string Name => "wma lossy";
|
||||
|
||||
public override bool Lossless => false;
|
||||
|
||||
public LossyEncoderSettings()
|
||||
: base()
|
||||
{
|
||||
this.m_subType = MediaSubType.WMAudioV9;
|
||||
}
|
||||
|
||||
public enum Codec
|
||||
{
|
||||
WMA9,
|
||||
WMA10Pro
|
||||
}
|
||||
|
||||
[DefaultValue(Codec.WMA10Pro)]
|
||||
[JsonProperty]
|
||||
public Codec Version
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_subType == MediaSubType.WMAudioV9 ? Codec.WMA10Pro : Codec.WMA9;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_subType = value == Codec.WMA10Pro ? MediaSubType.WMAudioV9 : MediaSubType.WMAudioV8;
|
||||
}
|
||||
}
|
||||
|
||||
[DefaultValue(true)]
|
||||
[JsonProperty]
|
||||
public bool VBR
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.m_vbr;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.m_vbr = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user