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; } }