EGE基础:音乐播放篇

EGE专栏:EGE专栏

音乐播放

一、winmm.lib 库的链接检查

  音乐播放需要使用到 winmm.lib 库,如果你是使用的VC编译器,因为源代码中加了 #pragma 编译指令,会自动链接用到的一些库,所以你使用VC的编译器话,那么就不用手动添加链接库了。
  但是用 #pragma链接库只对VC编译器有效,所以使用CodeBlocksDevC++ 的需要手动添加链接库
  音乐播放相关需要用到 winmm.lib 库, 所以必须加到链接库里,否则会出现链接错误(undefined reference to)
CodeBlocks 是加上libwinmm.a,如下图(配置时已经添加
在这里插入图片描述
DevC++ 是加上 -lwinmm 的编译参数(有些教程的配置里没加上,自己检查一下)
在这里插入图片描述
64位链接库的编译参数是

-lgraphics64 -luuid -lmsimg32 -lgdi32 -limm32 -lole32 -loleaut32 -lwinmm -lgdiplus

32位链接库的编译参数是

-lgraphics -luuid -lmsimg32 -lgdi32 -limm32 -lole32 -loleaut32 -lwinmm -lgdiplus

二、 MUSIC类

  EGE有用于控制音乐播放的 MUSIC类, 是对 windowsAPI的一个封装,调用了 <mmsystem.h> 中的函数。
  类定义如下:

typedef unsigned long DWORD;

class MUSIC
{
public:
	MUSIC();
	virtual ~MUSIC();
	operator HWND()const{ return (HWND)m_dwCallBack; }
public:
	int   IsOpen() { return (m_DID != MUSIC_ERROR) ? 1 : 0; }
	DWORD OpenFile(LPCSTR  filepath);
	DWORD OpenFile(LPCWSTR filepath);
	DWORD Play(DWORD dwFrom = MUSIC_ERROR, DWORD dwTo = MUSIC_ERROR);
	DWORD Pause();
	DWORD Seek(DWORD dwTo); //播放位置定位,单位为ms
	DWORD SetVolume(float value);
	DWORD Close();
	DWORD Stop();
	DWORD GetPosition();
	DWORD GetLength();
	// 以下函数GetPlayStatus的返回值为以下之一(意义看后缀):
	// MUSIC_MODE_NOT_OPEN   //没有正确打开
	// MUSIC_MODE_NOT_READY  //设备没准备好 (较少使用)
	// MUSIC_MODE_PAUSE  //暂停中
	// MUSIC_MODE_PLAY   //正在播放
	// MUSIC_MODE_STOP   //成功打开后,或者播放完是这个状态
	// MUSIC_MODE_OPEN   //打开中 (较少使用)
	// MUSIC_MODE_SEEK   //定位中 (较少使用)
	DWORD GetPlayStatus();
private:
	DWORD       m_DID;
	PVOID       m_dwCallBack;
};

三、让音乐播放起来

先把文件扩展名显示设置打开

打开 文件资源管理器, 勾选上文件扩展名。这样就能看到完整的文件名。
在这里插入图片描述

进入当前目录

  如果你的源文件是新建的,那么就是源文件所在的目录。
  源文件就是你的代码文件, 建项目的时候创建有 .cpp 文件,懂吧?
在这里插入图片描述

把音乐文件放到源文件旁边

  东西比较多,显得凌乱,为了简单方便,就直接把音乐文件放在源文件旁边。
在这里插入图片描述

播放音乐的程序

  • 先定义一个MUSIC类变量
	MUSIC music;
  • 使用music.OpenFile( )打开音乐文件
    还可以使用 music.IsOpen() 检查是否打开了文件,打开成功返回true, 失败则返回false;
music.OpenFile("音乐文件名(带扩展名)");
if (music.IsOpen())
{
	//文件已被成功打开
}
  • 使用music.Play()播放音乐
music.Play();

所以想要打开音乐播放,使用如下代码便可

MUSIC music;
music.OpenFile("音乐文件名");
music.Play();			//当然,如果没打开成功,是播放不出来的
测试程序

  下面这个是音乐播放测试
在这里插入图片描述
  改成自己的音乐文件名, 带扩展名, 比如上面这首,就写成:(因为之前文件是放在源文件旁边的,所以在相对路径里可以省去文件前面的路径,只写文件名就可以了)

music.OpenFile("马步谣-双笙.mp3");
  • 先调节一下电脑的音量大小,以免放不出声音是因为没开声音

在这里插入图片描述
  如果无法播放,请检查一下 文件名 是否写正确了。 (是否带了文件扩展名,路径是否正确,可以用绝对路径试试)
  如果文件名写对了还是无法播放,往后看,有解决办法。

#include <graphics.h>

int main()
{
	initgraph(640, 480, 0);
	setbkcolor(WHITE);
	setcolor(BLACK);
	setfont(40, 0, "楷体");

	MUSIC music;
	music.OpenFile("音乐名文件名(带扩展名)");
	
	//检查是否打开
	if (music.IsOpen())
	{
		xyprintf(200, 220, "%s\n", "音乐打开成功");
		music.Play();
	}
	else
	{
		xyprintf(200, 220, "%s\n", "音乐打开失败");
	}
	
	getch();

	music.Close();

	closegraph();

	return 0;
}
如果音乐文件名正确,但是还是无法打开音乐文件

  可能是音乐文件的格式不兼容,所以打不开

解决方案

  对音乐文件使用格式转换软件进行转码。比如常用的QQ音乐播放器,里面就有音频转码功能。把音乐放进去转码一次,转换完成后应该就能打开了。

具体操作
  1. 打开QQ音乐播放器,点击右上角选项按钮,选择 音频转码
    在这里插入图片描述
  2. 点击添加歌曲,选择转换的音乐文件(可以在文件夹里先按住Ctrl,再分别点击要选的音乐文件,可以多选),然后选择保存位置的位置,最后点击开始转换,转换完成后,点击打开文件夹就能看到转化后的音乐文件了,此时就可以使用了。
    在这里插入图片描述

四、音乐播放器控制

MUSIC类使用方法:

1. 使用 OpenFile() 打开音乐文件

  打开音乐文件用的是 OpenFile() 函数, 参数是带扩展名的音乐文件名。
  比如我有个文件名为 musicFile.mp3的文件,在当前工作目录下(当前工作目录,一般是与新建的源文件同一个目录)。或者你直接使用带全路径的文件名
  那么打开文件方法如下:

	music.OpenFile("musicFile.mp3");

2. 使用 IsOpen() 检测音乐文件是否成功打开

  • 成功打开返回 true,否则返回false

播放之前最好先判断一下文件是否已经打开,否则可能会出现意外的结果

if (music.IsOpen()) {
	音乐文件已经成功打开
}

GetPlayStatus() 获取播放状态

  下面可以获取播放状态

DWORD playStatus = music.GetPlayStatus();

  返回值是以下枚举值之一, 可以根据状态来判断。
  比如,播放完毕自动重放,可以检查状态是否为 MUSIC_MODE_STOP, 是的话就 Play(0)

enum music_state_flag {
   MUSIC_MODE_NOT_OPEN = 0x0,			未打开文件
   MUSIC_MODE_NOT_READY = 0x20C,		没准备好
   MUSIC_MODE_PAUSE = 0x211,			暂停播放状态
   MUSIC_MODE_PLAY = 0x20E,				正在播放
   MUSIC_MODE_STOP = 0x20D,				停止播放状态
   MUSIC_MODE_OPEN = 0x212,				已打开文件
   MUSIC_MODE_SEEK = 0x210,				播放进度条调整(未播放)
};

  音乐打开加载完成后,播放状态为 MUSIC_MODE_STOP

3. 使用 Play() 播放音乐

  播放之前最好先判断一下文件是否已经打开,否则可能会出现意外的结果

  检测到音乐文件已经成功打开后,便可以播放音乐。
  函数声明如下:
  下面的时间指的是音乐的时间,单位是毫秒

DWORD Play(DWORD dwFrom = MUSIC_ERROR, DWORD dwTo = MUSIC_ERROR);

Play(开始时间, 结束时间)

  • 继续播放使用
music.Play();
  • 从音乐某个时间开始播放至音乐结束
music.Play(开始时间);

  而且 music.Play(开始时间) 还可以调整播放进度,而且是立即播放
  播放时状态是MUSIC_MODE_PLAY

  • 从音乐某个时间开始播放,播放到指定时间停止
music.Play(开始时间, 结束时间);

Pause() 暂停播放

  暂停后状态是 MUSIC_MODE_PAUSE

music.Pause();

Seek() 调节播放进度

  但是我实验了好像Seek() 不能用,可以用 Play(时间) 代替

	music.Seek( 毫秒数);

  换成下面这个

   music.Play(毫秒数);

Stop() 停止播放

停止是调用Stop()
调用后,播放状态是MUSIC_MODE_STOP
调用 Stop(), 仅仅是将播放状态设为MUSIC_MODE_STOP, 并且暂停播放
但是播放进度并没有变,将停留在刚才的进度,你使用Play() 会从刚才停的地方继续播放。所以如果是处于停止状态,应该使用Play(0) , 而不是Play(),否则和暂停没区别

如果想要进度马上调整到开始处,可以使用

   music.Play(0);
   music.Stop();

停止后的播放应该是

if (music.GetPlayStatus() == MUSIC_MODE_STOP) {
	music.play(0);
}

GetLength() 获取音乐时长

返回音乐的总时长,单位是毫秒。

int musicDuration = music.GetLength();

GetPosition() 获取音乐播放进度

(已播放的时间,单位是毫秒)

int playTime = music.GetPosition();

SetVolume() 调节音量

参数是float 型的,范围是0.0 ~ 1.0, 0表示没有声音,大于1和1无差别
默认是1.0
(这个音量是基于电脑的扬声器大小调节的,不会改变电脑的扬声器大小)

music.SetVolume(volume);

Close() 关闭音乐文件

不使用当前音乐后,可以关闭当前音乐文件,同时音乐播放也就停止了。
如果想要用同一个 MUSIC 对象打开另一个音乐文件,需要先关闭再打开。
MUSIC类对象被销毁时自动调用Close()进行关闭

music.Close();

五、音乐播放状态切换示例

控制的时候先获取一下播放状态

  • 播放
    第一次播放直接 Play() 就可, 如果是在播放时候切换状态,可以先检测一下,避免多余操作。
    方法: 如果是暂停,那么调用 Play() 继续播放,如果是停止,那么调用 Play(0) 重新播放(如果你停止的时候已经调用Play(0)将进度调整到了开头,那么直接用Play()就行)
DWORD status = music.GetPlayStatus();
if (status == MUSIC_MODE_PAUSE)
	music.Play();
else if (status == MUSIC_MODE_STOP)
	music.Play(0);
  • 暂停
    如果正在播放,那么就暂停
DWORD status = music.GetPlayStatus();
if (status == MUSIC_MODE_PLAY)
	music.Pause();
  • 停止
    如果处于播放或暂停,那么先调用 Play(0) 将进度调至开头,再调用 Stop() 停止(因为单单调用Stop()并不会改变播放进度)
DWORD status = music.GetPlayStatus();
if (status != MUSIC_MODE_STOP) {	
	music.Play(0);
	music.Stop();
}
  • 播放暂停切换
DWORD status = music.GetPlayStatus();
if (status == MUSIC_MODE_PLAY)
	music.Pause();
else if (status == MUSIC_MODE_PAUSE)
	music.Play();
else if (status == MUSIC_MODE_STOP)
	music.Play(0);

  • 调整播放进度
    直接调用Play(播放时间), 单位是毫秒

六、注意事项

音乐重复播放

  音乐自动播放完毕后,状态为 MUSIC_MODE_STOP = 0x20D,可以通过这个状态判断音乐是否播放结束,结束后从头开始播放即可。
  此时调用 Play() 是无法继续播放的, 需要使用 Play(0) ,设置播放进度到开始位置。
  播放结束判断及从头播放处理如下:
  需要注意,这需要周期检测,可以放到帧循环了。

//如果音乐已经播放完毕
if (music.GetPlayStatus() == MUSIC_MODE_STOP ) {
	//从头开始播放
	music.Play(0);
}



音乐播放示例程序

音乐文件名替换成自己的

如果遇到音乐不能播放,音乐文件名写成绝对路径形式试试,再不行就把音乐用QQ音乐音频转码过一遍,转mp3再试试

#include <graphics.h>
#include <string.h>
#include <math.h>

class ProgressBar
{
private:
	//进度条样式参数
	int x, y, width, height;	//位置,尺寸
	color_t progressBkcolor;	//进度条背景色
	color_t progressColor;		//进度条进度颜色
	float progress;				//进度

public:
	ProgressBar(int x, int y, int width, int height) : x(x), y(y), width(width), height(height) {
		progressBkcolor = LIGHTGRAY;
		progressColor = LIGHTBLUE;
		progress = 0.0f;
	}

	void setProgress(float progress) {
		this->progress = progress;
		draw();
	}

private:
	void draw() {
		int cur = (int)(x + width * progress);

		setfillcolor(progressColor);
		bar(x, y, cur, y + height);
		setfillcolor(progressBkcolor);
		bar(cur, y, x + width, y + height);
	}
};

int main()
{
	initgraph(640, 480, INIT_RENDERMANUAL);			//初始化窗口
	setcaption("EGE音乐播放");		//设置窗口标题

	setbkcolor(WHITE);
	setcolor(BLACK);
	setfont(16, 0, "楷体");

	xyprintf(20, 20, "按空格键播放/暂停, S 键停止");
	xyprintf(20, 40, "P键增大音量,M键减小音量");
	xyprintf(20, 60, "N键前进,B键后退");
	xyprintf(20, 80, "Q键退出");

	setcolor(LIGHTBLUE);

	//手动渲染模式,先刷新一下窗口,让背景色先出来
	//因为音乐加载需要时间,这段时间窗口是黑色的
	delay_ms(0);

	MUSIC music;
	//音乐文件名,改成自己的音乐
	const char* musicFile = "resource/music/薛之谦 - 花儿和少年.mp3";
	//找歌名位置
	int len = strlen(musicFile);
	while (len >= 0 && musicFile[len] != '/' && musicFile[len] != '\\') {
		--len;
	}
	const char* musicName = musicFile + len + 1;
	music.OpenFile(musicFile);

	if (music.IsOpen()) {
		ProgressBar progressBar(20, 200, 500, 20);
		progressBar.setProgress(0.0f);

		music.Play(0);

		int playTime = 0;				
		int totalTime = music.GetLength();		//获取音乐总时长(单位:毫秒)
		float volume = 0.8f;					//音量	
		music.SetVolume(volume);

		xyprintf(20, 160, "音乐名:%s", musicName);
		xyprintf(400, 240, "音乐总时长:%02d:%02d",
			totalTime / 1000 / 60, totalTime / 1000 % 60);

		bool exit = false;

		//上次调节进度时间
		double lastAdjustTime = fclock(), curtime = lastAdjustTime;

		//检测键盘,控制音乐播放
		for (; is_run(); delay_fps(20)) {
			if (exit)
				break;

			while (kbmsg()) {
				key_msg keyMsg = getkey();
				if (keyMsg.msg != key_msg_down)
					continue;

				DWORD status = music.GetPlayStatus();
				int key = keyMsg.key;
				switch (key) {
				case 'Q':		//退出
					exit = true;
					break;
				case ' ':		//播放暂停切换
					if (status == MUSIC_MODE_PLAY)
						music.Pause();
					else if (status == MUSIC_MODE_PAUSE)
						music.Play();
					else if (status == MUSIC_MODE_STOP)
						music.Play(0);
					
					break;
				case 'S':		//停止
					if (status != MUSIC_MODE_STOP) {	
						music.Play(0);
						music.Stop();
					}
					break;

				case 'P':		//增加音量
					if ((volume += 0.1f) >= 1)
						volume = 1;
					music.SetVolume(volume);
					break;
				case 'M':		//减小音量
					if ((volume -= 0.1f) < 0)
						volume = 0;
					music.SetVolume(volume);
					break;
				case 'N':		//快进
					//no-break;
				case 'B':		//快退
					curtime= fclock();
					if (curtime- lastAdjustTime < 0.3)
						break;
					lastAdjustTime = curtime;

					if (key == 'N') {
						playTime += 10 * 1000;
						if (playTime > totalTime)
							playTime = totalTime;
					}
					else {
						playTime -= 10 * 1000;
						if (playTime < 0)
							playTime = 0;
					}
					music.Play(playTime);
					break;
				}
			}
			//获取播放时间(毫秒)
			int pos = music.GetPosition();

			//更新进度条
			if (pos != playTime) {
				playTime = pos;
				progressBar.setProgress(playTime * 1.0f / totalTime);
			}
			xyprintf(20, 240, "播放进度%02d:%02d", playTime / 1000 / 60, playTime / 1000 % 60);
			xyprintf(20, 280, "音量%3.0f%%", (volume * 100));
		}

		//关闭音乐
		music.Close();
	}
	else {
		xyprintf(50, 200, "打开音乐失败,请检查音乐文件名");
		getch();
	}

	closegraph();

	return 0;
}

在这里插入图片描述
  播放、暂停、停止,前进后退,显示音乐时长和播放进度,调节音量都已实现。


EGE专栏:EGE专栏

  • 35
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

依稀_yixy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值