22.1 Windows 和多媒体

来源:互联网 发布:淘宝ifashion手机进入 编辑:程序博客网 时间:2024/05/23 14:37
摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P979

        从某种意义上来说多媒体就是通过调用与设备无关的的函数来访问各种硬件。让我们首先来看一下硬件的部分,然后再讲解 Windows 多媒体 API 的结构。

22.1.1  多媒体硬件

        最常用的多媒体硬件大概就是波形音频设备了,通常被称为声卡。波形音频设备将麦克风输入或其他模拟音频输入转换为数字化的样本,然后把它们存储在内存中或储存为以 .WAV 扩展名结尾的硬盘文件。波形音频设备也可以将波形转换会模拟声音,再通过 PC 的扬声器来播放。

        声卡通常还包含一个 MIDI 设备。MIDI 是行业标准的乐器数字接口(Musical Instrument Digital Interface)。这种硬件能根据简短的二进制信息来演奏音符。MIDI 硬件通常还可以通过电缆连接到一个 MIDI 输入设备,如音乐键盘。此外,外部 MIDI 合成器往往也可以和声卡相连。

        大多数现在的计算机所连接的 CD-ROM 驱动器,通常都能够直接播放普通的音乐 CD。这就是所谓的“CD 音频”。波形音频设备MIDI 设备 CD 音频设备的输出,往往混合在一起,用户通过 Windows 的“音量控制”设定可以控制它们的音量

        另一些常见的多媒体“设备”不需要任何额外的硬件。Windows 的标准视频设备(也称为 AVI 视频设备)可以播放以 .AVI(audio-video interleave,音视频交叠)为扩展名的电影或动画文件。ActiveMovie 控件可以播放包括 QuickTime 和 MPEG 在内的一些其他类型的电影。PC 的显卡上还可能带有专门的硬件来协助播放这些电影。

        比较少见的是 PC 用户有时还会拥有先锋镭射影碟播放机索尼系列 VISCA 录像机这些设备具有串行接口,计算机软件可以通过这些接口来控制该设备译注:现在更常见的是通过 USB 接口或 HDMI 接口连接的多媒体设备)。某些显卡有一种被称为“画中画”的功能,允许外部视频信息和其他一些应用程序一起显示在 Windows 屏幕上。这些也被认为是多媒体设备。

