using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using CSCore; namespace FDK.メディア.サウンド.WASAPI { /// /// 指定されたメディアファイル(動画, 音楽)をデコードして、CSCore.IWaveStream オブジェクトを生成する。 /// internal class Decoder : CSCore.IWaveSource { /// /// シークは常にサポートする。 /// public bool CanSeek => ( true ); /// /// デコード後のオーディオデータの長さ[byte]。 /// public long Length { get { return this._EncodedWaveData.Length; } } /// /// 現在の位置。 /// 先頭からのオフセット[byte]で表す。 /// public long Position { get { return this._Position; } set { if( ( 0 > value ) || ( this.Length <= value ) ) throw new ArgumentOutOfRangeException(); this._Position = value; } } /// /// デコード後のオーディオデータのフォーマット。 /// public CSCore.WaveFormat WaveFormat { get; protected set; } /// /// メディアファイル(動画、音楽)をデコードする。 /// /// メディアファイル(MediaFoundation でデコードできるもの) /// デコード先のフォーマット。 public Decoder( string path, CSCore.WaveFormat waveFormat ) { //if( ( waveFormat.WaveFormatTag != AudioEncoding.IeeeFloat ) && // ( !CSCore.WaveFormatExtensible.SubTypeFromWaveFormat( waveFormat ).Equals( SharpDX.MediaFoundation.AudioFormatGuids.Float ) ) ) //{ // throw new NotSupportedException( "IEEE Float 以外の形式のフォーマットはサポートしません。" ); //} this.WaveFormat = waveFormat; this._初期化する( path ); } public void Dispose() { this._解放する(); } /// /// 連続した要素を読み込み、this.Position を読み込んだ要素の数だけ進める。 /// /// /// 読み込んだ要素を格納するための配列。 /// このメソッドから戻ると、buffer には offset ~ (offset + count - 1) の数の要素が格納されている。 /// /// /// buffer に格納を始める位置。 /// /// /// 読み込む最大の要素数。 /// /// /// buffer に読み込んだ要素の総数。 /// public int Read( byte[] buffer, int offset, int count ) { // 音がめちゃくちゃになるとうざいので、このメソッド内では例外を出さないこと。 if( ( null == this._EncodedWaveData ) && ( null == buffer ) ) return 0; // offset は、0~buffer.Length-1 に収める。 offset = Math.Max( 0, Math.Min( buffer.Length - 1, offset ) ); // count は、_EncodeWaveData.Length-Position, buffer.Length-offset, count のうちの最小値とする。 count = Math.Min( Math.Min( this._EncodedWaveData.Length - (int) this._Position, count ), buffer.Length - offset ); if( 0 < count ) { Array.Copy( sourceArray: this._EncodedWaveData, sourceIndex: this._Position, destinationArray: buffer, destinationIndex: offset, length: count ); this._Position += count; } return count; } private SharpDX.MediaFoundation.MediaType _MediaType = null; private byte[] _EncodedWaveData = null; private long _Position = 0; private void _初期化する( string path ) { try { using( var sourceReader = new SharpDX.MediaFoundation.SourceReader( path ) ) using( var waveStream = new System.IO.MemoryStream() ) { #region " 最初のオーディオストリームを選択し、その他のすべてのストリームを非選択にする。" //---------------- sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.AllStreams, false ); sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, true ); //---------------- #endregion #region " デコードフォーマットを持つ MediaType を作成し、SourceReader に登録する。" //---------------- using( var partialMediaType = new SharpDX.MediaFoundation.MediaType() ) { // 部分メディアタイプを作成し、オーディオフォーマットを設定する。 (オーディオフォーマットは IEEE FLOAT で固定。) partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.MajorType, SharpDX.MediaFoundation.MediaTypeGuids.Audio ); partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.Subtype, SharpDX.MediaFoundation.AudioFormatGuids.Float ); partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioNumChannels, this.WaveFormat.Channels ); partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioSamplesPerSecond, this.WaveFormat.SampleRate ); partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioBlockAlignment, this.WaveFormat.BlockAlign ); partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioAvgBytesPerSecond, this.WaveFormat.BytesPerSecond ); partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioBitsPerSample, this.WaveFormat.BitsPerSample ); partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AllSamplesIndependent, 1 ); // TRUE if( this.WaveFormat.WaveFormatTag == AudioEncoding.Extensible ) { var wfmEx = this.WaveFormat as CSCore.WaveFormatExtensible; partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioChannelMask, (int) wfmEx.ChannelMask ); partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioSamplesPerBlock, wfmEx.SamplesPerBlock ); partialMediaType.Set( SharpDX.MediaFoundation.MediaTypeAttributeKeys.AudioValidBitsPerSample, wfmEx.ValidBitsPerSample ); } // 作成したメディアタイプを sourceReader にセットする。必要なデコーダが見つからなかったら、ここで例外が発生する。 sourceReader.SetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, partialMediaType ); // 完成されたメディアタイプを取得する。 this._MediaType = sourceReader.GetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream ); // 最初のオーディオストリームが選択されていることを保証する。 sourceReader.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, true ); } //---------------- #endregion #region " sourceReader からサンプルを取得してデコードし、waveStream へ書き込んだのち、byte[] _EncodedWaveData へ出力する。" //----------------- using( var pcmWriter = new System.IO.BinaryWriter( waveStream ) ) { while( true ) { // 次のサンプルを読み込む。 int dwActualStreamIndexRef = 0; var dwStreamFlagsRef = SharpDX.MediaFoundation.SourceReaderFlags.None; Int64 llTimestampRef = 0; using( var sample = sourceReader.ReadSample( SharpDX.MediaFoundation.SourceReaderIndex.FirstAudioStream, SharpDX.MediaFoundation.SourceReaderControlFlags.None, out dwActualStreamIndexRef, out dwStreamFlagsRef, out llTimestampRef ) ) { if( null == sample ) break; // EndOfStream やエラーのときも null になる。 // sample をロックし、オーディオデータへのポインタを取得する。 int cbMaxLengthRef = 0; int cbCurrentLengthRef = 0; using( var mediaBuffer = sample.ConvertToContiguousBuffer() ) { // オーディオデータをメモリストリームに書き込む。 var audioData = mediaBuffer.Lock( out cbMaxLengthRef, out cbCurrentLengthRef ); try { byte[] dstData = new byte[ cbCurrentLengthRef ]; System.Runtime.InteropServices.Marshal.Copy( audioData, dstData, 0, cbCurrentLengthRef ); pcmWriter.Write( dstData, 0, cbCurrentLengthRef ); } finally { mediaBuffer.Unlock(); } } } } // ストリームの内容を byte 配列に出力。 this._EncodedWaveData = waveStream.ToArray(); } //----------------- #endregion } } catch { this._EncodedWaveData = new byte[] { }; } this._Position = 0; } private void _解放する() { FDK.Utilities.解放する( ref this._MediaType ); } #region " Win32 " //---------------- private const int MF_E_INVALIDMEDIATYPE = unchecked((int) 0xC00D36B4); //---------------- #endregion } }