2013年3月22日星期五

SDL简介(转)

什么是SDL?
即 Simple DirectMedia Layer,使用 LGPL 许可证。
免费的跨平台多媒体应用编程接口
用于游戏、游戏开发工具、模拟器、样本演示、多媒体应用等
它能做什么?
视频、音频、事件、CDROM支持、线程、计时器、各种图象文件格式读取、快速绘图、混音、游戏杆支
持、网络、MPEG解码等等,且CPU字节顺序无关。
大体上与DirectX比较对应关系如下:
SDL_Video、SDL_Image、OpenGL —— DirectDraw、Direct3D
SDL_Audio、SDL_Mixer —— DirectSound
SDL_Joystick、SDL_Base —— DirectInput
SDL_Net —— DirectPlay
SMPEG、SDL_Video、SDL_Audio、SDL_Sound、SDL_Filter —— DirectShow
字体、窗口管理等其他实用工具和大量样例
支持哪些平台?
Linux 随系统安装
Win32 需一个到几个较小的DLL
BeOS
MacOS, MacOS X
其他非官方移植
可以在哪些编程语言中使用?
几乎所有!SDL本身用 C 写成,有各种语言的接口。简单的函数调用,不需要COM。
个人观点(不代表任何团体和他人)
总的来说,SDL并不怎么优秀,但它是少数的DX替代品之一。有人会问为什么非得不用DX,也没有非得
不用,至少懒人总是希望用更少的功夫做更多的 事。对大多数人来说,无论DX、OpenGL还是SDL,都是
低层API,尤其DX,如果您精通DX,那么恭喜,您的硬件知识一定也不错。留心的话就会发 现今天的游
戏很多都提供选项:Direct3D、OpenGL、Glide还是Software,既是说很多游戏厂商都根据需要设计了
自己的高层API, 底层是可以替换的。那么也许有人说水平高的厂商都是充分开发硬件功能才有高性能
的表现,那么我举个例子,很多2D游戏的渲染虽然用了 DirectDraw,但却是纯软件在内存帧缓冲区渲
染,最后调用DirectDraw将图象Swap到屏幕。对于2D游戏加今天的机器配置,GDI加直 接帧缓冲操作已
经足够,需要更先进的渲染能力时就要使用3D技术,这也是在DX8中DirectDraw和Direct3D都被基于3D
的 DirectGraphics替换掉的原因。但最主要的是,选择SDL意味着跨平台。
回到正题。象DX一样,SDL的各个部分是可以单独使用的,但必须有SDL_Base。窗口消息管理方式很古
董,写过Win32程序的一定还记得switch...case,没错SDL用就是这个,但完全可以不用它的。SDL的 C
风格很浓,就像DirectX的COM风格很浓一样,用非 C 类语言的人会更愿意封装一下再用。
与DX相比,SDL有更快的启动速度,方便的调试(调试过DX程序吗?:( ),简洁的接口,很小的运行时
库,当然首要的是跨平台。SDL直接支持很多媒体文件格式,与DX比起来非常的方便。但SDL即简单直接
访问媒体层,不象DX支持那么多功能,当然也因为不是每个平台都能提供那么多功能。
样例代码上说,比起DX,SDL的样例非常短小精悍,程序流程是直线式,效果上一点也不差。非C语言的
翻译版本保留了C的风格,没有利用先进的语言特性,是个遗憾。至于帮助文件,绝对不如DirectX,很
多有用的信息是头文件里的注释。但毕竟 C 接口比COM简单得多,看看函数名和样例也就会用了。但没
有中文资料,不爱看英文的可能会头痛。可能的话大家分工翻译一下,毕竟比DX文档少多了。
详细信息在哪里?
http://www.libsdl.org/ SDL首页
http://www.delphi-jedi.org/ Delphi接口项目首页
http://jsdl.sourceforge.net/ Java接口首页
http://csgl.sourceforge.net C#接口首页
http://phpsdl.sourceforge.net php接口首页
使用SDL的游戏有哪些?
http://www.libsdl.org/games.php

有个列表,总之非常多

云风的 BLOG 思绪来得快去得也快,偶尔会在这里停留

http://blog.codingnow.com/

ActiveX、OLE和COM介绍

ActiveX、OLE和COM介绍  

2007-07-23 10:53:14|  分类: 技术速递 |字号 订阅

熟悉面向对象编程和网络编程的人一定对ActiveX、OLE和COM/DCOM这些概念不会陌生,但是它们之间究竟是什么样的关系,许多人都还是比
较模糊的。
在具体介绍它们的关系之间,我们还是先明确组件(Component)和对象(Object)之间的区别。组件是一个可重用的模块,它是由一组处理
过程、数据封装和用户接口组成的业务对象(Rules Object)。组件看起来像对象,但不符合对象的学术定义。它们的主要区别是:
1)组件可以在另一个称为容器(有时也称为承载者或宿主)的应用程序中使用,也可以作为独立过程使用;
2)组件可以由一个类构成,也可以由多个类组成,或者是一个完整的应用程序;
3)组件为模块重用,而对象为代码重用。
现在,比较流行的组件模型有COM(Component Objiect Module,对象组件模型)/DCOM(Distributed COM,分布式对象组件模型)和CORBA
(Common Object Request Broker Architecture,公共对象请求代理体系结构)。到这里,已经出现了与本文相关的主题COM,而CORBA与本
文无关,就不作介绍。之所以从组件与对象的区别说起,是想让大家明确COM和CORBA是处在整个体系结构的最底层,如果暂时对此还不能理
解,不妨继续往下看,最后在回过头看一看就自然明白了。
现在开始阐述ActiveX、OLE和COM的关系。首先,让大家有一个总体的概念,从时间的角度讲,OLE是最早出现的,然后是COM和ActiveX;从
体系结构角度讲,OLE和ActiveX是建立在COM之上的,所以COM是基础;单从名称角度讲,OLE、ActiveX是两个商标名称,而COM则是一个纯技
术名词,这也是大家更多的听说ActiveX和OLE的原因。
既然OLE是最早出现的,那么就从OLE说起,自从Windows操作系统流行以来,“剪贴板”(Clipboard)首先解决了不同程序间的通信问题(
由剪贴板作为数据交换中心,进行复制、粘贴的操作),但是剪贴板传递的都是“死”数据,应用程序开发者得自行编写、解析数据格式的
代码,于是动态数据交换(Dynamic Data Exchange,DDE)的通信协定应运而生,它可以让应用程序之间自动获取彼此的最新数据,但是,
解决彼此之间的“数据格式”转换仍然是程序员沉重的负担。对象的链接与嵌入(Object Linking and Embedded,OLE)的诞生把原来应用
程序的数据交换提高到“对象交换”,这样程序间不但获得数据也同样获得彼此的应用程序对象,并且可以直接使用彼此的数据内容,其实
OLE是Microsoft的复合文档技术,它的最初版本只是瞄准复合文档,但在后续版本OLE2中,导入了COM。由此可见,COM是应OLE的需求而诞生
的,所以虽然COM是OLE的基础,但OLE的产生却在COM之前。
COM的基本出发点是,让某个软件通过一个通用的机构为另一个软件提供服务。COM是应OLE的需求而诞生,但它的第一个使用者却是OLE2,所
以COM与复合文档间并没有多大的关系,实际上,后来COM就作为与复合文档完全无关的技术,开始被广泛应用。这样一来,Microsoft就开始
“染指”通用平台技术。但是COM并不是产品,它需要一个商标名称。而那时Microsoft的市场专家们已经选用了OLE作为商标名称,所以使用
COM技术的都开始贴上了OLE的标签。虽然这些技术中的绝大多数与复合文档没有关系。Microsoft的这一做法让人产生这样一个误解OLE是仅
指复合文档呢?还是不单单指复合文档?其实OLE是COM的商标名称,自然不仅仅指复合文档。但Microsoft自己恐怕无法解释清楚,这要花费
相当的精力和时间。
于是,随着Internet的发展,在1996年春,Microsoft改变了主意,选择ActiveX作为新的商标名称。ActiveX是指宽松定义的、基于COM的技
术集合,而OLE仍然仅指复合文档。当然,ActiveX最核心的技术还是COM。ActiveX和OLE的最大不同在于,OLE针对的是桌面上应用软件和文
件之间的集成,而ActiveX则以提供进一步的网络应用与用户交互为主。到这里,大家应该对ActiveX、OLE和COM三者的关系有了一个比较明
确的认识,COM才是最根本的核心技术,所以下面的重点COM。
让对象模型完全独立于编程语言,这是一个非常新奇的思想。这一点从C++和Java的对象概念上,我们就能有所了解。但所谓COM对象究竟是
什么呢?为了便于理解,可以把COM看作是某种(软件)打包技术,即把它看作是软件的不同部分,按照一定的面向对象的形式,组合成可以
交互的过程和以组支持库。COM对象可以用C++、Java和VB等任意一种语言编写,并可以用DLL或作为不同过程工作的执行文件的形式来实现。
使用COM对象的浏览器,无需关心对象是用什么语言写的,也无须关心它是以DLL还是以另外的过程来执行的。从浏览器端看,无任何区别。
这样一个通用的处理技巧非常有用。例如,由用户协调运行的两个应用,可以将它们的共同作业部分作为COM对象间的交互来实现(当然,现
在的OLE复合文档也能做到)。为在浏览器中执行从Web服务器下载的代码,浏览器可把它看作是COM对象,也就是说,COM技术也是一种打包
可下载代码的标准方法(ActiveX控件就是执行这种功能的)。甚至连应用与本机OS进行交互的方法也可以用COM来指定,例如在Windows和
Windows NT中用的是新API,多数是作为COM对象来定义的。可见,COM虽然起源于复合文档,但却可有效地适用于许多软件问题,它毕竟是处
在底层的基础技术。用一句话来说,COM是独立于语言的组件体系结构,可以让组件间相互通信。随着计算机网络的发展,COM进一步发展为
分布式组件对象模型,这就是DCOM,它类似于CORBA的ORB,本文对此将不再做进一步的阐述。
通过上面的讲述相信大家一定对ActiveX、OLE和COM/DCOM的关系有了一个清楚的了解
作者:戴宗友   汪涛

VC++之多媒体编程之录音

一、创建对话框应用程序

