public MyAudioPlayer CreateAudioPlayer(MF.SourceReader mfSourceReader)
        {
            var player = new MyAudioPlayer();

            player.Init(this._xaudio, mfSourceReader);
            return(player);
        }
        public void Init(XA.XAudio2 xaudio, MF.SourceReader mfSourceReader)
        {
            IntPtr waveFormatPtr = IntPtr.Zero;

            for (int i = 0; i < this._audioRingBuffers.Count(); ++i)
            {
                this._audioRingBuffers[i] = new NativeBufferSet();
                this._audioRingBuffers[i].ExpandNativeBuffer(OneRingBufferInitialSizeInBytes);
            }

            try
            {
                this._mfSourceReader = mfSourceReader;
                using (var mfMediaType = this._mfSourceReader.GetCurrentMediaType(MF.SourceReaderIndex.FirstAudioStream))
                {
                    int waveFormatLength;
                    // ExtracttWaveFormat は多分スペルミス。SharpDX.ComObject.ComObject(object iunknowObject) にも引数名にスペルミスあり。
                    var waveFormatWrapper = mfMediaType.ExtracttWaveFormat(out waveFormatLength, MF.WaveFormatExConvertFlags.ForceExtensible);
                    waveFormatPtr       = SharpDX.Multimedia.WaveFormat.MarshalToPtr(waveFormatWrapper);
                    this._xaSourceVoice = new XA.SourceVoice(xaudio, waveFormatWrapper, XA.VoiceFlags.NoPitch, 1.0f, false);
                    waveFormatWrapper   = null;
                }
            }
            finally
            {
                // CoTaskMemFree() 呼び出し相当。
                System.Runtime.InteropServices.Marshal.FreeCoTaskMem(waveFormatPtr);

                // マネージスレッド ID はネイティブスレッド ID とは異なる。
                // もしイベントベースで実装し、XAudio2 が用意するコールバックスレッドと比較する場合は、ネイティブスレッドをチェックするようにしたほうがよい。
                System.Diagnostics.Debug.WriteLine("CurrentThreadID = {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
            }
        }
        public static MF.SourceReader CreateSourceReader(byte[] buffer)
        {
            // 列挙体・構造体・COM インターフェイスのマッピングは下記を参考にできそう。SharpDX はある程度までラップコードを自動生成している模様。
            // http://sharpdx.org/wiki/class-library-api/mediafoundation.html
            // GUID 定数のマッピングは下記を参考にできそう。
            // https://github.com/sharpdx/SharpDX/blob/master/Source/SharpDX.MediaFoundation/Mapping-core.xml

            // マネージ参照の代入は COM スマートポインタの代入とは別物。
            // マネージ参照を代入しただけでは、明示的な Dispose すなわち IUnknown::Release() による COM 参照カウント減少は不要。
            // 通例、他の COM オブジェクトのメソッド引数に渡すなどのタイミングで COM 参照カウントが増える。
            // フィールドで保持するルート オブジェクトなど、別の COM オブジェクトから参照されないものだけを最後に Dispose すればよいはず。
            // HACK: オブジェクト間の COM 参照関連付け前に途中で例外が発生したときはどうする?
            // Dispose を明示的に呼び出すタイミングを失い、GC 任せになるのは危険なので、どのみち例外 catch 時に明示的な破棄が必要になる。
            // だとすれば C# での記述は煩雑すぎる。using で Dispose すれば、C++ のスマートポインタのように例外発生時の COM 参照カウントを安全に制御できる?
            // C# の using による後始末は、C++ のコンストラクタ・デストラクタと違ってコードのネストが深くなりがちなのが欠点。
            // using を使わずに生成すると、戻り値として返却するまでに例外発生した場合の後始末が大変。
            // かといって、using ブロックで生成したオブジェクトをそのまま返すと、
            // 破棄後の無効なオブジェクトを返すことになってしまうので、COM 参照カウントを増やしてから返す。
            // → ルート オブジェクトには using は使えない模様。たとえ QueryInterface や AddReference していても、
            // 一度でも Dispose されたオブジェクトを使おうとすると、AccessViolationException が発生する。
            // QueryInterface はオブジェクトを複製するものではなく、また SharpDX.ComObject.Dispose() で Release した後、
            // 参照カウント残数にかかわらずネイティブポインタに NULL を代入する実装になっていることが原因。
            // したがって、ルート オブジェクトは Dispose すなわち Release を明示的に制御する必要がある。

            // MediaAttributes のコンストラクタは MFCreateAttributes() 相当らしいが、第2引数 cInitialSize の説明は
            // The initial number of elements allocated for the attribute store. The attribute store grows as needed.
            // とあるので、initialSizeInBytes という引数名は間違っているように思われる。
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms701878.aspx

            MF.SourceReader mfSourceReader = null;

            try
            {
                using (var mfAttributes = new MF.MediaAttributes(1))
                {
                    mfAttributes.Set(MF.SinkWriterAttributeKeys.LowLatency, true);

                    using (var mfMediaType = new MF.MediaType())
                    {
                        mfMediaType.Set(MF.MediaTypeAttributeKeys.MajorType, MF.MediaTypeGuids.Audio);
                        //mfMediaType.Set(MF.MediaTypeAttributeKeys.Subtype, MF.AudioFormatGuids.Float); // MP3 では OK だが、WAV だと 0xC00D5212 で失敗する模様。
                        mfMediaType.Set(MF.MediaTypeAttributeKeys.Subtype, MF.AudioFormatGuids.Pcm);
                        mfSourceReader = new MF.SourceReader(buffer, mfAttributes);
                        // オリジナルの IMFSourceReader::SetCurrentMediaType() の第2引数は NULL を指定する予約引数だが、SharpDX では隠ぺいされている。
                        mfSourceReader.SetCurrentMediaType(MF.SourceReaderIndex.FirstAudioStream, mfMediaType);

                        //return mfSourceReader.QueryInterface<MF.SourceReader>();
                        //(mfSourceReader as SharpDX.IUnknown).AddReference();
                        return(mfSourceReader);
                    }
                }
            }
            catch
            {
                MyGenericsHelper.SafeDispose(ref mfSourceReader);
                throw;
            }
        }