22.1.2  API 概述

        Windows 中对多媒体功能的 API 支持主要分为两个集合,分别被称为“底层”接口和“高层”接口。

        底层接口包括一系列函数,这些函数一简单的描述性前缀开头,在 MSDN 文档的 /Platform SDK/Graphics and Multimedia Services/Multimedia Referenve/Multimedia Functions 中列出(译注://MSDN Library/WIN32 and COM Development/Graphics and Multimedia/Audio and Video/Windows Multimedia/Multimedia Reference/Multimedia Functions)(高层函数也在那里)。

        底层波形音频输入函数和输出函数分别以 waveIn 和 waveOut 前缀开头。我们将在本章中介绍这些函数。另外在本章中,我们还将试验使用 midiOut 函数控制 MIDI 输出设备。这里用到的 API 还包括 midiIn 和 midiStream 函数。

        在本章中还将用到一些以 time 前缀开头的函数,它们用来设置高分辨率的抢占式(preemptive)计时器,计时器的间隔可低至 1 毫秒。该功能主要被用于播放 MIDI 序列。其他还有几组涉及音频压缩、视频压缩、动画和视频序列的函数;不过本章将不讨论它们。

        多媒体函数列表中有 7 个函数的前缀为 mci,它们提供了对媒体控制接口(Media Control Interface,MCI)的访问。这是一个高层的可扩展接口,用于控制多媒体 PC 中的所有多媒体硬件。MCI 包括许多对所有多媒体硬件都适用的命令。因为多媒体的许多方面都可以建模成一个类似录音机的播放录制模型,所以确实可以做到这些命令对所有多媒体都使用。运行“打开设备”命令后,该设备就可以用来输入或输出,然后可以运行“录制”命令(表示进行输入操作)或“播放”命令(表示进行输出操作),完成后可运行“关闭”命令来关闭设备。

        MCI 本身有两种形式。一种形式是向 MCI 发送消息(类似 Windows 消息)。这些消息包括位编码标记和 C 数据结构。第二种形式是向 MCI 发送文本字符串。该功能主要是为脚本语言服务的,这些脚本语言具有灵活的字符串操作函数,却没有太多的 Windows API 调用支持。基于字符串版本的 MCI 也很适合交互探索和学习 MCI,我们稍后就会尝试一下。MCI 中的设备名称包括 cdaudio、waveadui、sequencer(MIDI)、videodisc、vcr、overlay(窗口中的模拟视频)、dat(digital audio tape,数字录音带)和 digitalvideo。MCI 设备分为“简单”和“复合”两种类型。简单的设备(如 cdaudio)不使用文件,而复合设备(如 waveaudio)则会使用文件,比如使用以 .WAV 为扩展名的波形音频文件。

        此外还有一种访问多媒体硬件的办法,它涉及 DirectX API,不过这已经超出了本书的范围。

        另外还有两个高层多媒体函数也值得一提,它们是我们早在第 3 章中就介绍过的 MessageBeep 和 PlaySound 函数。MessageBeep 函数可以播放控制面板中声音程序指定的声音。PlaySound 函数可以播放磁盘上的 .WAV 文件,或者播放作为资源被加载到内存中的 .WAV 文件。我们将在本章稍后的部分再次用到 PlaySound 函数。

22.1.3  使用 TESTMCI 程序来学习 MCI

        早期的 Windows 多媒体软件开发工具包中包含一个名为 MCITEST 的 C 程序,允许程序员交互输入 MCI 命令,并了解它们如何工作。现在已经不再提供这个程序了(至少不再是 C 语言版本了)。所以,我重新创建了一个 TESTMCI 程序,如图 22-1 所示。它的用户界面完全基于从前的 MCITEST 程序。虽然我相信二者的代码可能差不多,但我这里并没有使用 MCITEST 的代码。

/*--------------------------------------------------------TESTMCI.C -- MCI Command String Tester(c) Charles Petzold, 1998--------------------------------------------------------*/#include <Windows.h>#include "resource.h"#define ID_TIMER1BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);TCHAR szAppName[] = TEXT("TestMci");int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){if (-1 == DialogBox(hInstance, szAppName, NULL, DlgProc)){MessageBox(NULL, TEXT("This program requires Windows NT!"),szAppName, MB_ICONERROR);}return 0;}BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static HWNDhwndEdit;intiCharBeg, iCharEnd, iLineBeg, iLineEnd, iChar, iLine, iLength;MCIERRORerror;RECTrect;TCHARszCommand[1024], szReturn[1024],szError[1024], szBuffer[32];switch (message){case WM_INITDIALOG:// Center the window on screenGetWindowRect(hwnd, &rect);SetWindowPos(hwnd, NULL,(GetSystemMetrics(SM_CXSCREEN) - rect.right + rect.left) / 2,(GetSystemMetrics(SM_CYSCREEN) - rect.bottom + rect.top) / 2,0, 0, SWP_NOZORDER | SWP_NOSIZE);hwndEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);SetFocus(hwndEdit);return FALSE;case WM_COMMAND:switch (LOWORD(wParam)){case IDOK:// Find the line numbers corresponding to the selectionSendMessage(hwndEdit, EM_GETSEL, (WPARAM)&iCharBeg, (LPARAM)&iCharEnd);iLineBeg = SendMessage(hwndEdit, EM_LINEFROMCHAR, iCharBeg, 0);iLineEnd = SendMessage(hwndEdit, EM_LINEFROMCHAR, iCharEnd, 0);// Loop through all  the linesfor (iLine = iLineBeg; iLine <= iLineEnd; iLine++){// Get the line and terminate it; ignore if blank*(WORD *)szCommand = sizeof(szCommand) / sizeof(TCHAR);iLength = SendMessage(hwndEdit, EM_GETLINE, iLine, (LPARAM)szCommand);szCommand[iLength] = '\0';if (iLength == 0)continue;// Send the MCI commanderror = mciSendString(szCommand, szReturn,sizeof(szReturn) / sizeof(TCHAR), hwnd);// Set the Return String fieldSetDlgItemText(hwnd, IDC_RETURN_STRING, szReturn);// Set the Error String field (even if no error)mciGetErrorString(error, szError,sizeof(szError) / sizeof(TCHAR));SetDlgItemText(hwnd, IDC_ERROR_STRING, szError);}// Send the caret to the end of the last selected lineiChar = SendMessage(hwndEdit, EM_LINEINDEX, iLineEnd, 0);iChar += SendMessage(hwndEdit, EM_LINELENGTH, iCharEnd, 0);SendMessage(hwndEdit, EM_SETSEL, iChar, iChar);// Insert a carriage return/line feed combinationSendMessage(hwndEdit, EM_REPLACESEL, FALSE,(LPARAM)TEXT("\r\n"));SetFocus(hwndEdit);return TRUE;case IDCANCEL:EndDialog(hwnd, 0);return TRUE;case IDC_MAIN_EDIT:if (HIWORD(wParam) == EN_ERRSPACE){MessageBox(hwnd, TEXT("Error control out of space."),szAppName, MB_OK | MB_ICONINFORMATION);return TRUE;}break;}break;case MM_MCINOTIFY:EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_MESSAGE), TRUE);wsprintf(szBuffer, TEXT("Device ID = %i"), lParam);SetDlgItemText(hwnd, IDC_NOTIFY_ID, szBuffer);EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ID), TRUE);EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUCCESSFUL),wParam & MCI_NOTIFY_SUCCESSFUL);EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUPERSEDED), wParam & MCI_NOTIFY_SUPERSEDED);EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ABORTED),wParam & MCI_NOTIFY_ABORTED);EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_FAILURE),wParam & MCI_NOTIFY_FAILURE);SetTimer(hwnd, ID_TIMER, 5000, NULL);return TRUE;case WM_TIMER:KillTimer(hwnd, ID_TIMER);EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_MESSAGE), FALSE);EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ID), FALSE);EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUCCESSFUL), FALSE);EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_SUPERSEDED), FALSE);EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_ABORTED), FALSE);EnableWindow(GetDlgItem(hwnd, IDC_NOTIFY_FAILURE), FALSE);return TRUE;case WM_SYSCOMMAND:switch (LOWORD(wParam)){case SC_CLOSE:EndDialog(hwnd, 0);return TRUE;}break;}return FALSE;}
TESTMCI.RC (excerpts)// Microsoft Visual C++ 生成的资源脚本。//#include "resource.h"///////////////////////////////////////////////////////////////////////////////// Dialog//TESTMCI DIALOG DISCARDABLE  0, 0, 270, 276STYLE WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENUCAPTION "MCI Tester"FONT 8, "MS Sans Serif"BEGIN    EDITTEXT        IDC_MAIN_EDIT, 8, 8, 254, 100, ES_MULTILINE | ES_AUTOHSCROLL |    WS_VSCROLL    LTEXT           "Return String:", IDC_STATIC, 8, 114, 60, 8    EDITTEXT        IDC_RETURN_STRING, 8, 126, 120, 50, ES_MULTILINE |    ES_AUTOVSCROLL | ES_READONLY | WS_GROUP | NOT WS_TABSTOP    LTEXT           "Error String:", IDC_STATIC, 142, 114, 60, 8    EDITTEXT        IDC_ERROR_STRING, 142, 126, 120, 50, ES_MULTILINE |    ES_AUTOVSCROLL | ES_READONLY | NOT WS_TABSTOP    GROUPBOX        "MM_MCINOTIFY Message", IDC_STATIC, 9, 186, 254, 58    LTEXT           "", IDC_NOTIFY_ID, 26, 198, 100, 8    LTEXT           "MCI_NOTIFY_SUCCESSFUL", IDC_NOTIFY_SUCCESSFUL, 26, 212, 100,    8, WS_DISABLED    LTEXT           "MCI_NOTIFY_SUPERSEDED", IDC_NOTIFY_SUPERSEDED, 26, 226, 100,    8, WS_DISABLED    LTEXT           "MCI_NOTIFY_ABORTED", IDC_NOTIFY_ABORTED, 144, 212, 100, 8,    WS_DISABLED    LTEXT           "MCI_NOTIFY_FAILURE", IDC_NOTIFY_FAILURE, 144, 226, 100, 8,    WS_DISABLED    DEFPUSHBUTTON   "OK", IDOK, 57, 255, 50, 14    PUSHBUTTON      "Close", IDCANCEL, 162, 255, 50, 14END
RESOURCE.H  (excerpts)// Microsoft Visual C++ generated include file.// Used by TestMci.rc#define IDC_MAIN_EDIT                   1000#define IDC_NOTIFY_MESSAGE              1005#define IDC_NOTIFY_ID                   1006#define IDC_NOTIFY_SUCCESSFUL           1007#define IDC_NOTIFY_SUPERSEDED           1008#define IDC_NOTIFY_ABORTED              1009#define IDC_NOTIFY_FAILURE              1010#define IDC_SIGNAL_MESSAGE              1011#define IDC_SIGNAL_ID                   1012#define IDC_SIGNAL_PARAM                1013#define IDC_RETURN_STRING               1014#define IDC_ERROR_STRING                1015#define IDC_DEVICES                     1016#define IDC_STATIC                      -1

        与本章中的许多程序类似,TESTMCI 使用非模态对话框作为它的主窗口。与本章中的所有程序类似,TESTMCI 需要在 Visual C++ 的 Project Settings 对话框的 Links 页面中加入 WINMM.LIB 导入库。

        此程序使用了两个最重要的多媒体函数:mciSendString 和 mciGetErrorString。在 TESTMCI 的主编辑窗口中输入字符串后,按下 Enter 键(或 OK 按钮),该程序会将输入的字符串作为 mciSendString 函数的第一个 参数:

error = mciSendString(szCommand, szReturn,sizeof(szReturn) / sizeof(TCHAR), hwnd);
如果编辑窗口中被选定的文本多于一行,该程序将把它们按顺序发送到 mciSendString 函数。第二个参数是一个字符串地址,该字符串含有函数返回的信息。程序将这些信息显示在窗口的 Return String 区域。mciSendString 返回的错误代码被传递给 mciGetErrorString 函数,从而得到错误的文本说明,这会被显示在 TESTMCI 窗口的 Error String 区域。

22.1.4  TESTMCI 和 CD 音频

        通过控制 CD-ROM 驱动器来播放音频 CD,可以很好地了解 MCI 命令字符串。这是一个不错的起点,因为这些命令字符串通常十分简单,而且你还可以顺便听听音乐。为了方便这个实验,手头上最好备有 MSDN 中对 MCI 命令字符串的引用文档,可以参见 /Platform SDK/Graphics and Multimedia Services/Multimedia Reference/Multimedia Command Strings(译注://MSDN Library/WIN32 and COM Development/Graphics and Multimedia/Audio and Video/Windows Multimedia/Multimedia Reference/Multimedia Command Strings)。

        请确保 CD-ROM 驱动器的音频输出连接到扬声器的音频输出连接到扬声器或耳机上,然后放入一张音频 CD(例如 Bruce SpringSteen 的专辑 Born to Run)。在 Windows 98 下,CD 播放器应用程序可能会自动启动,并开始播放该专辑。如果是这样的话,关闭 CD 播放器。然后运行 TESTMCI 程序,并输入如下命令:

open cdaudio
按下 Enter 键。单词 “open” 是一个 MCI 命令,而单词 “cdaudio” 是设备名,MCI 将其识别为 CD-ROM 驱动器。(这里假设你的系统中只有一个 CD-ROM 驱动器;要得到多个 CD-ROM 驱动器的名字,则需要使用 sysinfo 命令。)

        TESTMCI 的 Return String 区域显示了 mciSendString 函数中系统返回给你程序的字符串。如果 open 命令运行成功,这里就是一个简单的数字 1。TESTMCI 的 Error String 区域显示的是 MCIGetErrorString 根据 mciSendString 返回值计算出的结果。如果 mciSendString 没有返回错误代码,则 Error String 区域将显示文本“The specified command was carried out。”(指定的命令运行正常)。

        假设 open 命令运行正常,现在可以输入如下命令:

play cdaudio
CD 就会开始播放该专辑的第一首歌 Thunder Road。你可以输入如下命令暂停 CD 播放:

pause cdaudio
对 cdaudio 设备来说,也可以使用如下命令暂停 CD 播放,效果和前面的语句一样:

stop cdaudio
然后可以使用如下语句继续播放:

<pre name="code" class="cpp"><pre name="code" class="cpp">play cdaudio

到目前为止,我们用过的所有字符串都是由一个命令和一个设备名称组成的。有些命令还有一些选项。例如,输入如下命令:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">status cdaudio position

根据播放时间的长短,Return String 区域会显示类似如下的数字串:

<pre name="code" class="cpp">01:15:25

这是什么意思呢?这显然不是小时、分钟和秒数,因为一张 CD 没有那么长的时间,为了找出这个时间格式到底是什么,输入如下命令:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">status cdaudio time format

Return String 区域现在会显示如下字符串:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">msf

这表示“分-秒-帧” (minute-second-frame)。在 CD 音频中,每秒有 75 帧。时间格式中的“帧”的部分可以取值 0~74。

        status 命令有许多选项。可以使用如下命令得到 msf 格式的 CD 的整个长度:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">status cdaudio length

以专辑《Born to Run》来说,Return String 区域将显示如下数字:

<pre name="code" class="cpp">39:28:19

这表示 39 分 28 秒 19 帧。

        现在尝试如下命令:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">status cdaudio number of tracks

此时 Return String 区域会显示如下数字:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">8

        从该 CD 封面可以知道,专辑上的 Born to Run 歌曲在 CD 的第五条音轨上。MCI 命令中,音轨号码从 1 开始编号。所以我们可以输入如下命令来找出 Born to Run 这首歌有多长:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">status cdaudio length track 5

运行该命令后,Return String 区域会显示如下数字:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">04:30:22

我们也可以使用如下命令找出这条音轨从专辑的什么位置开始:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">status cdaudio position track 5

此时 Return String 区域会显示如下数字:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">17:36:35

有了这些信息,现在可以使用如下命令直接跳到专辑中该歌曲所在的音轨了:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">play cdaudio from 17:36:35 to 22:06:57

此命令将播放这一首歌曲,然后停止。最后面的这个值是 17:36:35 加上 4:30:22(音轨的长度)计算出来的。或者,也可以使用如下命令来确定此值:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">status cdaudio position track 6

或者你还可以用下面的命令把时间格式设置为“音轨-分-秒-帧”:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">set cdaudio time format tmsf

然后运行命令

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">play cdaudio from 5:0:0:0 to 6:0:0:0

还有一种更简单的方法,命令如下:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">play cdaudio from 5 to 6

当时间尾部的部分是 0 的时候,可以省略它们。另外还可以把时间格式设置为以毫秒为单位。

        每个 MCI 命令字符串都可以在串的末尾添加 wait 选项或者 notify 选项(或者两者都加上)。例如,假设指向播放歌曲 Born to Run 的最初 10 秒,此后希望该程序做些其他事情,那么这里提供了一种方式来实现这一点(假定时间格式已被设置为 tmsf),命令如下:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">play cdaudio from 5:0:0 to 5:0:10 wait

在这种情况下,mciSendString 函数会一直等到函数执行完毕才返回,也就是说,直到 Born to Run 的前 10 秒播放完成。

        很显然,对一般的单线程应用程序来说,这不是一件好事。如果你不小心输入了

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">play cdaudio wait

那么 mciSendString 函数将不会把控制权交还给程序,直到播放完整张专辑。如果必须使用 wait 选项(这在运行自动 MCI 脚本时十分方便,我很快会演示),那么请先使用 break 命令。此命令允许设置一个可以中断 mciSendString 命令的虚拟键代码,以将控制权返还给程序。例如,如果想设置 Esc 键作为中断键,可以使用如下命令

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">break cdaudio on 27

其中 27 是 VK_ESCAPE 的十进制值

        比 wait 选项更好的选择是 notify 选项:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">play cdaudio from 5:0:0 to 5:0:10 notify

在这种情况下,mciSendString 函数会立即返回,但是,当 MCI 命令中指定的操作结束后,mciSendString 的最后一个参数所指定的句柄代表的窗口将收到一条 MM_MCINOTIFY 消息。TESTMCI 程序将该消息的结果显示在 MM_MCINOTIFY 组合框内。为了避免和输入的其他命令混淆,5 秒后 TESTMCI 程序会停止显示 MM_MCINOTIFY 消息的结果。

        你可以同时使用 wait 和 notify 选项,但是我想不出这么做的理由。如果不使用这两个选项,默认的行为是既不等待也不通知,通常这是想要的行为。

        当结束摆弄这些命令后,可以通过输入如下命令来停止播放 CD:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">stop cdaudio

如果在关闭之前不停止 CD-ROM 设备的话,CD 会继续播放,哪怕已经关闭了该设备。

        还可以尝试一下以下命令,不过你的硬件不一定支持:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">eject cdaudio

最后使用如下命令关闭设备:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">close cdaudio

        虽然 TESTMCI 本身无法保存或加载文本文件,但是你可以在编辑控件和剪贴板之间复制文本。可以在 TESTMCI 中选择一些文本,复制到剪贴板(使用 Ctrl+C),再从剪贴板复制文本到记事本,最后保存。将这个过程反过来,就可以加载一系列 MCI 命令到 TESTMCI 中。如果选中了一系列命令并单击 OK 按钮或按下 Enter 键,TESTMCI 将逐条执行这些命令。这些就可以构建 MCI “脚本”,也就是 MCI 命令的列表。

        例如,假设你想要听这几首歌:Jungleland(在该专辑的最后一条音轨上),Thunder Road 和 Born to Run,并想以以上顺序播放这三首歌曲。你可以创建如下的脚本:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">open cdaudioset cdaudio time format tmsfbreak cdaudio on 27play cdaudio from 8 waitplay cdaudio from 1 to 2 waitplay cdaudio from 5 to 6 waitstop cdaudioeject cdaudioclose cdaudio

如果这里没有使用 wait 关键字,那么脚本将无法正常工作,因为 mciSendString 命令会立即返回,然后马上执行下一条命令。

        现在,应该相当清楚如何构建一个模拟 CD 播放器的简单应用程序了。该程序可以判断音轨的数量和每条音轨的长度,并允许用户从任意一点开始播放。(但是请记住,mciSendString 总是以文本字符串的形式返回信息,所以需要编写解析逻辑来将这些字符串转换为数字。)这样的一个程序几乎肯定还会用到 Windows 计时器,用来计量一秒(或其他长度)时间间隔。在 WM_TIMER 消息中,程序将会调用如下命令来判断 CD 是处于暂停状态还是播放状态:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">status cdaudio mode

而如下命令将使程序刷新其显示,以告知用户当前的播放位置:

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">status cdaudio position

但还有一些更有趣的东西也是可以实现的:如果程序知道音乐的高潮部分的时间点,它就可以在屏幕上显示与 CD 音乐同步的图形。无论是用于音乐教学,还是创建自己的图形化音乐视频,这都十分好用。

0 0
原创粉丝点击