二、编辑对话框资源
按钮ID与标题
IDOK4                              退出
IDC_BUTTON_REC         录音
IDC_BUTTON_STOP       停止
IDC_BUTTON_PLAY         播放

三、添加变量、函数
       1、添加变量

       2、添加函数

四、添加代码
       1、在“stdafx.h”文件内添加包含语句
// stdafx.h : include file for standard system include files,......
#include<vfw.h>
#pragma comment(lib,"vfw32.lib")
//{{AFX_INSERT_LOCATION}}
#endif
       2、为函数添加代码
void CMusicDlg::OnButtonPlay()
{
// TODO: Add your control notification handler code here
   if(MCIWndCanPlay(musicget))//判断是否可以播放
   MCIWndPlay(musicget);
}
void CMusicDlg::OnButtonRec()
{
// TODO: Add your control notification handler code here
MCIWndClose(musicget);
musicget=MCIWndCreate(this->m_hWnd,::AfxGetApp()->m_hInstance,WS_CAPTION,NULL);
MCIWndNew(musicget,"waveaudio"); //打开录音设备
    if(MCIWndCanRecord(musicget))     //判断是否可以录音
      MCIWndRecord(musicget);       //录音
}
void CMusicDlg::OnButtonStop()
{
// TODO: Add your control notification handler code here
   MCIWndStop(musicget);
}
void CMusicDlg::OnOk4()
{
// TODO: Add your control notification handler code here
CDialog::OnOK();
}
五、编译
六、运行

七、函数说明
       1、MCIWndCreate函数声明
        HWND MCIWndCreate(HWND hwndParaent,HINSTANCE hInstance,DWORD dwStyle,LPSTR szFilw)
       hwndParent:指向父窗口的句柄。
       hInstance:与MCIWnd窗口相关联的模块实例。
       dwStyle:窗口风格。
       szFile:包含MCI设备和将打开文件名的字符串指针。
       功能:注册MCI窗口类,同时创建一使用MCI服务的MCIWnd窗口。函数调用成功,返回MCI窗口句柄。
        2、MCEWndNew宏声明
       LONG MCIWndNew(hwnd,lp)
       hwnd:指向MCIWnd窗口的句柄。
       ip:包含MCI设备名的指向内存的指针。
       功能:创建一新文件用于当前MCI设备。可以使用该宏发送一MCEWND_NEW消息。函数调用成功,返回零。
       3、MCIWndCanRecord函数声明
        BOOL MCIWndCanRecord(hwnd)
        hwnd:指向MCIWnd窗口的句柄。
        功能:判断一MCI设备是否支持录音,可以使用该宏发送一MCEWNDM_CAN_RECORD消息。设备支持录音,返回TRUE;否则返回FALSE。
       4、MCIWndRecord函数声明
      LONG MCIWndRecord(hwnd)
        hwnd:指向MCIWnd窗口的句柄。
        功能:MCI设备开始录音。函数调用成功,返回零值。
       5、MCIWndPlay函数声明
        LONG MCIWndPlay(hwnd)
        hwnd:指向MCIWnd窗口的句柄。
        功能:发一命令给MCI设备开始播放。函数调用成功,返回零值。

