MIDI 输出设置器
允许更改 Vista 和 Windows 7 中的默认 MIDI 输出设备
概述
普通用户通常只有一个 MIDI 播放设备,即内置的软件合成器“Microsoft GS Wavetable Synth”,所有 MIDI 文件都通过它播放——这没问题。然而,许多用户(包括我和你,因为你正在阅读这篇文章)拥有更优越的或多个设备,无论是软件还是硬件的,所以我们想要/需要能够选择系统使用的播放设备。
在 Vista 之前,你可以通过控制面板中的声音和音频设备|音频来选择播放设备。不知何故,微软决定在 Vista 中移除此选项,并且至今为止的任何服务包或 Windows 7 (Beta) 中都没有重新添加它,尽管有许多关于将其包含在内的请求。
2008 年 2 月,我制作了一个微小的小应用程序来解决这个问题,它在我的 Vista 系统上运行良好,并且我在其他地方公开提供了。但它并不适用于所有人。有些人根本没有列出任何设备,另一些人只列出了一些可用设备。我目前正在编写一个全面的托管包装器,涵盖所有 MIDI 相关的内容,我意识到我应该重新审视这个问题,并将其作为该项目的一部分来彻底解决。本文就是结果。
我之前解决方案的问题在于它使用注册表来检索已安装设备的信息。并非所有设备都会创建这些注册表条目,因此应用程序并未显示所有设备。
解决方案
获取设备
检索所有 MIDI 输出设备原来很简单——尽管这需要使用一点 PInvoke,调用 *winmm.dll* 中的函数:midiOutGetNumDevs
。这是一个非常简单的函数。它不接受任何参数,只返回输出设备的数量。
[DllImport("winmm.dll")]
static extern UInt32 midiOutGetNumDevs();
然后我们可以简单地使用任何设备 ID,从 0 到该函数结果减 1(它是基于零的)并保存它,但这不够用户友好。没有人知道他们设备的 ID 号,只知道名称,所以我们需要另一个函数来获取每个设备的信息,包括名称,即 midiOutGetDevCaps
。这个函数稍微复杂一些。
[DllImport("winmm.dll")]
static extern UInt32 midiOutGetDevCaps(Int32 uDeviceID,
ref MIDIOUTCAPS lpMidiOutCaps, UInt32 cbMidiOutCaps);
第一个参数是我们想要查询的设备的 ID。设备 ID 是顺序的且基于零的,所以我们可以简单地使用一个基于上面检索到的结果的 foreach
循环并递归调用此函数。
第二个参数是一个结构体 MIDIOUTCAPS
,函数将在此结构体中存储设备信息。
[StructLayout(LayoutKind.Sequential)]
struct MIDIOUTCAPS
{
public UInt16 wMid;
public UInt16 wPid;
public UInt32 vDriverVersion;
[MarshalAs(UnmanagedType.ByValTStr,
SizeConst = Constants.MAXPNAMELEN)]
public string szPname;
public UInt16 wTechnology;
public UInt16 wVoices;
public UInt16 wNotes;
public UInt16 wChannelMask;
public UInt32 dwSupport;
}
我们只对 szPname
感兴趣。常量 MAXPNAMELEN
来自 *MMSystem.h* 文件(我没有将其包含在项目中,因为大部分内容是不需要的),其值为 32。
第三个参数是 MIDIOUTCAPS
结构体的大小。该函数返回一个值,指示调用函数时发生的错误(如果有)。我们只关心没有错误的情况(当它返回 MMSYSERR_NOERROR
时,该常量的值为 0
)。
输出类
为了从托管世界调用此函数(针对每个 ID),我们需要一个类 Output
,它在构造函数中接收一个 MIDIOUTCAPS
。
public class Output
{
public Output(Int32 id, MIDIOUTCAPS caps)
{
ID = id;
Name = caps.szPname;
}
public Int32 ID
{
get;
private set;
}
public string Name
{
get;
private set;
}
}
现在,我们可以递归调用 midiOutGetDevCaps
并返回一个只读列表。
public static void Load()
{
outputs = null;
List<output> devices = new List<output>();
UInt32 numberOfDevices = Functions.midiOutGetNumDevs();
if (numberOfDevices > 0)
{
for (Int32 i = 0; i < numberOfDevices; i++)
{
MIDIOUTCAPS caps = new MIDIOUTCAPS();
if (Functions.midiOutGetDevCaps(i, ref caps,
(UInt32)Marshal.SizeOf(caps)) == Constants.MMSYSERR_NOERROR)
{
devices.Add(new Output(i, caps));
}
}
}
outputs = devices.AsReadOnly();
}
保存 ID
Windows 使用一个注册表设置,其中包含默认 MIDI 输出设备的 ID。键是:HKEY_CURRENT_USER\Software\Microsoft\ActiveMovie\devenum\{4EFE2452-168A-11D1-BC76-00C04FB9453B}\Default MidiOut Device,需要更改的值是一个名为 MidiOutId
的 DWORD
。
我们只需要将此值更改为我们首选设备的 ID 即可完成。
// Where value is the desired ID
RegistryKey defaultKey = null;
try
{
defaultKey = Registry.CurrentUser.OpenSubKey(DefaultMidiOutDevice,
RegistryKeyPermissionCheck.ReadWriteSubTree,
RegistryRights.SetValue);
defaultKey.SetValue(MidiOutId, value);
}
finally
{
if (defaultKey != null)
{
defaultKey.Close();
}
}
结论
除非微软在后来的操作系统中对注册表或 *winmm.dll* 的这部分进行破坏性更改,否则这应该适用于所有未来的版本。我在 XP(不需要,但它也能工作!)、Vista 和 Windows 7 Beta(build 7000)上进行了测试。
我包含了二进制文件(以及源代码),供那些只需要应用程序来重新控制 MIDI 系统的人使用。
参考文献
历史
- 2009 年 5 月 10 日:初始版本