0
点赞
收藏
分享

微信扫一扫

[C#] MCI 详解与封装类, MCI 播放音乐, 获取播放状态, 获取音频长度, 进度调整,


淦!

琢磨了一晚上啊, 总算有些眉目了. (所以我还是一个笨蛋啊, 明明这么简单的东西却花费了这么长时间

首先, MCI的全称是Multimedia Control Interface, 即多媒体控制接口, 通过它, 我们可以做到播放音频视频, 甚至录制音频, 虽然古老, 但是真的强大.

注意, 文章较官方文档有不少删减, 如果看标准内容, 还是看官方文档比较好.

MCI Command Strings 官方文档: Microsoft Command Strings - Win32 app | Microsoft Docs 哦对了, 文档是纯英文哦~

理论基础:

  1. MCI 不能与 C# 中的内存流打交道, 他只能播放文件.
  2. MCI 支持很多格式, 包含: CD音频, 数字音频, MIDI音乐, 视频唱片(videodisc), VCR, 以及波形音频.
  3. MCI 中, 被播放的音频文件被称作 设备(Device)
  4. MCI 中, 支持巨多设置项, 包括播放进度, 音量大小, 声道开关, 如果你播放的是 MIDI, 支持的设置更多.

正式开始:

下面, 我们将以播放音频为例, 尽可能多的讲述 MCI 的相关知识, 文章在今后可能会继续更新.

示例文件在: C:\Users\Null\Desktop\Tutorial.wav

> 引用库:
  • 最最开始, 无非是载入库.
  1. 对于 C++, 需要引用 Windows.h 以及 Mmsystem.h 这两个头文件
  2. 对于 C#, using System.Runtime.InteropServices; 以进行非托管库的调用.
> 执行指令:
  • MCI 中, 有 3 种方式来执行 MCI 指令. 分别是: mciSendString, mciExecute, mciSendCommand, 它们均位于 winmm.dll 中.
  • 函数原型:

MCIERROR mciSendString(    // MCIERROR 是 无符号长整形数字
    LPCTSTR lpszCommand,
    LPTSTR  lpszReturnString,
    UINT    cchReturn,
    HANDLE  hwndCallback
);
  
BOOL mciExecute(
    LPCSTR pszCommand
);

  MCIERROR mciSendCommand(
      MCIDEVICEID IDDevice,    // 设备 ID, 通过另一个函数打开文件可以获得
      UINT        uMsg,
      DWORD_PTR   fdwCommand,
      DWORD_PTR   dwParam
  );

  • C# 声明方式:

[DllImport("winmm.dll", EntryPoint = "mciSendString", CharSet = CharSet.Unicode)]
extern static ulong MciSendString(string command, string buffer, int bufferSize, IntPtr callback);

[DllImport("winmm.dll", EntryPoint = "mciExecute", CharSet = CharSet.Unicode)]
extern static bool MciExecute(string command);

[DllImport("winmm.dll", EntryPoint = "mciSendCommand", CharSet = CharSet.Unicode)]
extern static ulong MciSendCommand(uint deviceId, uint uMsg, IntPtr fdwCommand, IntPtr dwParam);

  • 简略说明:

mciSendString: 最常用的方法, 通过字符串来表示一个指令, 会返回错误码, 不会有弹窗警告
mciExecute: 调试时较常用的方法, 在执行时若有未完善的地方, 会弹窗警告, 也因为如此, 程序的发布版本不会使用这个(影响使用体验)
mciSendCommand: 很少用, 通过指令的ID来表示指令, 会返回错误码, 不会有弹窗警告
 
某些 MCI 指令具有返回值, 例如获取播放状态, 这些指令不能通过mciExecute执行.

  • 文章只会通过 mciSendString 来介绍 MCI 哟

> 打开文件:

  • 对于播放音频, 首要的第一件事, 肯定就是打开文件并将其载入到内存了. 不过有一点很重要, 就是 MCI 指令只支持短路径(ShortPath), 所以在拿到文件路径后, 我们得将其转换为短路径.
  • 如果不对路径进行转换, 那么某些名字长度大于8的文件(准确来说是路径中任何一部分长度大于8)的将无法播放

关于短路径与长路径: windows系统下的文件长名和文件短名 短路径与长路径的转换: [C#/C/C++] GetShortPathName 详解, 长路径转换为短路径

  • MCI 指令中, 通过 open 来打开一个文件, 并且在末尾还可以使用 “alias 别名” 来为这个已打开的文件起一个别名.
  • 下面是两个示例:

// 文件是 C:\Users\Null\Desktop\Tutorial.wav
// 转换为短路径是 C:\Users\Null\Desktop\Tut~1.wav
mciSendString(@"open C:\Users\Null\Desktop\Tut~1.wav", null, 0, IntPtr.Zero);
mciSendString(@"open C:\Users\Null\Desktop\Tut~1.wav alias tutorial", null, 0, IntPtr.Zero);

  • 在第一的 mciSendString 中, 很清晰明了, 打开了一个文件, 而第二个中, 我们还加了一个alias, 即, 别名, MCI支持为打开的文件起一个别名, 并且推荐这么做.
    第二个种, 我们为它起的别名是 tutorial.

> 播放音频:

  • 播放音频的 MCI 指令是 play, 直接 “play 设备”
  • 示例:

mciSendString(@"play C:\Users\Null\Desktop\Tut~1.wav", null, 0, IntPtr.Zero);
mciSendString(@"play tutorial", null, 0, IntPtr.Zero);

  • 如果你没有为文件指定别名, 那么在使用 play 指令时, 只能指定短路径
    如果你为文件指定了别名, 直接play加上别名即可播放这个文件.

> 重复播放音频:

  • 这里, 用到了参数, 就像alias一样, 可选. 重复播放还是使用的play指令.
  • 示例:

mciSendString(@"play C:\Users\Null\Desktop\Tut~1.wav repeat", null, 0, IntPtr.Zero);
mciSendString(@"play tutorial repeat", null, 0, IntPtr.Zero);

> 同步播放音频:

  • 同样是参数, 这里是wait, 支持wait参数的指令可以同步执行, 例如play指令
  • 示例:

mciSendString(@"play C:\Users\Null\Desktop\Tut~1.wav wait", null, 0, IntPtr.Zero);
mciSendString(@"play tutorial wait", null, 0, IntPtr.Zero);

> 暂停播放:

  • 暂停, pause, 用法很简单, 同样是 “pause 设备”
  • 示例:

mciSendString(@"pause C:\Users\Null\Desktop\Tut~1.wav", null, 0, IntPtr.Zero);
mciSendString(@"pause tutorial", null, 0, IntPtr.Zero);

> 恢复播放:

  • 恢复, resume, 语法是 “resume 设备”
  • 示例:

mciSendString(@"pause C:\Users\Null\Desktop\Tut~1.wav", null, 0, IntPtr.Zero);
mciSendString(@"pause tutorial", null, 0, IntPtr.Zero);

> 关闭文件:

  • 关闭, close, 语法是: “close 设备”
  • 示例:

mciSendString(@"close C:\Users\Null\Desktop\Tut~1.wav", null, 0, IntPtr.Zero);
mciSendString(@"close tutorial", null, 0, IntPtr.Zero);

> 改变播放位置:

  • seek 指令, 这个指令比较复杂哦. 语法如下:

seek device to position
seek device to start
seek device to end

  • 示例:

mciSendString(@"seek tutorial to 1000", null, 0, IntPtr.Zero);
mciSendString(@"seek tutorial to start", null, 0, IntPtr.Zero);
mciSendString(@"seek tutorial to end", null, 0, IntPtr.Zero);

  • 短路径肯定是能用的哦, 我只是懒得写了, 至于 “seek device to position” 的用法, 其中position默认是ms为单位的整数时间哦, 也就是说, 1000代表1s.
  • seek 的单位是可以调整的, 继续看哦

> 设置相关:

  • 设置, set, 这个支持的就更多了

以下是适用于 CD Audio 的语法 :
set device time format milliseconds
set device time format msf
set device time format tmsf

以下是适用于 Wave Audio 的语法 :
set device any input
set device any output
set device channels channel_count
set device format tag pcm
set device format tag tag
set device input integer
set device output integer
set device time format bytes
set device time format milliseconds

选项

描述

time format milliseconds

将时间格式设置为毫秒, 所有使用position值的指令都将采用毫秒作为单位, 你可以将milliseconds缩写为ms. 对于音序器设备,

time format msf

设置时间格式到 分钟, 妙, 以及帧. 所有使用position值的指令都见采用MSF格式(CD音频的默认格式), 请将MSF值指定为 mm:ss:ff 的格式, mm是分钟, ss是秒, ff是帧. 如果一个字段以及后面的字段都是0, 你可以忽略掉它. 例如, 3, 3:0, 3:0:0 都是表示3分钟的正确方式. MSF字段有以下最大值, 分钟:99, 秒:59, 帧: 74.

time format tmsf

将时间格式设置为 音轨, 分钟, 秒, 以及帧. 所有使用position值的指令都见采用TMSF格式, 额, 与上面的一样, 只不过多了个音轨. 音轨的最大值是99, 分钟, 秒, 帧的最大值与MSF格式一致.

any input

当录制时, 使用所有支持当前格式的输入, 这是默认设置

any output

当播放时, 使用所有支持当前格式的输出, 这是默认的

time format bytes

在 PCM 文件格式中, 设置时间格式(单位)为字节, 指定这个指令后, 所有position信息都将被指定为字节格式

> 状态信息:

  • 状态, status, 语法: “status device option”, 返回到 mciSendString 参数指定的字符串缓冲区

适用于音频的常用语法

status device position

status device length

status device mode

status device time format

选项

描述

position

获取目前播放的位置单位与时间格式统一

length

获取当前播放音频的长度 单位与时间格式统一

mode

获取播放状态, 返回的值是以下值之一: stopped / playing / paused

time format

获取当前的时间格式

string buffer = new string('\0', 256);       // 分配一个长度的字符串用来存放返回值
mciSendString("status tutorial position", buffer, 256, IntPtr.Zero);   // 调用
Console.WriteLine(buffer.TrimEnd('\0'));     // 打印返回值, TrimEnd的原因字符串的是长度是256, 函数没有使用的部分仍然是 \0 字符.

> 音频设置

  • 设置音频, setaudio, 语法 “setaudio device option

常用语法:

setaudio device left volume to factor

setaudio device right volume to factor

setaudio device volume to factor

选项

描述

left/right volume to factor

将指定声道的音量设置为指定值

volume to factor

将音量设置为指定值

错误码:

  • 下面是sendMciString会返回的错误码以及描述(对名称翻译, 然后稍加校正), 哦对了, 返回 0 代表无错误哦

错误码

名称

描述

257

MCIERR_INVALID_DEVICE_ID

无效设备 ID

259

MCIERR_UNRECOGNIZED_KEYWORD

未识别关键字

261

MCIERR_UNRECOGNIZED_COMMAND

未识别的命令

262

MCIERR_HARDWARE

硬件

263

MCIERR_INVALID_DEVICE_NAME

无效的设备名称

264

MCIERR_OUT_OF_MEMORY

内存不足

265

MCIERR_DEVICE_OPEN

设备打开

266

MCIERR_CANNOT_LOAD_DRIVER

无法加载驱动程序

267

MCIERR_MISSING_COMMAND_STRING

缺少命令字符串

268

MCIERR_PARAM_OVERFLOW

参数溢出

269

MCIERR_MISSING_STRING_ARGUMENT

缺少字符串参数

270

MCIERR_BAD_INTEGER

坏整数

271

MCIERR_PARSER_INTERNAL

分析器内部 (估计是这个API内部对指令分析时出现的错误)

272

MCIERR_DRIVER_INTERNAL

驱动程序内部

273

MCIERR_MISSING_PARAMETER

缺少参数

274

MCIERR_UNSUPPORTED_FUNCTION

不支持的功能

275

MCIERR_FILE_NOT_FOUND

未找到文件

276

MCIERR_DEVICE_NOT_READY

设备未就绪

277

MCIERR_INTERNAL

内部

278

MCIERR_DRIVER

驱动器

279

MCIERR_CANNOT_USE_ALL

不能全部使用

280

MCIERR_MULTIPLE

多个

281

MCIERR_EXTENSION_NOT_FOUND

未找到扩展

282

MCIERR_OUTOFRANGE

超出范围

284

MCIERR_FLAGS_NOT_COMPATIBLE

标志不兼容

286

MCIERR_FILE_NOT_SAVED

文件未保存

287

MCIERR_DEVICE_TYPE_REQUIRED

需要设备类型

288

MCIERR_DEVICE_LOCKED

设备已锁定

289

MCIERR_DUPLICATE_ALIAS

重复别名

290

MCIERR_BAD_CONSTANT

坏常量

291

MCIERR_MUST_USE_SHAREABLE

必须使用可共享

292

MCIERR_MISSING_DEVICE_NAME

缺少设备名称

293

MCIERR_BAD_TIME_FORMAT

错误的时间格式

294

MCIERR_NO_CLOSING_QUOTE

没有关闭中的引用

295

MCIERR_DUPLICATE_FLAGS

重复标志

296

MCIERR_INVALID_FILE

无效文件

297

MCIERR_NULL_PARAMETER_BLOCK

空参数块

298

MCIERR_UNNAMED_RESOURCE

未命名的资源

299

MCIERR_NEW_REQUIRES_ALIAS

新需要别名

300

MCIERR_NOTIFY_ON_AUTO_OPEN

自动打开时通知

301

MCIERR_NO_ELEMENT_ALLOWED

不允许任何元素

302

MCIERR_NONAPPLICABLE_FUNCTION

不可应用功能

303

MCIERR_ILLEGAL_FOR_AUTO_OPEN

非法自动打开

304

MCIERR_FILENAME_REQUIRED

需要文件名

305

MCIERR_EXTRA_CHARACTERS

额外字符 (可能是指多出了一些不需要的字符)

306

MCIERR_DEVICE_NOT_INSTALLED

未安装设备

307

MCIERR_GET_CD

获取 CD

308

MCIERR_SET_CD

设置 CD

309

MCIERR_SET_DRIVE

设置驱动器

310

MCIERR_DEVICE_LENGTH

设备长度

311

MCIERR_DEVICE_ORD_LENGTH

设备 ORD 长度

312

MCIERR_NO_INTEGER

无整数

320

MCIERR_WAVE_OUTPUTSINUSE

波输出

321

MCIERR_WAVE_SETOUTPUTINUSE

波设置输出

322

MCIERR_WAVE_INPUTSINUSE

波输入使用

323

MCIERR_WAVE_SETINPUTINUSE

波设置

324

MCIERR_WAVE_OUTPUTUNSPECIFIED

波输出未指定

325

MCIERR_WAVE_INPUTUNSPECIFIED

波输入未指定

326

MCIERR_WAVE_OUTPUTSUNSUITABLE

波输出可居住

327

MCIERR_WAVE_SETOUTPUTUNSUITABLE

波设置通普通西装

328

MCIERR_WAVE_INPUTSUNSUITABLE

波输入可居住

329

MCIERR_WAVE_SETINPUTUNSUITABLE

波设置通图适合

336

MCIERR_SEQ_DIV_INCOMPATIBLE

Seq Div 不兼容

337

MCIERR_SEQ_PORT_INUSE

SEQ 端口 INUSE

338

MCIERR_SEQ_PORT_NONEXISTENT

Seq 端口不存在

339

MCIERR_SEQ_PORT_MAPNODEVICE

Seq 端口地图无设备

340

MCIERR_SEQ_PORT_MISCERROR

SEQ 杂项错误

341

MCIERR_SEQ_TIMER

SEQ 定时器

342

MCIERR_SEQ_PORTUNSPECIFIED

SEQ 端口未指定

343

MCIERR_SEQ_NOMIDIPRESENT

SEQ 没有MIDI在场

346

MCIERR_NO_WINDOW

无窗口

347

MCIERR_CREATEWINDOW

创建窗口

348

MCIERR_FILE_READ

文件读取

349

MCIERR_FILE_WRITE

文件写入

350

MCIERR_NO_IDENTITY

无标识

封装类:

去这里吧, 在我的另一篇文章中 [C#] 音乐播放 3 种方式 Demo 与 MCI 音乐播放器封装类. o(〃‘▽’〃)o

附加内容:

  • 下面是我的一些发现
  1. WinForm 程序使用 MCI 是可以打开 MP3 文件的, 但是如果是控制台程序, 就会出现错误, 错误码266, “MCIERR_CANNOT_LOAD_DRIVER”
  2. MCI 的某些指令不能正常使用, 但其实并不是很影响, 例如, “set device audio left/right off/off”, 无法正常使用.
  3. 音量控制我目前还是没弄成… 不过可以确认的是, 进度获取, 调整, 长度获取是没问题的, 有这些最基本的, 就差不多公用了呢

放一张我写文章时的照片吧 , 原文贴的哪都是 (笑

[C#] MCI 详解与封装类, MCI 播放音乐, 获取播放状态, 获取音频长度, 进度调整,_win32


举报

相关推荐

0 条评论