VC++多媒体编程

 标题成绩:我念写法式节制IDE接心的光驱,但是却找没有到正在VC里该如何做,好比我知讲正在MMC(Multi-Media Commands)里有一条下令是Test Unit Ready(00h),但是我没有知讲正在VC里如何把那条下令收给光驱,

  解问:

  对一样平常的操做法式去讲,Visual C++ 可以或许讲是搜罗万象,但是令人遗憾的是,几远出有传讲风闻过Visual C++ 对多媒体供给过头孟抚撑,以致有人讲Visual C++没有摇率多媒体编程。如果我们完备操做Visual C++的类库而没有念面把戏的话,死怕连最一个简朴的RPG游戏堵没有出去。对一个需供除夜量动绘、声音的多媒体操做法式去讲,Visual C++ 最多供给了一个中壳,而格氏苹个劣秀的声音、动绘引擎的任务,便降到了法式员的身上。

  正在而后各章节中,将陆绝介绍除夜家如何斥天阿谁引擎。

  1 对声音的措置

  1.1媒体节重朴心

  MCI(Media Control Inte***ce)媒体节重朴心是MircroSoft供给的一组多媒体设备战文件的尺度接心,它的益处是可以或许便当天节制尽除夜多数多媒体设备搜罗 音频、视频、影碟、录相称多媒体装被霈而没有需供知讲它们的内部工做自遇。但是前人云R∩也萧何,败也萧何。MCI虽然看上往下除夜齐,但对一些初级操做 去讲,它是远远步杌的。好比Visual C++虽然看上往无所事事,却需供法式员自家♀天多媒体引擎一样。对MCI指令散,我们将只做简朴介绍,重面放正在后里的波形文件混音器上。

  MCI的节制格式:

  一样平常讲去,法式员操做两个函数便可以或许与MCI挨交讲了:

  MCIERROR mciSendCommand(MCIDEVICEID wDeviceID, UINT uMsg,

  DWORD dwFlags, DWORD dwParam );

  下令字符串格式,用接远于仄居糊心用语的格式收支节制下令,开用于初级编程如VB、TOOLBOOK涤耄

  MCIERROR mciSendString(LPCTSTR lpszCommand,LPTSTR lpszReturnStr

  ing, UINT cchReturn, HANDLE hwndCallback

  );

  下令消息格式,用专业语法收支节制消息,开用于VC等发言编程,此格式直接与MCI设备挨交讲。

  对mciSendCommand,第一个参数指定了设备标识,阿谁标识会正在法式钥氦开MCI装笨啾由体系供给。第两个参数指定将如何节制装被霈具体 请查阅后里“MCI指令”一栏。第三个参数为访谒标识,第四个参数一样平常为一个数据挨算,标识法式正在访谒MCI时要的一些疑息。颖ヘ具体原料,请查阅 本光盘配套书。

  对mciSendString,第一个参数为一串节制字符串,返回疑息由系徒壁进第两个参数,第三个参数指明返回疑息的最除夜少度,若对MCI拆配设定了"notify"标识表记标帜则需供正在第四个参数挖上返回窗心句柄。

  举例:

  mciSendCommand(DeviceID,MCI_CLOSE,NULL,NULL);//启闭一个MCI装被龌

  mciSendString("open aaa.avi",0,0,0); //挨开文件"aaa.avi";

  MCI的设备范例:

  MCI的设备范例有:

  设备形貌

  形貌字符串

  申明

  MCI_ALL_DEVICE_ID

  统统设备

  MCI_DEVTYPE_ANIMATION

  Animation

  动绘设备

  MCI_DEVTYPE_CD_AUDIO

  Cdaudio

  CD音频

  MCI_DEVTYPE_DAT

  Dat

  数字音频

  MCI_DEVTYPE_DIGITAL_VIDEO

  Digitalvideo

  数琢坑频

  MCI_DEVTYPE_OTHER

  Other

  已界讲设备

  MCI_DEVTYPE_OVERLAY

  Overlay

  堆叠视频

  MCI_DEVTYPE_SCANNER

  Scanner

  扫描仪

  MCI_DEVTYPE_SEQUENCER

  Sequencer MIDI

  序列器

  MCI_DEVTYPE_VCR

  Vcr

  摇陆录相机

  MCI_DEVTYPE_VIDEODISC

  Videodisc

  激光柿犹

  MCI_DEVTYPE_WAVEFORM_AUDIO

  waveaudio Wave

  音频

  对已正在上里界岛媚MCI装被霈映雩可检察system.ini文件中[mci]部门,比方:

  [mci]

  cdaudio=mcicda.drv

  sequencer=mciseq.drv

  waveaudio=mciwave.drv

  avivideo=mciavi.drv

  videodisc=mcipionr.drv

  vcr=mcivisca.drv

  ActiveMovie=mciqtz.drv

  QTWVideo=mciqtw.drv

  MPEGVideo=C:\PROGRA~1\XING\XINGMP~1\xmdrv95.dll

  个中末了两句告别指了然Apple的QuickTime装被霈设备名为"QTWVidio"、MPEG记忆装被霈设备名为"MPEGVideo"。

  正在MCI编程中,既可以或许将设备形貌当设备名,也能够或许将形貌字符串当设备名,一个极吨Ы擦的格式识台度员没有要正在法式中指定设备名,Windows将自动按照文件扩大名辨认设备范例。

  举个例子去讲,挨开一个多媒体文件有以下三种格式:

  [1]:自动辨认:挨开一个"WAV"文件

  MCI_OPEN_PARMS mciOpen;

  mciOpen.lpstrDeviceType=0;

  mciOpen.lpstrElementName="aaa.wav";

  mciSendCommand(NULL,MCI_OPEN, MCI_OPEN_ELEMENT,

  (DWORD)&mciOpen);

  [2]:指定设备形貌:挨开CD播放器

  MCI_OPEN_PARMS mciOpen;

  mciOpen.lpstrDeviceType=(LPSTR)MCI_DEVTYPE_CD_AUDIO ;

  mciSendCommand(NULL,MCI_OPEN,MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID,

  (DWORD)&mciOpen);

  [3]:指定形貌字符串: 挨开一个AVI文件

  MCI_OPEN_PARMS mciOpen;

  mciOpen.lpstrDeviceType="avivideo";

  mciOpen.lpstrElementName="aaa.avi";

  mciSendCommand(NULL,MCI_OPEN,MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,

  (DWORD)&mciOpen);

  寄看三种挨开格式中,函数第三个参数的辩黑,后里会讲到那类辩黑。

  MCI指令

  MCI操做以下指令:

  MCI_BREAK

  设置间断键,缺省是”CTRL+BREAK"

  MCI_CAPTURE

  抓与当前帧并存进指定文件,仅用于数琢坑频

  MCI_CLOSE

  启闭设备

  MCI_CONFIGURE

  弹出竖坐对话框,仅用于数琢坑频

  MCI_COPY

  拷贝数据至剪掀板

  MCI_CUE

  延时播放或录音

  MCI_CUT

  删除数据

  MCI_DELETE

  删除数据

  MCI_ESCAPE

  仅用于激光视频

  MCI_FREEZE

  将隐现定格

  MCI_GETDEVCAPS

  得到设备疑息

  MCI_INDEX

  当前屏幕隐现与可,仅用于VCR设备

  MCI_INFO

  得到字符串疑息

  MCI_LIST

  得到输进装笨帻目,撑沉魁琢坑频战VCR设备

  MCI_LOAD

  拆进一个文件

  MCI_MARK

  消弭或做一个暗号,与MCI_SEEK配套

  MCI_MARK

  消弭或做一个暗号,与MCI_SEEK配套

  MCI_MONITOR

  为数琢坑频指定述讲设备

  MCI_OPEN

  挨开设备

  MCI_PASTE

  粘帖数据

  MCI_PAUSE

  暂伏掀前动做

  MCI_PLAY

  播放

  MCI_PUT

  设置源、方针战边框矩形

  MCI_QUALITY

  界讲设备缺省量量

  MCI_RECORD

  匹里劈脸录制

  MCI_RESERVE

  分拨硬疟间

  MCI_RESTORE

  拷贝一个bmp文件至帧灰″

  MCI_RESUME

  使一个停息设备重新启动

  MCI_SAVE

  留村据

  MCI_SEEK

  变动媒体位置

  MCI_SET

  设置设备疑息

  MCI_SETAUDIO

  设置音量

  MCI_SETTIMECODE

  启映鲵消弭VCR设备的时分码

  MCI_SETTUNER

  设置VCR设备频讲

  MCI_SETVIDEO

  设置video参数

  MCI_SIGNAL

  正在工做区上设置指定空间

  MCI_STATUS

  得到设备疑息

  MCI_STEP

  使播啡影备跳帧

  MCI_STOP

  停止播放

  MCI_SYSINFO

  返回MCI设备疑息

  MCI_UNDO

  消弭操纵

  MCI_UNFREEZE

  使操做MCI_UNFREEZE的视频灰″邙规复举动

  MCI_UPDATE

  更兄牺现地区

  MCI_WHERE

  得到设备减少矩形

  MCI_WINDOW

  指定图形设备窗心战窗心特性

  个中比较常常操做的指令有MCI_OPEN、MCI_CLOSE、MCI_PLAY、MCI_STOP、MCI_PAUSE、MCI_STATUS等涤耄

  真例阐收

  正在Visual C++ 5.0步播上面与new,单击projects,拔与MFC AppWizard(exe),竖坐一个新的名为"mcitest"的工程文件,OK冶。

  寄看正在操做法式范例当选择"Dialog based",然后Finish完成。那是一个基于对话框的操做法式,为林ш成MCI测试的任务,我们要变动一下对话框本钱。面与"Resource View",正在"Dialog"下拔与"IDD_MCITEST_DIALOG"对话框,按序增减Button如图所示。

  完成对话框的编削。左键单击mcitest files、拔与Add Files To Project,减进配套光盘止终给的"commci.cpp"战"commci.h"文件。挨开ClassWizard,正在Class Name下选择CMcitestDlg,减进统统的按键消息措置函数。

  正在"cmcitestDlg"类中,告别用"COMMCI"界讲Wav、Midi、Avi三个成员变量,正在按钮吸应进程中告别写上吸应措置函数open( )、play( )、close( )、pause( )、stop( )。

  正在"projoct"步播下单击setting,弹出设置对话框,正在"link"下"object/library modules"下减进"winmm.lib",编译并匝弄法式:

  图5.1

  源法式介绍

  // commci.h: inte***ce for the commci class.

  //

  ////////////////////////////////////////////////// ////////////////////

  #if !defined(AFX_COMMCI_H__90CEFD04_CC96_11D1_94F8_000 0B431BBA1__INCLUDED_)

  #define AFX_COMMCI_H__90CEFD04_CC96_11D1_94F8_0000B431BBA1 __INCLUDED_

  #if _MSC_VER >= 1000

  #pragma once

  #endif // _MSC_VER >= 1000

  //#include <windows.h>

  #include <mmsystem.h>

  class COMMCI

  {

  private:

  HWND hOwer; //窗心的具有者

  MCI_OPEN_PARMS mciOpen;

  public:

  COMMCI();

  ~COMMCI() {Close(); }

  MCIERROR Open(LPCSTR DeviceType,LPCSTR filename);

  //经过进程形貌字符串挨开设备

  MCIERROR Open(int DeviceType,LPCSTR filename); //经过进程设备范例挨开设备

  MCIERROR Open(LPCSTR filename); //自动检测设备

  void Play(HWND hWnd); //播放MCI,hWnd为回调窗心句柄

  void Close(void); //启闭设备

  void Stop(void); //停止设备

  void Pause(void); //停息设备

  DWORD Query(); //检测设备

  };

  ////////////////////////////////////////////////// ///////

  // COMMCI.CPP 中操做到的挨算

  ////////////////////////////////////////////////// ///////

  //typedef struct tagMCI_OPEN_PARMS {

  // DWORD dwCallback;

  // MCIDEVICEID wDeviceID;

  // WORD wReserved0;

  // LPCSTR lpstrDeviceType;

  // LPCSTR lpstrElementName;

  // LPCSTR lpstrAlias;

  //} MCI_OPEN_PARMS, FAR *LPMCI_OPEN_PARMS;

  //typedef struct tagMCI_PLAY_PARMS {

  // DWORD dwCallback;

  // DWORD dwFrom;

  // DWORD dwTo;

  //} MCI_PLAY_PARMS, *PMCI_PLAY_PARMS, FAR *LPMCI_PLAY_PARMS;

  //typedef struct tagMCI_STATUS_PARMS {

  // DWORD dwCallback;

  // DWORD dwReturn;

  // DWORD dwItem;

  // DWORD dwTrack;

  //} MCI_STATUS_PARMS, *PMCI_STATUS_PARMS,

  FAR * LPMCI_STATUS_PARMS;

  ////////////////////////////////////////////////// ////////

  // mci 初初化格式

  ////////////////////////////////////////////////// ////////

  //COMMCI.Open("waveaudio",filename); wave ; *.wav ,

  //COMMCI.Open("sequencer",filename); midi ; *.mid , *.rmi

  //COMMCI.Open("reelmagic",filename); vcd ; *.mpg ,

  //COMMCI.Open("avivideo",filename); avi ; *.avi ,

  ////////////////////////////////////////////////// ///////

  // mci 自遇返回值

  ////////////////////////////////////////////////// ///////

  // case MCI_MODE_NOT_READY:

  // case MCI_MODE_STOP:

  // case MCI_MODE_PLAY:

  // case MCI_MODE_RECORD:

  // case MCI_MODE_SEEK:

  // case MCI_MODE_PAUSE:

  // case MCI_MODE_OPEN:

  #endif // !defined(AFX_COMMCI_H__90CEFD04_CC96_11D1_94F8_000 0B431BBA1__INCLUDED_)

  // commci.cpp: implementation of the commci class.

  //

  ////////////////////////////////////////////////// ////////////////////

  #include "stdafx.h"

  //#include "mcitest.h"

  #include "commci.h"

  #ifdef _DEBUG

  #undef THIS_FILE

  static char THIS_FILE[]=__FILE__;

  #define new DEBUG_NEW

  #endif

  ////////////////////////////////////////////////// ////////////////////

  // Construction/Destruction

  ////////////////////////////////////////////////// ////////////////////

  COMMCI::COMMCI()

  {

  memset(this,0,sizeof(COMMCI));

  }

  MCIERROR COMMCI::Open(LPCSTR DeviceType,LPCSTR filename)

  {

  //假定有挨开的设备便启闭

  if (mciOpen.wDeviceID) Close();

  //初初化MCI_OPEN_PARMS挨算

  mciOpen.lpstrDeviceType=DeviceType;

  mciOpen.lpstrElementName=filename;

  //除挨开设备设备代码为0,上里的任何mciSendCommand语句皆要指定设

  //备代码。

  if ( mciSendCommand(NULL,MCI_OPEN,

  MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,

  (DWORD)&mciOpen))

  return FALSE;

  return TRUE;

  }

  MCIERROR COMMCI::Open(LPCSTR filename)

  {

  if (mciOpen.wDeviceID) Close();

  mciOpen.lpstrElementName=filename;

  if ( mciSendCommand(NULL,MCI_OPEN,

   MCI_OPEN_ELEMENT,

  (DWORD)&mciOpen))

  return FALSE;

  return TRUE;

  }

  MCIERROR COMMCI::Open(int DeviceType,LPCSTR filename)

  {

  if (mciOpen.wDeviceID) Close();

  mciOpen.lpstrDeviceType=(LPCSTR)DeviceType;

  mciOpen.lpstrElementName=filename;

  return mciSendCommand(NULL,MCI_OPEN,

  MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID ,(DWORD)&mciOpen);

  }

  void COMMCI::Play(HWND hWnd)

  {

  MCI_PLAY_PARMS mciPlay;

  hOwer=hWnd; //回调窗心句柄

  //MCI_PLAY_PARMS挨算只需供设定回调窗心句柄

  mciPlay.dwCallback=(WORD)hOwer;

  mciSendCommand(mciOpen.wDeviceID,MCI_PLAY,MCI_NOTI FY,

  (DWORD)&mciPlay);

  }

  void COMMCI::Close(void)

  {

  if (mciOpen.wDeviceID)

  mciSendCommand(mciOpen.wDeviceID,MCI_CLOSE,NULL,NU LL);

  memset(this,0,sizeof(COMMCI));

  }

  void COMMCI::Stop(void)

  {

  if (mciOpen.wDeviceID)

  mciSendCommand(mciOpen.wDeviceID,MCI_STOP,NULL,NUL L);

  }

  void COMMCI::Pause(void)

  {

  if (mciOpen.wDeviceID)

  mciSendCommand(mciOpen.wDeviceID,MCI_PAUSE,NULL,NU LL);

  }

  DWORD COMMCI::Query()

  {

  MCI_STATUS_PARMS mciStatus;

  mciStatus.dwItem=MCI_STATUS_MODE;

  mciSendCommand(mciOpen.wDeviceID,MCI_STATUS,

  MCI_STATUS_ITEM,(LPARAM)&mciStatus);

  return mciStatus.dwReturn;

OpenAL

OpenAL(Open Audio Library)是自由软件界的跨平台音效API。它设计给多通道三维位置音效的特效表现。其 API 风格模仿自 OpenGL。[1]

编辑本段历史

OpenAL 最初是由 Loki Software 所开发。是为了将 Windows 商业游戏移植到 Linux 上。Loki 倒闭以后,这个专案由自由软件/开放源始码社群继续维护。不过现在最大的主导者(并大量发展)是创新科技,并得到来自 Apple 和自由软件/开放源代码爱好者的持续支援。

编辑本段结构功能

OpenAL 主要的功能是在来源物体、音效缓冲和收听者中编码。来源物体包含一个指向缓冲区的指标、声音的速度、位置和方向,以及声音强度。收听者物体包含收听者的速度、位置和方向,以及全部声音的整体增益。缓冲里包含 8 或 16 位元、单声道或立体声 PCM 格式的音效资料,表现引擎进行所有必要的计算,如距离衰减、多普勒效应等。
不同于 OpenGL 规格,OpenAL 规格包含两个API分支;以实际 OpenAL 函式组成的核心,和 ALC API,ALC 用于管理表现内容、资源使用情况,并将跨平台风格封在其中。还有“ALUT”程式库,提供高阶“易用”的函式,其定位相当于 OpenGL 的 GLUT。

编辑本段可携性

这个 API 可用于以下平台︰
* Mac OS X
* GNU/Linux(OSS 和 ALSA 的后端)
* BSD
* Solaris
* IRIX
* Windows PC
* PlayStation 2
* PlayStation 3
* Xbox
* Xbox 360
* Nintendo GameCube
* Wii
* MorphOS

编辑本段应用程式

* Blender - 3D 建模和渲染工具。
* Unity - 3D 游戏引擎和 IDE。
* ..Basic4gl - 编译器和编程软件
更详尽的列表可在 OpenAL 网站查询。

windows SDK模拟游戏钢琴的实现

最近想做一个模拟钢琴的小软件,想做个既有键盘接口又有鼠标接口的小软件。所涉及到的知识点如下:
  1.有关键盘的知识
  2.有关鼠标的知识
  3.GDI的知识
  4.媒体播放函数的有关知识


  因为这几天也正好在看这些内容,所以理论结合实际是最好的学习方式
 
首先要找钢琴音阶,但是上网找了一圈也没有发现,最后在4399里面找到一个差不多类型的模拟钢琴游戏,而且音色不错,索性用Adobe Audition的录音功能将每个音阶都录下来做一些编辑,自己制作了20来个音阶,用mp3格式保存。
然后用的是mcisendstring函数实现mp3格式音乐的播放。


第一步首先做键盘接口,其实这一步还是比较简单的,在窗口过程里面处理对应的虚拟键码的消息就行了,这里遇到了一个小问题,找不到 <,  和  >.  这两个按键对应的虚拟键码了,找了一下虚拟键码表上面好像也没有写啊?先暂时搁下
成果做出来以后,发现一个比较严重的问题,就是使用mcisendstring这个宏的时候,不能同时打开某个音乐文件,这样比如我的la这个音阶有2秒的话,必须要等到2秒结束以后才能再次播放,这样显然影响了连续的效果,目前也没有什么好的解决方案。


第二步就要涉及到GDI和鼠标接口了,这一部分正在看,等到成品做出来以后在附上源码吧。


昨天晚上做梦的时候突然想到怎么实现可以几乎同时按一个键发出声音了,话说梦真是个神奇的东西啊。
既然不能同时打开同一个音乐文件,那么我在每次打开前关闭这个音乐文件不就实现这个效果了吗?想到了确实觉得挺简单的,但是昨天怎么就没想到呢?呵呵,下面先附上我键盘接口的代码:


  1. /*------------------------------------------------------------ 
  2.    a piano program 
  3.   ------------------------------------------------------------*/  
  4.   
  5. #include <windows.h>  
  6. #include <mmsystem.h>  
  7.   
  8. #pragma comment(lib, "WINMM.LIB")  
  9.   
  10. LRESULT CALLBACK WndProc (HWNDUINTWPARAMLPARAM) ;  
  11.   
  12. int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,  
  13.                     PSTR szCmdLine, int iCmdShow)  
  14. {  
  15.      static TCHAR szAppName[] = TEXT ("HelloWin") ;  
  16.      HWND         hwnd ;  
  17.      MSG          msg ;  
  18.      WNDCLASS     wndclass ;  
  19.        
  20.      wndclass.style         = CS_HREDRAW | CS_VREDRAW ;  
  21.      wndclass.lpfnWndProc   = WndProc ;  
  22.      wndclass.cbClsExtra    = 0 ;  
  23.      wndclass.cbWndExtra    = 0 ;  
  24.      wndclass.hInstance     = hInstance ;  
  25.      wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;  
  26.      wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;  
  27.      wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;  
  28.      wndclass.lpszMenuName  = NULL ;  
  29.      wndclass.lpszClassName = szAppName ;  
  30.   
  31.      if (!RegisterClass (&wndclass))  
  32.      {  
  33.           MessageBox (NULL, TEXT ("This program requires Windows NT!"),  
  34.                       szAppName, MB_ICONERROR) ;  
  35.           return 0 ;  
  36.      }  
  37.        
  38.      hwnd = CreateWindow (szAppName,                  // window class name  
  39.                           TEXT ("The Hello Program"), // window caption  
  40.                           WS_OVERLAPPEDWINDOW,        // window style  
  41.                           CW_USEDEFAULT,              // initial x position  
  42.                           CW_USEDEFAULT,              // initial y position  
  43.                           CW_USEDEFAULT,              // initial x size  
  44.                           CW_USEDEFAULT,              // initial y size  
  45.                           NULL,                       // parent window handle  
  46.                           NULL,                       // window menu handle  
  47.                           hInstance,                  // program instance handle  
  48.                           NULL) ;                     // creation parameters  
  49.        
  50.      ShowWindow (hwnd, iCmdShow) ;  
  51.      UpdateWindow (hwnd) ;  
  52.        
  53.      while (GetMessage (&msg, NULL, 0, 0))  
  54.      {  
  55.           TranslateMessage (&msg) ;  
  56.           DispatchMessage (&msg) ;  
  57.      }  
  58.      return msg.wParam ;  
  59. }  
  60.   
  61. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
  62. {  
  63.       
  64.      HDC         hdc;  
  65.      PAINTSTRUCT ps ;  
  66.      RECT        rect;  
  67.       
  68.      switch (message)  
  69.      {  
  70.      case WM_CREATE:  
  71.             
  72.           return 0 ;  
  73.       
  74.      case WM_PAINT:  
  75.           
  76.           hdc = BeginPaint (hwnd, &ps);    
  77.           GetClientRect (hwnd, &rect);  
  78.           DrawText (hdc, TEXT ("an intersting piano game^_^  just use keyboard" ), -1, &rect,  
  79.                     DT_SINGLELINE | DT_CENTER | DT_VCENTER);  
  80.             
  81.           EndPaint (hwnd, &ps) ;  
  82.           return 0 ;  
  83.      case WM_KEYDOWN:  
  84.          switch(wParam)  
  85.          {  
  86.          case 0x31:  
  87.              mciSendString(TEXT("close c://1.mp3"), NULL, 0, NULL);  
  88.              mciSendString(TEXT("play c://1.mp3"), NULL, 0, NULL);       //0-9键盘接口  
  89.              break;  
  90.          case 0x32:  
  91.              mciSendString(TEXT("close c://2.mp3"), NULL, 0, NULL);  
  92.              mciSendString(TEXT("play c://2.mp3"), NULL, 0, NULL);  
  93.              break;  
  94.          case 0x33:  
  95.              mciSendString(TEXT("close c://3.mp3"), NULL, 0, NULL);  
  96.             mciSendString(TEXT("play c://3.mp3"), NULL, 0, NULL);  
  97.              break;  
  98.          case 0x34:  
  99.              mciSendString(TEXT("close c://4.mp3"), NULL, 0, NULL);  
  100.              mciSendString(TEXT("play c://4.mp3"), NULL, 0, NULL);  
  101.              break;  
  102.          case 0x35:  
  103.              mciSendString(TEXT("close c://5.mp3"), NULL, 0, NULL);  
  104.              mciSendString(TEXT("play c://5.mp3"), NULL, 0, NULL);  
  105.              break;  
  106.          case 0x36:  
  107.              mciSendString(TEXT("close c://6.mp3"), NULL, 0, NULL);  
  108.              mciSendString(TEXT("play c://6.mp3"), NULL, 0, NULL);  
  109.              break;  
  110.          case 0x37:  
  111.              mciSendString(TEXT("close c://7.mp3"), NULL, 0, NULL);  
  112.              mciSendString(TEXT("play c://7.mp3"), NULL, 0, NULL);  
  113.              break;  
  114.          case 0x38:  
  115.              mciSendString(TEXT("close c://8.mp3"), NULL, 0, NULL);  
  116.              mciSendString(TEXT("play c://8.mp3"), NULL, 0, NULL);  
  117.              break;  
  118.          case 0x39:  
  119.              mciSendString(TEXT("close c://9.mp3"), NULL, 0, NULL);  
  120.              mciSendString(TEXT("play c://9.mp3"), NULL, 0, NULL);  
  121.              break;  
  122.          case 0x30:  
  123.              mciSendString(TEXT("close c://10.mp3"), NULL, 0, NULL);  
  124.              mciSendString(TEXT("play c://10.mp3"), NULL, 0, NULL);  
  125.              break;  
  126.   
  127.          case 65:                                                                                                //字母键盘接口  
  128.              mciSendString(TEXT("close c://1.mp3"), NULL, 0, NULL);  
  129.              mciSendString(TEXT("play c://1.mp3"), NULL, 0, NULL);  
  130.              break;  
  131.          case 90:  
  132.              mciSendString(TEXT("close c://2.mp3"), NULL, 0, NULL);  
  133.              mciSendString(TEXT("play c://2.mp3"), NULL, 0, NULL);  
  134.              break;  
  135.          case 83:  
  136.              mciSendString(TEXT("close c://3.mp3"), NULL, 0, NULL);  
  137.              mciSendString(TEXT("play c://3.mp3"), NULL, 0, NULL);  
  138.              break;  
  139.          case 88:  
  140.              mciSendString(TEXT("close c://4.mp3"), NULL, 0, NULL);  
  141.              mciSendString(TEXT("play c://4.mp3"), NULL, 0, NULL);  
  142.              break;  
  143.          case 68:  
  144.              mciSendString(TEXT("close c://5.mp3"), NULL, 0, NULL);  
  145.              mciSendString(TEXT("play c://5.mp3"), NULL, 0, NULL);  
  146.              break;  
  147.          case 67:  
  148.              mciSendString(TEXT("close c://6.mp3"), NULL, 0, NULL);  
  149.              mciSendString(TEXT("play c://6.mp3"), NULL, 0, NULL);  
  150.              break;  
  151.          case 70:  
  152.              mciSendString(TEXT("close c://7.mp3"), NULL, 0, NULL);  
  153.              mciSendString(TEXT("play c://7.mp3"), NULL, 0, NULL);  
  154.              break;  
  155.          case 86:  
  156.              mciSendString(TEXT("close c://8.mp3"), NULL, 0, NULL);  
  157.              mciSendString(TEXT("play c://8.mp3"), NULL, 0, NULL);  
  158.              break;  
  159.          case 71:  
  160.              mciSendString(TEXT("close c://9.mp3"), NULL, 0, NULL);  
  161.              mciSendString(TEXT("play c://9.mp3"), NULL, 0, NULL);  
  162.              break;  
  163.          case 66:  
  164.              mciSendString(TEXT("close c://10.mp3"), NULL, 0, NULL);  
  165.              mciSendString(TEXT("play c://10.mp3"), NULL, 0, NULL);  
  166.              break;  
  167.          case 72:  
  168.              mciSendString(TEXT("close c://11.mp3"), NULL, 0, NULL);  
  169.              mciSendString(TEXT("play c://11.mp3"), NULL, 0, NULL);  
  170.              break;  
  171.          case 78:  
  172.              mciSendString(TEXT("close c://12.mp3"), NULL, 0, NULL);  
  173.              mciSendString(TEXT("play c://12.mp3"), NULL, 0, NULL);  
  174.              break;  
  175.          case 74:  
  176.              mciSendString(TEXT("close c://13.mp3"), NULL, 0, NULL);  
  177.              mciSendString(TEXT("play c://13.mp3"), NULL, 0, NULL);  
  178.              break;  
  179.          case 77:  
  180.              mciSendString(TEXT("close c://14.mp3"), NULL, 0, NULL);  
  181.              mciSendString(TEXT("play c://14.mp3"), NULL, 0, NULL);  
  182.              break;  
  183.          case 75:  
  184.              mciSendString(TEXT("close c://15.mp3"), NULL, 0, NULL);  
  185.              mciSendString(TEXT("play c://15.mp3"), NULL, 0, NULL);  
  186.              break;  
  187.          case VK_OEM_8:  
  188.              mciSendString(TEXT("close c://16.mp3"), NULL, 0, NULL);  
  189.              mciSendString(TEXT("play c://16.mp3"), NULL, 0, NULL);  
  190.              break;  
  191.          case 76:  
  192.              mciSendString(TEXT("close c://17.mp3"), NULL, 0, NULL);  
  193.              mciSendString(TEXT("play c://17.mp3"), NULL, 0, NULL);  
  194.              break;  
  195.          }  
  196.          return 0;  
  197.      case WM_DESTROY:  
  198.           PostQuitMessage (0) ;  
  199.           return 0 ;  
  200.      }  
  201.      return DefWindowProc (hwnd, message, wParam, lParam) ;  
  202. }  

