C# 如何将IDirectSound添加到IFilterGraph2?

C# 如何将IDirectSound添加到IFilterGraph2?,c#,com,directshow,naudio,directshow.net,C#,Com,Directshow,Naudio,Directshow.net,我已经在谷歌上搜索了好几天了,但我想不出这是怎么回事 我有一个WPF项目,我想在多个屏幕上同时播放视频和独立的音频系统。对于播放视频,我们使用,这依赖于。对于其他系统,我们希望使用更现代的东西,比如核心音频API via。我们控制环境,只需支持Windows 7(将来可能会迁移到8) WPFMediaKit的MediaUriElement允许您设置其AudioRenderer,但由于。。。直接声音?我不完全确定限制从何而来,但这是一个问题,因为我们所有的音频设备都有非常相似的名称(在31个字符的

我已经在谷歌上搜索了好几天了,但我想不出这是怎么回事

我有一个WPF项目,我想在多个屏幕上同时播放视频和独立的音频系统。对于播放视频,我们使用,这依赖于。对于其他系统,我们希望使用更现代的东西,比如核心音频API via。我们控制环境,只需支持Windows 7(将来可能会迁移到8)

WPFMediaKit的
MediaUriElement
允许您设置其
AudioRenderer
,但由于。。。直接声音?我不完全确定限制从何而来,但这是一个问题,因为我们所有的音频设备都有非常相似的名称(在31个字符的范围内很可能不是唯一的)。因此,计划是修改源代码,并允许它接受表示DirectSound设备的GUID,并更改其行为以通过该GUID进行查找。向用户显示音频设备端点列表的代码将依赖于核心音频API中NAudio的
MMDeviceEnumerator

我已经走了这么远:

private static void DemonstrateTheIssue() {
    SpeechSynthesizer speech = new SpeechSynthesizer();
    MemoryStream stream = new MemoryStream();
    SpeechAudioFormatInfo synthFormat = new SpeechAudioFormatInfo(EncodingFormat.Pcm, 88200, 16, 1, 16000, 2, null);
    speech.SetOutputToAudioStream(stream, synthFormat);
    speech.Speak("This is a test. This is only a test.");
    stream.Position = 0;
    RawSourceWaveStream reader = new RawSourceWaveStream(stream, new WaveFormat());

    MMDeviceEnumerator coreAudioDeviceEnumerator = new MMDeviceEnumerator();
    MMDeviceCollection coreAudioAudioEndPoints = coreAudioDeviceEnumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active);
    MMDevice coreAudioSpdif = coreAudioAudioEndPoints.First(ep => ep.FriendlyName.Contains("S/PDIF")); // just an example. In the real application, would be selected by user and the GUID would be stored instead of the FriendlyName
    string spdifGuidValue = coreAudioSpdif.Properties[PropertyKeys.PKEY_AudioEndpoint_GUID].Value as string;
    Guid spdifGuid = Guid.Parse(spdifGuidValue);

    DirectSoundOut directSoundSpdif = new DirectSoundOut(spdifGuid);
    directSoundSpdif.Init(reader);
    directSoundSpdif.Play();
}
这个代码有效。当我检查NAudio的
DirectSoundOut
源代码时,我可以看到它从
dsound.dll
调用
DirectSoundOut.DirectSoundCreate
,从而初始化
IDirectSound
实例。我不完全确定我能用那个实例做什么。那么,回到这个谜团的另一面

让我们看看WPFMediaKit是怎么做的,当我给它一个
数字音频(s/PDIF)(High-De)的
音频渲染器
时,通过源代码挖掘,我最终发现了
AddFilterByName(IGraphBuilder graphBuilder,Guid设备分类,字符串友好名称)
,它最终调用了
AddFilterByDevice(IGraphBuilder图形生成器、DsDevice)
。好的,这使用了它自己的包装类,
DsDevice
…它包装了
IMoniker
和其他一些东西,包括
IPropertyBag
附加到
IMoniker
实例的一些属性。我在跟踪消失到COM之前看到的最后一行C代码称为:
filterGraph.AddSourceFilterForMoniker(device.Mon,null,device.Name,out filter)
filterGraph
是一个
IFilterGraph2
实例和
device.Name
从名字对象的属性包返回
“FriendlyName”

我觉得我离得很近,但我只是无法将这些点连接起来。我如何才能拿一个
IDirectSound
实例,告诉
filterGraph
在其上播放音频?我是否需要为
IDirectSound
取一个名字?如果需要,怎么做?有没有其他我不知道的方法?或者……我可以创建/编写/获取对我自己的过滤器和音频的引用告诉
filterGraph
?如果是这样,我该怎么做


请像我是个孩子一样回答这个问题--这些东西真的让我不知所措。

DirectSound和DirectShow是两个不同的API。没有直接将DirectSound对象添加到DirectShow筛选器图中,因此您无法做到这一点

有两种方法可以解决此问题:您可以坚持您已经持有一个
IDirectSound
接口指针,并希望在DirectShow中使用它。在这种情况下,您必须实现一个自定义音频渲染器过滤器,该过滤器将由此DirectSound接口支持,并且您的渲染器将所有音频转发到设备如果你感兴趣的话。你真的不想这样做。特别是,你不能只在C#中实现这一点

第二种方法是找到一个现有的DirectShow音频渲染器,它正在使用您感兴趣的设备。然后使用它,它将通过同一个DirectSound设备播放,只是不完全是您手上的指针

有关第二种方法的更多详细信息。DirectShow有一个特殊类别,其中列出了所有音频渲染器:。枚举该类别中的筛选器时,可以枚举为每个现有设备创建的实例

此筛选器充当音频设备的包装器 用户系统上可用的音频设备,请使用ICreateDevEnum 与音频渲染器类别的接口 (CLSID_AudioRenderCategory)。对于每个音频设备 渲染器类别包含两个筛选器实例 对应于DirectSound渲染器,另一个对应于 音频渲染器(WaveOut)过滤器。DirectSound实例具有 友好名称“DirectSound:DeviceName”,其中DeviceName是名称 WaveOut实例的友好名称为DeviceName


您可以在那里找到合适的设备,然后可以将此过滤器用作常规音频渲染器(即,将其添加到图形中,然后将其连接到图形中)。但是,筛选器不会公开其内部使用的
IDirectSound
指针。

感谢您的全面回答。第二种方法让我感兴趣,但我已经走上了这条道路,但没有运气——也许我错过了什么。WPFMediaKit通过DirectShowLib,使用
CreateDevEnum
IEnumoniker
来查找设备名称。但是,正如我所说,我的名称在31个字符内不是唯一的。我尝试使用枚举设备的名字对象的属性包来查找与核心音频设备相关的内容,但失败了;我不知道要查找什么密钥,并且
IPropertyBag
确实很难找到所有密钥的列表。您有任何密钥吗其他建议?在DirectShow中,DirectSound设备名称不会在第32个字符处进行修剪。您是否在DirectSound一侧进行了修剪?或者,名称完全相同?从
IMoniker
返回的名称将在第32个字符处进行修剪,即
数字音频(S/PDIF)(高De)
从设备.GetPropBagValue(“FriendlyName”)返回