2 using System.Collections.Generic;
3 using System.Diagnostics;
7 namespace FDK.メディア.サウンド.WASAPI
10 /// 指定されたメディアファイル(動画, 音楽)をデコードして、CSCore.IWaveStream オブジェクトを生成する。
12 internal class Decoder : CSCore.IWaveSource
17 public bool CanSeek => ( true );
20 /// デコード後のオーディオデータの長さ[byte]。
24 get { return this._EncodedWaveData.Length; }
29 /// 先頭からのオフセット[byte]で表す。
33 get { return this._Position; }
36 if( ( 0 > value ) || ( this.Length <= value ) )
37 throw new ArgumentOutOfRangeException();
39 this._Position = value;
44 /// デコード後のオーディオデータのフォーマット。
46 public CSCore.WaveFormat WaveFormat
53 /// メディアファイル(動画、音楽)をデコードする。
55 /// <param name="path">メディアファイル(MediaFoundation でデコードできるもの)</param>
56 /// <param name="waveFormat">デコード先のフォーマット。</param>
57 public Decoder( string path, CSCore.WaveFormat waveFormat )
59 //if( ( waveFormat.WaveFormatTag != AudioEncoding.IeeeFloat ) &&
60 // ( !CSCore.WaveFormatExtensible.SubTypeFromWaveFormat( waveFormat ).Equals( SharpDX.MediaFoundation.AudioFormatGuids.Float ) ) )
62 // throw new NotSupportedException( "IEEE Float 以外の形式のフォーマットはサポートしません。" );
64 this.WaveFormat = waveFormat;
75 /// 連続した要素を読み込み、this.Position を読み込んだ要素の数だけ進める。
77 /// <param name="buffer">
78 /// 読み込んだ要素を格納するための配列。
79 /// このメソッドから戻ると、buffer には offset ~ (offset + count - 1) の数の要素が格納されている。
81 /// <param name="offset">
84 /// <param name="count">
88 /// buffer に読み込んだ要素の総数。
90 public int Read( byte[] buffer, int offset, int count )
92 // 音がめちゃくちゃになるとうざいので、このメソッド内では例外を出さないこと。
94 if( ( null == this._EncodedWaveData ) && ( null == buffer ) )
97 // offset は、0~buffer.Length-1 に収める。
98 offset = Math.Max( 0, Math.Min( buffer.Length - 1, offset ) );
100 // count は、_EncodeWaveData.Length-Position, buffer.Length-offset, count のうちの最小値とする。
101 count = Math.Min( Math.Min( this._EncodedWaveData.Length - (int) this._Position, count ), buffer.Length - offset );
106 sourceArray: this._EncodedWaveData,
107 sourceIndex: this._Position,
108 destinationArray: buffer,
109 destinationIndex: offset,
112 this._Position += count;
118 private SharpDX.MediaFoundation.MediaType _MediaType = null;
119 private byte[] _EncodedWaveData = null;
120 private long _Position = 0;
122 private void _初期化する( string path )
126 using( var sourceReader = new SharpDX.MediaFoundation.SourceReader( path ) )
127 using( var waveStream = new System.IO.MemoryStream() )
129 #region " 最初のオーディオストリームを選択し、その他のすべてのストリームを非選択にする。"
131 sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.AllStreams, false );
132 sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, true );
135 #region " デコードフォーマットを持つ MediaType を作成し、SourceReader に登録する。"
137 using( var partialMediaType = new SharpDX.MediaFoundation.MediaType() )
139 // 部分メディアタイプを作成し、オーディオフォーマットを設定する。 (オーディオフォーマットは IEEE FLOAT で固定。)
140 partialMediaType.Set<Guid>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.MajorType, SharpDX.MediaFoundation.MediaTypeGuids.Audio );
141 partialMediaType.Set<Guid>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.Subtype, SharpDX.MediaFoundation.AudioFormatGuids.Float );
142 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioNumChannels, this.WaveFormat.Channels );
143 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioSamplesPerSecond, this.WaveFormat.SampleRate );
144 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioBlockAlignment, this.WaveFormat.BlockAlign );
145 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioAvgBytesPerSecond, this.WaveFormat.BytesPerSecond );
146 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioBitsPerSample, this.WaveFormat.BitsPerSample );
147 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AllSamplesIndependent, 1 ); // TRUE
149 if( this.WaveFormat.WaveFormatTag == AudioEncoding.Extensible )
151 var wfmEx = this.WaveFormat as CSCore.WaveFormatExtensible;
152 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioChannelMask, (int) wfmEx.ChannelMask );
153 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioSamplesPerBlock, wfmEx.SamplesPerBlock );
154 partialMediaType.Set<int>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioValidBitsPerSample, wfmEx.ValidBitsPerSample );
157 // 作成したメディアタイプを sourceReader にセットする。必要なデコーダが見つからなかったら、ここで例外が発生する。
158 sourceReader.SetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, partialMediaType );
160 // 完成されたメディアタイプを取得する。
161 this._MediaType = sourceReader.GetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream );
163 // 最初のオーディオストリームが選択されていることを保証する。
164 sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, true );
168 #region " sourceReader からサンプルを取得してデコードし、waveStream へ書き込んだのち、byte[] _EncodedWaveData へ出力する。"
170 using( var pcmWriter = new System.IO.BinaryWriter( waveStream ) )
175 int dwActualStreamIndexRef = 0;
176 var dwStreamFlagsRef = SharpDX.MediaFoundation.SourceReaderFlags.None;
177 Int64 llTimestampRef = 0;
178 using( var sample = sourceReader.ReadSample(
179 SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream,
180 SharpDX.MediaFoundation.SourceReaderControlFlags.None,
181 out dwActualStreamIndexRef,
182 out dwStreamFlagsRef,
183 out llTimestampRef ) )
186 break; // EndOfStream やエラーのときも null になる。
188 // sample をロックし、オーディオデータへのポインタを取得する。
189 int cbMaxLengthRef = 0;
190 int cbCurrentLengthRef = 0;
191 using( var mediaBuffer = sample.ConvertToContiguousBuffer() )
193 // オーディオデータをメモリストリームに書き込む。
194 var audioData = mediaBuffer.Lock( out cbMaxLengthRef, out cbCurrentLengthRef );
197 byte[] dstData = new byte[ cbCurrentLengthRef ];
198 System.Runtime.InteropServices.Marshal.Copy( audioData, dstData, 0, cbCurrentLengthRef );
199 pcmWriter.Write( dstData, 0, cbCurrentLengthRef );
203 mediaBuffer.Unlock();
209 // ストリームの内容を byte 配列に出力。
210 this._EncodedWaveData = waveStream.ToArray();
218 this._EncodedWaveData = new byte[] { };
226 FDK.Utilities.解放する( ref this._MediaType );
231 private const int MF_E_INVALIDMEDIATYPE = unchecked((int) 0xC00D36B4);