中间有很多东西都是重复的,只是简单地完成了一个音乐播放的功能,而且也没有加上资源,加上GDI等东西。
这几天偶尔在书上看到了类似的一个程序,集合了菜单,资源等很多功能,是用MIDI来控制音乐播放的,所以有很多选项可以选择,下面附上代码:
  1. /*--------------------------------------- 
  2.    KBMIDI.C -- Keyboard MIDI Player  
  3.                (c) Charles Petzold, 1998 
  4.   ---------------------------------------*/  
  5.   
  6. #include <windows.h>  
  7.   
  8. // Defines for Menu IDs  
  9. // --------------------  
  10.   
  11. #define IDM_OPEN    0x100  
  12. #define IDM_CLOSE   0x101  
  13. #define IDM_DEVICE  0x200  
  14. #define IDM_CHANNEL 0x300  
  15. #define IDM_VOICE   0x400  
  16.   
  17. LRESULT CALLBACK WndProc (HWNDUINTWPARAMLPARAM);  
  18.   
  19. TCHAR    szAppName [] = TEXT ("KBMidi") ;  
  20. HMIDIOUT hMidiOut ;  
  21. int      iDevice = MIDIMAPPER, iChannel = 0, iVoice = 0, iVelocity = 64 ;  
  22. int      cxCaps, cyChar, xOffset, yOffset ;  
  23.   
  24.      // Structures and data for showing families and instruments on menu  
  25.      // ----------------------------------------------------------------  
  26.   
  27. typedef struct  
  28. {  
  29.      TCHAR * szInst ;  
  30.      int    iVoice ;  
  31. }  
  32. INSTRUMENT ;  
  33.   
  34. typedef struct  
  35. {  
  36.      TCHAR      * szFam ;  
  37.      INSTRUMENT   inst [8] ;  
  38. }  
  39. FAMILY ;  
  40.   
  41. FAMILY fam [16] = {   
  42.        
  43.      TEXT ("Piano"),  
  44.   
  45.           TEXT ("Acoustic Grand Piano"),        0,  
  46.           TEXT ("Bright Acoustic Piano"),       1,  
  47.           TEXT ("Electric Grand Piano"),        2,  
  48.           TEXT ("Honky-tonk Piano"),            3,  
  49.           TEXT ("Rhodes Piano"),                4,  
  50.           TEXT ("Chorused Piano"),              5,  
  51.           TEXT ("Harpsichord"),                 6,  
  52.           TEXT ("Clavinet"),                    7,  
  53.   
  54.      TEXT ("Chromatic Percussion"),  
  55.   
  56.           TEXT ("Celesta"),                     8,  
  57.           TEXT ("Glockenspiel"),                9,  
  58.           TEXT ("Music Box"),                   10,  
  59.           TEXT ("Vibraphone"),                  11,  
  60.           TEXT ("Marimba"),                     12,  
  61.           TEXT ("Xylophone"),                   13,  
  62.           TEXT ("Tubular Bells"),               14,  
  63.           TEXT ("Dulcimer"),                    15,  
  64.   
  65.      TEXT ("Organ"),  
  66.   
  67.           TEXT ("Hammond Organ"),               16,  
  68.           TEXT ("Percussive Organ"),            17,  
  69.           TEXT ("Rock Organ"),                  18,  
  70.           TEXT ("Church Organ"),                19,  
  71.           TEXT ("Reed Organ"),                  20,  
  72.           TEXT ("Accordian"),                   21,  
  73.           TEXT ("Harmonica"),                   22,  
  74.           TEXT ("Tango Accordian"),             23,  
  75.   
  76.      TEXT ("Guitar"),  
  77.   
  78.           TEXT ("Acoustic Guitar (nylon)"),     24,  
  79.           TEXT ("Acoustic Guitar (steel)"),     25,  
  80.           TEXT ("Electric Guitar (jazz)"),      26,  
  81.           TEXT ("Electric Guitar (clean)"),     27,  
  82.           TEXT ("Electric Guitar (muted)"),     28,  
  83.           TEXT ("Overdriven Guitar"),           29,  
  84.           TEXT ("Distortion Guitar"),           30,  
  85.           TEXT ("Guitar Harmonics"),            31,  
  86.   
  87.      TEXT ("Bass"),  
  88.   
  89.           TEXT ("Acoustic Bass"),               32,  
  90.           TEXT ("Electric Bass (finger)"),      33,  
  91.           TEXT ("Electric Bass (pick)"),        34,  
  92.           TEXT ("Fretless Bass"),               35,  
  93.           TEXT ("Slap Bass 1"),                 36,  
  94.           TEXT ("Slap Bass 2"),                 37,  
  95.           TEXT ("Synth Bass 1"),                38,  
  96.           TEXT ("Synth Bass 2"),                39,  
  97.   
  98.      TEXT ("Strings"),  
  99.   
  100.           TEXT ("Violin"),                      40,  
  101.           TEXT ("Viola"),                       41,  
  102.           TEXT ("Cello"),                       42,  
  103.           TEXT ("Contrabass"),                  43,  
  104.           TEXT ("Tremolo Strings"),             44,  
  105.           TEXT ("Pizzicato Strings"),           45,  
  106.           TEXT ("Orchestral Harp"),             46,  
  107.           TEXT ("Timpani"),                     47,  
  108.   
  109.      TEXT ("Ensemble"),  
  110.   
  111.           TEXT ("String Ensemble 1"),           48,  
  112.           TEXT ("String Ensemble 2"),           49,  
  113.           TEXT ("Synth Strings 1"),             50,  
  114.           TEXT ("Synth Strings 2"),             51,  
  115.           TEXT ("Choir Aahs"),                  52,  
  116.           TEXT ("Voice Oohs"),                  53,  
  117.           TEXT ("Synth Voice"),                 54,  
  118.           TEXT ("Orchestra Hit"),               55,  
  119.   
  120.      TEXT ("Brass"),  
  121.   
  122.           TEXT ("Trumpet"),                     56,  
  123.           TEXT ("Trombone"),                    57,  
  124.           TEXT ("Tuba"),                        58,  
  125.           TEXT ("Muted Trumpet"),               59,  
  126.           TEXT ("French Horn"),                 60,  
  127.           TEXT ("Brass Section"),               61,  
  128.           TEXT ("Synth Brass 1"),               62,  
  129.           TEXT ("Synth Brass 2"),               63,  
  130.   
  131.      TEXT ("Reed"),  
  132.   
  133.           TEXT ("Soprano Sax"),                 64,  
  134.           TEXT ("Alto Sax"),                    65,  
  135.           TEXT ("Tenor Sax"),                   66,  
  136.           TEXT ("Baritone Sax"),                67,  
  137.           TEXT ("Oboe"),                        68,  
  138.           TEXT ("English Horn"),                69,  
  139.           TEXT ("Bassoon"),                     70,  
  140.           TEXT ("Clarinet"),                    71,  
  141.   
  142.      TEXT ("Pipe"),  
  143.   
  144.           TEXT ("Piccolo"),                     72,  
  145.           TEXT ("Flute"),                       73,  
  146.           TEXT ("Recorder"),                    74,  
  147.           TEXT ("Pan Flute"),                   75,  
  148.           TEXT ("Bottle Blow"),                 76,  
  149.           TEXT ("Shakuhachi"),                  77,  
  150.           TEXT ("Whistle"),                     78,  
  151.           TEXT ("Ocarina"),                     79,  
  152.   
  153.      TEXT ("Synth Lead"),  
  154.   
  155.           TEXT ("Lead 1 (square)"),             80,  
  156.           TEXT ("Lead 2 (sawtooth)"),           81,  
  157.           TEXT ("Lead 3 (caliope lead)"),       82,  
  158.           TEXT ("Lead 4 (chiff lead)"),         83,  
  159.           TEXT ("Lead 5 (charang)"),            84,  
  160.           TEXT ("Lead 6 (voice)"),              85,  
  161.           TEXT ("Lead 7 (fifths)"),             86,  
  162.           TEXT ("Lead 8 (brass + lead)"),       87,  
  163.   
  164.      TEXT ("Synth Pad"),  
  165.   
  166.           TEXT ("Pad 1 (new age)"),             88,  
  167.           TEXT ("Pad 2 (warm)"),                89,  
  168.           TEXT ("Pad 3 (polysynth)"),           90,  
  169.           TEXT ("Pad 4 (choir)"),               91,  
  170.           TEXT ("Pad 5 (bowed)"),               92,  
  171.           TEXT ("Pad 6 (metallic)"),            93,  
  172.           TEXT ("Pad 7 (halo)"),                94,  
  173.           TEXT ("Pad 8 (sweep)"),               95,  
  174.   
  175.      TEXT ("Synth Effects"),  
  176.   
  177.           TEXT ("FX 1 (rain)"),                 96,  
  178.           TEXT ("FX 2 (soundtrack)"),           97,  
  179.           TEXT ("FX 3 (crystal)"),              98,  
  180.           TEXT ("FX 4 (atmosphere)"),           99,  
  181.           TEXT ("FX 5 (brightness)"),           100,  
  182.           TEXT ("FX 6 (goblins)"),              101,  
  183.           TEXT ("FX 7 (echoes)"),               102,  
  184.           TEXT ("FX 8 (sci-fi)"),               103,  
  185.   
  186.      TEXT ("Ethnic"),  
  187.   
  188.           TEXT ("Sitar"),                       104,  
  189.           TEXT ("Banjo"),                       105,  
  190.           TEXT ("Shamisen"),                    106,  
  191.           TEXT ("Koto"),                        107,  
  192.           TEXT ("Kalimba"),                     108,  
  193.           TEXT ("Bagpipe"),                     109,  
  194.           TEXT ("Fiddle"),                      110,  
  195.           TEXT ("Shanai"),                      111,  
  196.   
  197.      TEXT ("Percussive"),  
  198.   
  199.           TEXT ("Tinkle Bell"),                 112,  
  200.           TEXT ("Agogo"),                       113,  
  201.           TEXT ("Steel Drums"),                 114,  
  202.           TEXT ("Woodblock"),                   115,  
  203.           TEXT ("Taiko Drum"),                  116,  
  204.           TEXT ("Melodic Tom"),                 117,  
  205.           TEXT ("Synth Drum"),                  118,  
  206.           TEXT ("Reverse Cymbal"),              119,  
  207.   
  208.      TEXT ("Sound Effects"),  
  209.   
  210.           TEXT ("Guitar Fret Noise"),           120,  
  211.           TEXT ("Breath Noise"),                121,  
  212.           TEXT ("Seashore"),                    122,  
  213.           TEXT ("Bird Tweet"),                  123,  
  214.           TEXT ("Telephone Ring"),              124,  
  215.           TEXT ("Helicopter"),                  125,  
  216.           TEXT ("Applause"),                    126,  
  217.           TEXT ("Gunshot"),                     127 } ;  
  218.   
  219.      // Data for translating scan codes to octaves and notes  
  220.      // ----------------------------------------------------  
  221.   
  222. #define NUMSCANS    (sizeof key / sizeof key[0])  
  223.   
  224. struct  
  225. {  
  226.      int     iOctave ;  
  227.      int     iNote ;  
  228.      int     yPos ;  
  229.      int     xPos ;  
  230.      TCHAR * szKey ;  
  231. }  
  232. key [] =  
  233. {  
  234.                                          // Scan  Char  Oct  Note  
  235.                                          // ----  ----  ---  ----  
  236.          -1, -1, -1, -1, NULL,           //   0   None  
  237.          -1, -1, -1, -1, NULL,           //   1   Esc  
  238.          -1, -1,  0,  0, TEXT (""),      //   2     1  
  239.           5,  1,  0,  2, TEXT ("C#"),    //   3     2    5    C#  
  240.           5,  3,  0,  4, TEXT ("D#"),    //   4     3    5    D#  
  241.          -1, -1,  0,  6, TEXT (""),      //   5     4  
  242.           5,  6,  0,  8, TEXT ("F#"),    //   6     5    5    F#  
  243.           5,  8,  0, 10, TEXT ("G#"),    //   7     6    5    G#  
  244.           5, 10,  0, 12, TEXT ("A#"),    //   8     7    5    A#  
  245.          -1, -1,  0, 14, TEXT (""),      //   9     8  
  246.           6,  1,  0, 16, TEXT ("C#"),    //  10     9    6    C#  
  247.           6,  3,  0, 18, TEXT ("D#"),    //  11     0    6    D#  
  248.          -1, -1,  0, 20, TEXT (""),      //  12     -  
  249.           6,  6,  0, 22, TEXT ("F#"),    //  13     =    6    F#  
  250.          -1, -1, -1, -1, NULL,           //  14    Back  
  251.             
  252.          -1, -1, -1, -1, NULL,           //  15    Tab  
  253.           5,  0,  1,  1, TEXT ("C"),     //  16     q    5    C  
  254.           5,  2,  1,  3, TEXT ("D"),     //  17     w    5    D  
  255.           5,  4,  1,  5, TEXT ("E"),     //  18     e    5    E  
  256.           5,  5,  1,  7, TEXT ("F"),     //  19     r    5    F  
  257.           5,  7,  1,  9, TEXT ("G"),     //  20     t    5    G  
  258.           5,  9,  1, 11, TEXT ("A"),     //  21     y    5    A  
  259.           5, 11,  1, 13, TEXT ("B"),     //  22     u    5    B  
  260.           6,  0,  1, 15, TEXT ("C"),     //  23     i    6    C  
  261.           6,  2,  1, 17, TEXT ("D"),     //  24     o    6    D  
  262.           6,  4,  1, 19, TEXT ("E"),     //  25     p    6    E  
  263.           6,  5,  1, 21, TEXT ("F"),     //  26     [    6    F  
  264.           6,  7,  1, 23, TEXT ("G"),     //  27     ]    6    G  
  265.          -1, -1, -1, -1, NULL,           //  28    Ent  
  266.             
  267.          -1, -1, -1, -1, NULL,           //  29    Ctrl  
  268.           3,  8,  2,  2, TEXT ("G#"),    //  30     a    3    G#  
  269.           3, 10,  2,  4, TEXT ("A#"),    //  31     s    3    A#  
  270.          -1, -1,  2,  6, TEXT (""),      //  32     d  
  271.           4,  1,  2,  8, TEXT ("C#"),    //  33     f    4    C#  
  272.           4,  3,  2, 10, TEXT ("D#"),    //  34     g    4    D#  
  273.          -1, -1,  2, 12, TEXT (""),      //  35     h  
  274.           4,  6,  2, 14, TEXT ("F#"),    //  36     j    4    F#  
  275.           4,  8,  2, 16, TEXT ("G#"),    //  37     k    4    G#  
  276.           4, 10,  2, 18, TEXT ("A#"),    //  38     l    4    A#  
  277.          -1, -1,  2, 20, TEXT (""),      //  39     ;  
  278.           5,  1,  2, 22, TEXT ("C#"),    //  40     '    5    C#  
  279.          -1, -1, -1, -1, NULL,           //  41     `  
  280.             
  281.          -1, -1, -1, -1, NULL,           //  42    Shift  
  282.          -1, -1, -1, -1, NULL,           //  43     \  (not line continuation)  
  283.           3,  9,  3,  3, TEXT ("A"),     //  44     z    3    A  
  284.           3, 11,  3,  5, TEXT ("B"),     //  45     x    3    B  
  285.           4,  0,  3,  7, TEXT ("C"),     //  46     c    4    C  
  286.           4,  2,  3,  9, TEXT ("D"),     //  47     v    4    D  
  287.           4,  4,  3, 11, TEXT ("E"),     //  48     b    4    E  
  288.           4,  5,  3, 13, TEXT ("F"),     //  49     n    4    F  
  289.           4,  7,  3, 15, TEXT ("G"),     //  50     m    4    G  
  290.           4,  9,  3, 17, TEXT ("A"),     //  51     ,    4    A  
  291.           4, 11,  3, 19, TEXT ("B"),     //  52     .    4    B  
  292.           5,  0,  3, 21, TEXT ("C")      //  53     /    5    C  
  293. } ;  
  294.   
  295. int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,  
  296.                     PSTR szCmdLine, int iCmdShow)  
  297. {  
  298.      MSG      msg;  
  299.      HWND     hwnd ;  
  300.      WNDCLASS wndclass ;  
  301.        
  302.      wndclass.style         = CS_HREDRAW | CS_VREDRAW ;  
  303.      wndclass.lpfnWndProc   = WndProc ;  
  304.      wndclass.cbClsExtra    = 0 ;  
  305.      wndclass.cbWndExtra    = 0 ;  
  306.      wndclass.hInstance     = hInstance ;  
  307.      wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;  
  308.      wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;  
  309.      wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;  
  310.      wndclass.lpszMenuName  = NULL ;  
  311.      wndclass.lpszClassName = szAppName ;  
  312.        
  313.      if (!RegisterClass (&wndclass))  
  314.      {  
  315.           MessageBox (NULL, TEXT ("This program requires Windows NT!"),  
  316.                       szAppName, MB_ICONERROR) ;  
  317.           return 0 ;  
  318.      }  
  319.        
  320.      hwnd = CreateWindow (szAppName, TEXT ("Keyboard MIDI Player"),  
  321.                           WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,  
  322.                           CW_USEDEFAULT, CW_USEDEFAULT,  
  323.                           CW_USEDEFAULT, CW_USEDEFAULT,  
  324.                           NULL, NULL, hInstance, NULL) ;  
  325.   
  326.      if (!hwnd)  
  327.           return 0 ;  
  328.        
  329.      ShowWindow (hwnd, iCmdShow) ;  
  330.      UpdateWindow (hwnd);   
  331.        
  332.      while (GetMessage (&msg, NULL, 0, 0))  
  333.      {  
  334.           TranslateMessage (&msg) ;  
  335.           DispatchMessage (&msg) ;  
  336.      }  
  337.      return msg.wParam ;  
  338. }  
  339.   
  340. // Create the program's menu (called from WndProc, WM_CREATE)  
  341. // ----------------------------------------------------------  
  342.   
  343. HMENU CreateTheMenu (int iNumDevs)  
  344. {  
  345.      TCHAR       szBuffer [32] ;  
  346.      HMENU       hMenu, hMenuPopup, hMenuSubPopup ;  
  347.      int         i, iFam, iIns ;  
  348.      MIDIOUTCAPS moc ;  
  349.        
  350.      hMenu = CreateMenu () ;  
  351.        
  352.           // Create "On/Off" popup menu  
  353.        
  354.      hMenuPopup = CreateMenu () ;  
  355.        
  356.      AppendMenu (hMenuPopup, MF_STRING, IDM_OPEN, TEXT ("&Open")) ;   
  357.      AppendMenu (hMenuPopup, MF_STRING | MF_CHECKED, IDM_CLOSE,   
  358.                              TEXT ("&Closed")) ;  
  359.        
  360.      AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup,   
  361.                         TEXT ("&Status")) ;  
  362.        
  363.           // Create "Device" popup menu  
  364.        
  365.      hMenuPopup = CreateMenu () ;  
  366.        
  367.           // Put MIDI Mapper on menu if it's installed  
  368.        
  369.      if (!midiOutGetDevCaps (MIDIMAPPER, &moc, sizeof (moc)))  
  370.           AppendMenu (hMenuPopup, MF_STRING, IDM_DEVICE + (int) MIDIMAPPER,  
  371.                          moc.szPname) ;  
  372.      else  
  373.           iDevice = 0 ;  
  374.        
  375.           // Add the rest of the MIDI devices  
  376.        
  377.      for (i = 0 ; i < iNumDevs ; i++)  
  378.      {  
  379.           midiOutGetDevCaps (i, &moc, sizeof (moc)) ;  
  380.           AppendMenu (hMenuPopup, MF_STRING, IDM_DEVICE + i, moc.szPname) ;  
  381.      }  
  382.        
  383.      CheckMenuItem (hMenuPopup, 0, MF_BYPOSITION | MF_CHECKED) ;  
  384.      AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup,   
  385.                         TEXT ("&Device")) ;  
  386.        
  387.           // Create "Channel" popup menu  
  388.        
  389.      hMenuPopup = CreateMenu () ;  
  390.        
  391.      for (i = 0 ; i < 16 ; i++)  
  392.      {  
  393.           wsprintf (szBuffer, TEXT ("%d"), i + 1) ;  
  394.           AppendMenu (hMenuPopup, MF_STRING | (i ? MF_UNCHECKED : MF_CHECKED),  
  395.                                   IDM_CHANNEL + i, szBuffer) ;  
  396.      }  
  397.        
  398.      AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup,   
  399.                         TEXT ("&Channel")) ;  
  400.        
  401.           // Create "Voice" popup menu  
  402.        
  403.      hMenuPopup = CreateMenu () ;  
  404.        
  405.      for (iFam = 0 ; iFam < 16 ; iFam++)  
  406.      {  
  407.           hMenuSubPopup = CreateMenu () ;  
  408.             
  409.           for (iIns = 0 ; iIns < 8 ; iIns++)  
  410.           {  
  411.                wsprintf (szBuffer, TEXT ("&%d.\t%s"), iIns + 1,  
  412.                                    fam[iFam].inst[iIns].szInst) ;  
  413.                AppendMenu (hMenuSubPopup,  
  414.                            MF_STRING | (fam[iFam].inst[iIns].iVoice ?  
  415.                                              MF_UNCHECKED : MF_CHECKED),  
  416.                            fam[iFam].inst[iIns].iVoice + IDM_VOICE,  
  417.                            szBuffer) ;  
  418.           }  
  419.             
  420.           wsprintf (szBuffer, TEXT ("&%c.\t%s"), 'A' + iFam,  
  421.                               fam[iFam].szFam) ;  
  422.           AppendMenu (hMenuPopup, MF_STRING | MF_POPUP, (UINT) hMenuSubPopup,  
  423.                                   szBuffer) ;  
  424.      }  
  425.      AppendMenu (hMenu, MF_STRING | MF_POPUP, (UINT) hMenuPopup,   
  426.                         TEXT ("&Voice")) ;  
  427.      return hMenu ;  
  428. }  
  429.   
  430. // Routines for simplifying MIDI output  
  431. // ------------------------------------  
  432.   
  433. DWORD MidiOutMessage (HMIDIOUT hMidi, int iStatus, int iChannel,  
  434.                       int iData1,  int iData2)  
  435. {  
  436.      DWORD dwMessage ;  
  437.        
  438.      dwMessage = iStatus | iChannel | (iData1 << 8) | (iData2 << 16) ;  
  439.        
  440.      return midiOutShortMsg (hMidi, dwMessage) ;  
  441. }  
  442.   
  443. DWORD MidiNoteOff (HMIDIOUT hMidi, int iChannel, int iOct, int iNote, int iVel)  
  444. {  
  445.      return MidiOutMessage (hMidi, 0x080, iChannel, 12 * iOct + iNote, iVel) ;  
  446. }  
  447.   
  448. DWORD MidiNoteOn (HMIDIOUT hMidi, int iChannel, int iOct, int iNote, int iVel)  
  449. {  
  450.      return MidiOutMessage (hMidi, 0x090, iChannel, 12 * iOct + iNote, iVel) ;  
  451. }  
  452.   
  453. DWORD MidiSetPatch (HMIDIOUT hMidi, int iChannel, int iVoice)  
  454. {  
  455.      return MidiOutMessage (hMidi, 0x0C0, iChannel, iVoice, 0) ;  
  456. }  
  457.   
  458. DWORD MidiPitchBend (HMIDIOUT hMidi, int iChannel, int iBend)  
  459. {  
  460.      return MidiOutMessage (hMidi, 0x0E0, iChannel, iBend & 0x7F, iBend >> 7) ;  
  461. }  
  462.   
  463. // Draw a single key on window  
  464. // ---------------------------  
  465.   
  466. VOID DrawKey (HDC hdc, int iScanCode, BOOL fInvert)  
  467. {  
  468.      RECT rc ;  
  469.        
  470.      rc.left   = 3 * cxCaps * key[iScanCode].xPos / 2 + xOffset ;  
  471.      rc.top    = 3 * cyChar * key[iScanCode].yPos / 2 + yOffset ;  
  472.      rc.right  = rc.left + 3 * cxCaps ;  
  473.      rc.bottom = rc.top  + 3 * cyChar / 2 ;  
  474.        
  475.      SetTextColor (hdc, fInvert ? 0x00FFFFFFul : 0x00000000ul) ;  
  476.      SetBkColor   (hdc, fInvert ? 0x00000000ul : 0x00FFFFFFul) ;  
  477.        
  478.      FillRect (hdc, &rc, GetStockObject (fInvert ? BLACK_BRUSH : WHITE_BRUSH)) ;  
  479.        
  480.      DrawText (hdc, key[iScanCode].szKey, -1, &rc,  
  481.                     DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;  
  482.        
  483.      FrameRect (hdc, &rc, GetStockObject (BLACK_BRUSH)) ;  
  484. }  
  485.   
  486. // Process a Key Up or Key Down message  
  487. // ------------------------------------  
  488.   
  489. VOID ProcessKey (HDC hdc, UINT message, LPARAM lParam)  
  490. {  
  491.      int iScanCode, iOctave, iNote ;  
  492.        
  493.      iScanCode = 0x0FF & HIWORD (lParam) ;  
  494.        
  495.      if (iScanCode >= NUMSCANS)                       // No scan codes over 53  
  496.           return ;  
  497.        
  498.      if ((iOctave = key[iScanCode].iOctave) == -1)    // Non-music key  
  499.           return ;  
  500.        
  501.      if (GetKeyState (VK_SHIFT) < 0)  
  502.           iOctave += 0x20000000 & lParam ? 2 : 1 ;  
  503.        
  504.      if (GetKeyState (VK_CONTROL) < 0)  
  505.           iOctave -= 0x20000000 & lParam ? 2 : 1 ;  
  506.        
  507.      iNote = key[iScanCode].iNote ;  
  508.        
  509.      if (message == WM_KEYUP)                           // For key up  
  510.      {  
  511.           MidiNoteOff (hMidiOut, iChannel, iOctave, iNote, 0) ;   // Note off  
  512.           DrawKey (hdc, iScanCode, FALSE) ;  
  513.           return ;  
  514.      }  
  515.        
  516.      if (0x40000000 & lParam)                          // ignore typematics  
  517.           return ;  
  518.        
  519.      MidiNoteOn (hMidiOut, iChannel, iOctave, iNote, iVelocity) ; // Note on  
  520.      DrawKey (hdc, iScanCode, TRUE) ;                 // Draw the inverted key  
  521. }  
  522.   
  523. // Window Procedure  
  524. // ----------------  
  525.   
  526. LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
  527. {  
  528.      static BOOL bOpened = FALSE ;  
  529.      HDC         hdc ;  
  530.      HMENU       hMenu ;  
  531.      int         i, iNumDevs, iPitchBend, cxClient, cyClient ;  
  532.      MIDIOUTCAPS moc ;  
  533.      PAINTSTRUCT ps ;  
  534.      SIZE        size ;  
  535.      TCHAR       szBuffer [16] ;  
  536.        
  537.      switch (message)  
  538.      {  
  539.      case WM_CREATE:  
  540.                // Get size of capital letters in system font  
  541.             
  542.           hdc = GetDC (hwnd) ;  
  543.             
  544.           GetTextExtentPoint (hdc, TEXT ("M"), 1, &size) ;  
  545.           cxCaps = size.cx ;  
  546.           cyChar = size.cy ;  
  547.             
  548.           ReleaseDC (hwnd, hdc) ;  
  549.             
  550.                // Initialize "Volume" scroll bar  
  551.             
  552.           SetScrollRange (hwnd, SB_HORZ, 1, 127, FALSE) ;  
  553.           SetScrollPos   (hwnd, SB_HORZ, iVelocity, TRUE) ;  
  554.             
  555.                // Initialize "Pitch Bend" scroll bar  
  556.             
  557.           SetScrollRange (hwnd, SB_VERT, 0, 16383, FALSE) ;  
  558.           SetScrollPos   (hwnd, SB_VERT, 8192, TRUE) ;  
  559.             
  560.                // Get number of MIDI output devices and set up menu  
  561.             
  562.           if (0 == (iNumDevs = midiOutGetNumDevs ()))  
  563.           {  
  564.                MessageBeep (MB_ICONSTOP) ;  
  565.                MessageBox (hwnd, TEXT ("No MIDI output devices!"),  
  566.                                  szAppName, MB_OK | MB_ICONSTOP) ;  
  567.                return -1 ;  
  568.           }  
  569.           SetMenu (hwnd, CreateTheMenu (iNumDevs)) ;  
  570.           return 0 ;  
  571.             
  572.      case WM_SIZE:  
  573.           cxClient = LOWORD (lParam) ;  
  574.           cyClient = HIWORD (lParam) ;  
  575.             
  576.           xOffset = (cxClient - 25 * 3 * cxCaps / 2) / 2 ;  
  577.           yOffset = (cyClient - 11 * cyChar) / 2 + 5 * cyChar ;  
  578.           return 0 ;  
  579.             
  580.      case WM_COMMAND:  
  581.           hMenu = GetMenu (hwnd) ;  
  582.             
  583.               // "Open" menu command  
  584.             
  585.           if (LOWORD (wParam) == IDM_OPEN && !bOpened)  
  586.           {  
  587.                if (midiOutOpen (&hMidiOut, iDevice, 0, 0, 0))  
  588.                {  
  589.                     MessageBeep (MB_ICONEXCLAMATION) ;  
  590.                     MessageBox (hwnd, TEXT ("Cannot open MIDI device"),  
  591.                                 szAppName, MB_OK | MB_ICONEXCLAMATION) ;  
  592.                }  
  593.                else  
  594.                {  
  595.                     CheckMenuItem (hMenu, IDM_OPEN,  MF_CHECKED) ;  
  596.                     CheckMenuItem (hMenu, IDM_CLOSE, MF_UNCHECKED) ;  
  597.                       
  598.                     MidiSetPatch (hMidiOut, iChannel, iVoice) ;  
  599.                     bOpened = TRUE ;  
  600.                }  
  601.           }  
  602.             
  603.                // "Close" menu command  
  604.             
  605.           else if (LOWORD (wParam) == IDM_CLOSE && bOpened)  
  606.           {  
  607.                CheckMenuItem (hMenu, IDM_OPEN,  MF_UNCHECKED) ;  
  608.                CheckMenuItem (hMenu, IDM_CLOSE, MF_CHECKED) ;  
  609.                  
  610.                     // Turn all keys off and close device  
  611.                  
  612.                for (i = 0 ; i < 16 ; i++)  
  613.                     MidiOutMessage (hMidiOut, 0xB0, i, 123, 0) ;  
  614.                  
  615.                midiOutClose (hMidiOut) ;  
  616.                bOpened = FALSE ;  
  617.           }  
  618.             
  619.                // Change MIDI "Device" menu command  
  620.             
  621.           else if (LOWORD (wParam) >= IDM_DEVICE - 1 &&   
  622.                    LOWORD (wParam) <  IDM_CHANNEL)  
  623.           {  
  624.                CheckMenuItem (hMenu, IDM_DEVICE + iDevice, MF_UNCHECKED) ;  
  625.                iDevice = LOWORD (wParam) - IDM_DEVICE ;  
  626.                CheckMenuItem (hMenu, IDM_DEVICE + iDevice, MF_CHECKED) ;  
  627.                  
  628.                     // Close and reopen MIDI device  
  629.                  
  630.                if (bOpened)  
  631.                {  
  632.                     SendMessage (hwnd, WM_COMMAND, IDM_CLOSE, 0L) ;  
  633.                     SendMessage (hwnd, WM_COMMAND, IDM_OPEN,  0L) ;  
  634.                }  
  635.           }  
  636.             
  637.                // Change MIDI "Channel" menu command  
  638.             
  639.           else if (LOWORD (wParam) >= IDM_CHANNEL &&   
  640.                    LOWORD (wParam) <  IDM_VOICE)  
  641.           {  
  642.                CheckMenuItem (hMenu, IDM_CHANNEL + iChannel, MF_UNCHECKED);  
  643.                iChannel = LOWORD (wParam) - IDM_CHANNEL ;  
  644.                CheckMenuItem (hMenu, IDM_CHANNEL + iChannel, MF_CHECKED) ;  
  645.                  
  646.                if (bOpened)  
  647.                     MidiSetPatch (hMidiOut, iChannel, iVoice) ;  
  648.           }  
  649.             
  650.                // Change MIDI "Voice" menu command  
  651.             
  652.           else if (LOWORD (wParam) >= IDM_VOICE)  
  653.           {  
  654.                CheckMenuItem (hMenu, IDM_VOICE + iVoice, MF_UNCHECKED) ;  
  655.                iVoice = LOWORD (wParam) - IDM_VOICE ;  
  656.                CheckMenuItem (hMenu, IDM_VOICE + iVoice, MF_CHECKED) ;  
  657.                  
  658.                if (bOpened)  
  659.                     MidiSetPatch (hMidiOut, iChannel, iVoice) ;  
  660.           }  
  661.             
  662.           InvalidateRect (hwnd, NULL, TRUE) ;  
  663.           return 0 ;  
  664.             
  665.           // Process a Key Up or Key Down message  
  666.             
  667.      case WM_KEYUP:  
  668.      case WM_KEYDOWN:  
  669.           hdc = GetDC (hwnd) ;  
  670.             
  671.           if (bOpened)  
  672.                ProcessKey (hdc, message, lParam) ;  
  673.             
  674.           ReleaseDC (hwnd, hdc) ;  
  675.           return 0 ;  
  676.             
  677.           // For Escape, turn off all notes and repaint  
  678.             
  679.      case WM_CHAR:  
  680.           if (bOpened && wParam == 27)  
  681.           {  
  682.                for (i = 0 ; i < 16 ; i++)  
  683.                     MidiOutMessage (hMidiOut, 0xB0, i, 123, 0) ;  
  684.                  
  685.                InvalidateRect (hwnd, NULL, TRUE) ;  
  686.           }  
  687.           return 0 ;  
  688.             
  689.           // Horizontal scroll: Velocity  
  690.             
  691.      case WM_HSCROLL:  
  692.           switch (LOWORD (wParam))  
  693.           {  
  694.           case SB_LINEUP:         iVelocity -= 1 ;  break ;  
  695.           case SB_LINEDOWN:       iVelocity += 1 ;  break ;  
  696.           case SB_PAGEUP:         iVelocity -= 8 ;  break ;  
  697.           case SB_PAGEDOWN:       iVelocity += 8 ;  break ;  
  698.           case SB_THUMBPOSITION:  iVelocity = HIWORD (wParam) ;  break ;  
  699.           default:                return 0 ;  
  700.           }  
  701.           iVelocity = max (1, min (iVelocity, 127)) ;  
  702.           SetScrollPos (hwnd, SB_HORZ, iVelocity, TRUE) ;  
  703.           return 0 ;  
  704.             
  705.           // Vertical scroll:  Pitch Bend  
  706.        
  707.      case WM_VSCROLL:  
  708.           switch (LOWORD (wParam))  
  709.           {  
  710.           case SB_THUMBTRACK:    iPitchBend = 16383 - HIWORD (wParam) ;  break ;  
  711.           case SB_THUMBPOSITION: iPitchBend = 8191 ;                     break ;  
  712.           default:               return 0 ;  
  713.           }  
  714.           iPitchBend = max (0, min (iPitchBend, 16383)) ;  
  715.           SetScrollPos (hwnd, SB_VERT, 16383 - iPitchBend, TRUE) ;  
  716.             
  717.           if (bOpened)  
  718.                MidiPitchBend (hMidiOut, iChannel, iPitchBend) ;  
  719.           return 0 ;  
  720.        
  721.      case WM_PAINT:  
  722.           hdc = BeginPaint (hwnd, &ps) ;  
  723.             
  724.           for (i = 0 ; i < NUMSCANS ; i++)  
  725.                if (key[i].xPos != -1)  
  726.                     DrawKey (hdc, i, FALSE) ;  
  727.                  
  728.           midiOutGetDevCaps (iDevice, &moc, sizeof (MIDIOUTCAPS)) ;  
  729.           wsprintf (szBuffer, TEXT ("Channel %i"), iChannel + 1) ;  
  730.        
  731.           TextOut (hdc, cxCaps, 1 * cyChar,   
  732.                         bOpened ? TEXT ("Open") : TEXT ("Closed"),  
  733.                         bOpened ? 4 : 6) ;  
  734.           TextOut (hdc, cxCaps, 2 * cyChar, moc.szPname,  
  735.                         lstrlen (moc.szPname)) ;  
  736.           TextOut (hdc, cxCaps, 3 * cyChar, szBuffer, lstrlen (szBuffer)) ;  
  737.           TextOut (hdc, cxCaps, 4 * cyChar,  
  738.                         fam[iVoice / 8].inst[iVoice % 8].szInst,  
  739.                lstrlen (fam[iVoice / 8].inst[iVoice % 8].szInst)) ;  
  740.        
  741.           EndPaint (hwnd, &ps) ;  
  742.           return 0 ;  
  743.                  
  744.      case WM_DESTROY :  
  745.           SendMessage (hwnd, WM_COMMAND, IDM_CLOSE, 0L) ;  
  746.           PostQuitMessage (0) ;  
  747.           return 0 ;  
  748.      }  
  749.      return DefWindowProc (hwnd, message, wParam, lParam) ;  
  750. }  

程序虽然有点长,自己现在也没有学得很好,所以还不能完全看懂。希望以后能写一下关于这个程序的解释