音乐播放需要使用到 winmm.lib 库,如果你是使用的VC编译器,因为源代码中加了 #pragma 编译指令,会自动链接用到的一些库,所以你使用VC的编译器话,那么就不用手动添加链接库了。
但是用 #pragma链接库只对VC编译器有效,所以使用CodeBlocks 和 DevC++ 的需要手动添加链接库
音乐播放相关需要用到 winmm.lib 库, 所以必须加到链接库里,否则会出现链接错误(undefined reference to)
CodeBlocks 是加上libwinmm.a, 如下图,倒数第二个(配置时已经添加)
CodeBlocks
DevC++ 是加上 -lwinmm 的编译参数, 如下图(有些教程的配置里没加上,自己检查一下)
64位链接库的编译参数是
-lgraphics64 -luuid -lmsimg32 -lgdi32 -limm32 -lole32 -loleaut32 -lwinmm
32位链接库的编译参数是
-lgraphics -luuid -lmsimg32 -lgdi32 -limm32 -lole32 -loleaut32 -lwinmm
EGE有用于控制音乐播放的 MUSIC类, 是对 windowsAPI的一个封装,调用了
类定义如下:
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.OpenFile("音乐文件名(带扩展名)");
music.Play();
所以想要打开音乐播放,使用如下代码便可
MUSIC music;
music.OpenFile("音乐文件名");
music.Play(); //当然,如果没打开成功,是播放不出来的
下面这个是音乐播放测试
改成自己的音乐文件名, 带扩展名, 比如下面这首,就写成
music.OpenFile("马步谣-双笙.mp3");
如果无法播放,请检查一下文件名是否写对,如果文件名写对了还是无法播放,往后看,有解决办法
#include
int main()
{
initgraph(640, 480, 0);
setbkcolor(WHITE);
setcolor(BLACK);
setfont(40, 0, "楷体");
MUSIC music;
music.OpenFile("音乐名文件名(带扩展名)");
//检查是否打开
xyprintf(200, 220, "%s\n", music.IsOpen() ? "音乐打开成功" : "音乐打开失败");
music.Play();
getch();
music.Close();
closegraph();
return 0;
}
可能是音乐文件的格式不兼容,所以打不开。
解决方案:
下载一个格式工厂, 新版的有可能不行。将音频文件转换成mp3格式(本来是mp3格式的也要过一遍)
3.8版本格式工厂下载 http://down.pcgeshi.com/FormatFactory_3.8.0.0_setup.exe
4.8版本格式工厂下载
http://www.downza.cn/soft/187613.html
MUSIC类使用方法:
打开音乐文件用的是 OpenFile() 函数, 参数是带扩展名的音乐文件名。
比如我有个文件名为 musicFile.mp3的文件,在当前工作目录下(当前工作目录,一般是与新建的源文件同一个目录)。或者你直接使用带全路径的文件名。
那么打开文件方法如下:
music.OpenFile("musicFile.mp3");
if (music.IsOpen()) {
音乐文件已经成功打开
}
下面可以获取播放状态
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
检测到音乐文件已经成功打开后,便可以播放音乐。
函数声明如下:
下面的时间指的是音乐的时间,单位是毫秒
DWORD Play(DWORD dwFrom = MUSIC_ERROR, DWORD dwTo = MUSIC_ERROR);
即Play(开始时间, 结束时间)
music.Play();
music.Play(开始时间);
而且 music.Play(开始时间) 还可以调整播放进度,而且是立即播放
播放时状态是MUSIC_MODE_PLAY
music.Play(开始时间, 结束时间);
暂停后状态是 MUSIC_MODE_PAUSE
music.Pause();
但是我实验了好像Seek() 不能用,可以用 Play(时间) 代替
music.Seek( 毫秒数);
换成下面这个
music.Play(毫秒数);
停止是调用Stop()
调用后,播放状态是MUSIC_MODE_STOP。
调用 Stop(), 仅仅是将播放状态设为MUSIC_MODE_STOP, 并且暂停播放
但是播放进度并没有变,将停留在刚才的进度,你使用Play() 会从刚才停的地方继续播放。所以如果是处于停止状态,应该使用Play(0) , 而不是Play(),否则和暂停没区别
如果想要进度马上调整到开始处,可以使用
music.Play(0);
music.Stop();
停止后的播放应该是
if (music.GetPlayStatus() == MUSIC_MODE_STOP) {
musci.play(0);
}
返回音乐的总时长,单位是毫秒。
int musicDuration = music.GetLength();
(已播放的时间,单位是毫秒)
int playTime = music.GetPosition();
参数是float 型的,范围是0.0 ~ 1.0, 0表示没有声音,大于1和1无差别
默认是1.0
(这个音量是基于电脑的扬声器大小调节的,不会改变电脑的扬声器大小)
music.SetVolume(volume);
不使用当前音乐后,可以关闭当前音乐文件,同时音乐播放也就停止了。
如果想要用同一个 MUSIC 对象打开另一个音乐文件,需要先关闭再打开。
MUSIC类对象被销毁时自动调用Close()进行关闭
music.Close();
控制的时候先获取一下播放状态
(如果你停止的时候已经调用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();
(因为单单调用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);
音乐自动播放完毕后,状态为 MUSIC_MODE_STOP = 0x20D,此时调用 Play() 是无法继续播放的, 需要使用 Play(0) ,设置播放进度到开始位置。
//如果音乐已经播放完毕
if (music.GetPlayStatus() == MUSIC_MODE_STOP ) {
//从头开始播放
music.Play(0);
}
音乐文件名替换成自己的
如果遇到音乐不能播放,音乐文件名写成绝对路径形式试试,再不行就把音乐用格式工厂过一遍,转mp3再试试
#include
#include
#include
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";
//找歌名位置
size_t 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;
}