diff --git a/CUETools.Codecs.WMA/CUETools.Codecs.WMA.csproj b/CUETools.Codecs.WMA/CUETools.Codecs.WMA.csproj index 3a3c1bd..fa359c6 100644 --- a/CUETools.Codecs.WMA/CUETools.Codecs.WMA.csproj +++ b/CUETools.Codecs.WMA/CUETools.Codecs.WMA.csproj @@ -60,6 +60,7 @@ + diff --git a/CUETools.Codecs.WMA/WMAReader.cs b/CUETools.Codecs.WMA/WMAReader.cs index 74919c6..fc953d5 100644 --- a/CUETools.Codecs.WMA/WMAReader.cs +++ b/CUETools.Codecs.WMA/WMAReader.cs @@ -70,7 +70,7 @@ namespace CUETools.Codecs.WMA for (int dwIndex = 0; dwIndex < dwStreamCount; dwIndex++) { IWMStreamConfig pConfig = null; - pProfile.GetStream(0, out pConfig); + pProfile.GetStream(dwIndex, out pConfig); try { Guid guid; @@ -84,14 +84,15 @@ namespace CUETools.Codecs.WMA var pIWMMediaProps = pConfig as IWMMediaProps; int cbType = 0; pIWMMediaProps.GetMediaType(null, ref cbType); - var mt = new AMMediaType(); - mt.formatSize = cbType; - pIWMMediaProps.GetMediaType(mt, ref cbType); - if (mt.formatType != FormatType.WaveEx) + var pMediaType = new AMMediaType(); + pMediaType.formatSize = cbType; + pIWMMediaProps.GetMediaType(pMediaType, ref cbType); + if (pMediaType.formatType != FormatType.WaveEx) continue; - if (mt.subType != MediaSubType.WMAudio_Lossless) + if (pMediaType.subType != MediaSubType.WMAudio_Lossless) continue; m_wStreamNum = wStreamNum; + pcm = WaveFormatExtensible.FromMediaType(pMediaType).GetConfig(); break; } finally @@ -110,7 +111,7 @@ namespace CUETools.Codecs.WMA m_syncReader.GetOutputNumberForStream(m_wStreamNum, out m_dwAudioOutputNum); IWMOutputMediaProps pProps; m_syncReader.GetOutputProps(m_dwAudioOutputNum, out pProps); - var m_pWfx = new WaveFormatEx(); + try { StringBuilder sName = null; @@ -129,6 +130,12 @@ namespace CUETools.Codecs.WMA 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)); @@ -143,7 +150,15 @@ namespace CUETools.Codecs.WMA throw new Exception("not Audio"); if (FormatType.WaveEx != pMediaType.formatType) 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 { @@ -202,8 +217,6 @@ namespace CUETools.Codecs.WMA //{ //} - pcm = new AudioPCMConfig(m_pWfx.wBitsPerSample, m_pWfx.nChannels, m_pWfx.nSamplesPerSec); - //int cbMax; //m_syncReader.GetMaxOutputSampleSize(m_dwAudioOutputNum, out cbMax); } diff --git a/CUETools.Codecs.WMA/WMAWriter.cs b/CUETools.Codecs.WMA/WMAWriter.cs index 8fb25a6..577ce55 100644 --- a/CUETools.Codecs.WMA/WMAWriter.cs +++ b/CUETools.Codecs.WMA/WMAWriter.cs @@ -134,19 +134,17 @@ namespace CUETools.Codecs.WMA { if (pMediaType.majorType == MediaType.Audio && pMediaType.formatType == FormatType.WaveEx && pMediaType.subType == m_subType) { - WaveFormatEx pWfx = new WaveFormatEx(); - Marshal.PtrToStructure(pMediaType.formatPtr, pWfx); - var info = new WMAFormatInfo() - { - codec = iCodec, - codecName = codecName.ToString(), - format = iFormat, - formatName = szDesc.ToString(), - subType = pMediaType.subType, - pcm = new AudioPCMConfig(pWfx.wBitsPerSample, pWfx.nChannels, pWfx.nSamplesPerSec) - }; - if (PCM == null || (pWfx.nChannels == PCM.ChannelCount && pWfx.wBitsPerSample >= PCM.BitsPerSample && pWfx.nSamplesPerSec == PCM.SampleRate)) - yield return info; + 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 @@ -268,27 +266,6 @@ namespace CUETools.Codecs.WMA private bool writingBegan = false; private long sampleCount, finalSampleCount; - const ushort WAVE_FORMAT_EXTENSIBLE = 0xFFFE; - const ushort WAVE_FORMAT_PCM = 1; - - /// - /// From WAVEFORMATEXTENSIBLE - /// - [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 { set @@ -335,27 +312,7 @@ namespace CUETools.Codecs.WMA pInput.GetMediaType(pMediaType, ref cbType); try { - var wfe = new WaveFormatExtensible(); - 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; - } + var wfe = new WaveFormatExtensible(m_settings.PCM); Marshal.FreeCoTaskMem(pMediaType.formatPtr); pMediaType.formatPtr = IntPtr.Zero; pMediaType.formatSize = 0; diff --git a/CUETools.Codecs.WMA/WaveFormatExtensible.cs b/CUETools.Codecs.WMA/WaveFormatExtensible.cs new file mode 100644 index 0000000..d3d0fd4 --- /dev/null +++ b/CUETools.Codecs.WMA/WaveFormatExtensible.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Runtime.InteropServices; +using WindowsMediaLib.Defs; + +namespace CUETools.Codecs.WMA +{ + /// + /// From WAVEFORMATEXTENSIBLE + /// + [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; + } +} diff --git a/CUETools.Codecs/AudioPCMConfig.cs b/CUETools.Codecs/AudioPCMConfig.cs index d2a1d04..0b736f1 100644 --- a/CUETools.Codecs/AudioPCMConfig.cs +++ b/CUETools.Codecs/AudioPCMConfig.cs @@ -24,6 +24,7 @@ SPEAKER_TOP_BACK_CENTER = 0x10000, SPEAKER_TOP_BACK_RIGHT = 0x20000, + DIRECTOUT = 0, KSAUDIO_SPEAKER_MONO = (SPEAKER_FRONT_CENTER), KSAUDIO_SPEAKER_STEREO = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_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 SpeakerConfig ChannelMask { get { return _channelMask; } } 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: return SpeakerConfig.KSAUDIO_SPEAKER_MONO; @@ -68,15 +69,15 @@ case 8: 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; _channelCount = channelCount; _sampleRate = sampleRate; - _channelMask = GetDefaultChannelMask(); + _channelMask = channelMask == 0 ? GetDefaultChannelMask(channelCount) : channelMask; } } } diff --git a/CUETools.Codecs/WAVReader.cs b/CUETools.Codecs/WAVReader.cs index 10fd1e2..6cfbc40 100644 --- a/CUETools.Codecs/WAVReader.cs +++ b/CUETools.Codecs/WAVReader.cs @@ -177,13 +177,14 @@ namespace CUETools.Codecs _br.ReadInt32(); // bytes per second int _blockAlign = _br.ReadInt16(); int _bitsPerSample = _br.ReadInt16(); + int _channelMask = 0; pos += 16; if (fmtTag == 0xFFFEU && ckSize >= 34) // WAVE_FORMAT_EXTENSIBLE { _br.ReadInt16(); // CbSize _br.ReadInt16(); // ValidBitsPerSample - int channelMask = _br.ReadInt32(); + _channelMask = _br.ReadInt32(); fmtTag = _br.ReadUInt16(); pos += 10; } @@ -191,7 +192,7 @@ namespace CUETools.Codecs if (fmtTag != 1) // 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) throw new Exception("WAVE has strange BlockAlign"); } diff --git a/CUETools.Codecs/WAVWriter.cs b/CUETools.Codecs/WAVWriter.cs index e4a756f..fb64337 100644 --- a/CUETools.Codecs/WAVWriter.cs +++ b/CUETools.Codecs/WAVWriter.cs @@ -75,7 +75,7 @@ namespace CUETools.Codecs const uint fccFormat = 0x20746D66; 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; @@ -83,7 +83,10 @@ namespace CUETools.Codecs uint dataLenPadded = dataLen + (dataLen & 1); _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(fccFormat); if (wavex) @@ -105,8 +108,8 @@ namespace CUETools.Codecs { _bw.Write((ushort)22); // length of WAVEX structure _bw.Write((ushort)Settings.PCM.BitsPerSample); - _bw.Write((uint)3); // speaker positions (3 == stereo) - _bw.Write((ushort)1); // PCM + _bw.Write((uint)Settings.PCM.ChannelMask); + _bw.Write((ushort)1); // PCM Guid _bw.Write((ushort)0); _bw.Write((ushort)0); _bw.Write((ushort)0x10); @@ -130,7 +133,10 @@ namespace CUETools.Codecs } _bw.Write(fccData); - _bw.Write(dataLen); + if (_finalSampleCount <= 0) + _bw.Write((uint)0xffffffff); + else + _bw.Write(dataLen); _headersWritten = true; } @@ -139,19 +145,19 @@ namespace CUETools.Codecs { if (_finalSampleCount <= 0) { - const long maxFileSize = 0x7FFFFFFEL; 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); + if (dataLenPadded + hdrLen - 8 < 0xffffffff) + { + if ((dataLen & 1) == 1) + _bw.Write((byte)0); - _bw.Seek(4, SeekOrigin.Begin); - _bw.Write((uint)(dataLenPadded + hdrLen - 8)); + _bw.Seek(4, SeekOrigin.Begin); + _bw.Write((uint)(dataLenPadded + hdrLen - 8)); - _bw.Seek((int)hdrLen - 4, SeekOrigin.Begin); - _bw.Write((uint)dataLen); + _bw.Seek((int)hdrLen - 4, SeekOrigin.Begin); + _bw.Write((uint)dataLen); + } } _bw.Close(); diff --git a/CUETools.LossyWAV/Program.cs b/CUETools.LossyWAV/Program.cs index 1f203b6..58f9acc 100644 --- a/CUETools.LossyWAV/Program.cs +++ b/CUETools.LossyWAV/Program.cs @@ -92,7 +92,7 @@ namespace LossyWAVSharp { WAVReader audioSource = new WAVReader(sourceFile, (sourceFile == "-" ? Console.OpenStandardInput() : null)); 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 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)); diff --git a/CUETools.Processor/CUEConfig.cs b/CUETools.Processor/CUEConfig.cs index ea86d36..2ccc56e 100644 --- a/CUETools.Processor/CUEConfig.cs +++ b/CUETools.Processor/CUEConfig.cs @@ -473,6 +473,8 @@ return processor.Go(); { using (TextReader reader = new StringReader(settings)) encoder.settings = encoder.settingsSerializer.Deserialize(reader) as AudioEncoderSettings; + if (encoder.settings is UserDefinedEncoderSettings && (encoder.settings as UserDefinedEncoderSettings).Path == "") + encoders.Remove(encoder); } catch {