最近接到个小任务,用MFC做一个音乐播放器,不过这也是一直想尝试的,于是今天早上9点到下午16:00,总算是完成了需要的功能
开门见山,先展示一下整体界面
下面按照功能模块介绍我做这个播放器的过程
预加载歌曲
我是在一个txt文件里面记录了我的本地音乐的路径,然后程序启动的时候就读取这个txt,根据路径去查找音乐
vector<CString>preLoadMusicPath;
vector<CString>preLoadMusicName;
CListBox listBox;//这个变量是在自己在listctrl控件上面绑定的
BOOL CMyMusicPlayerDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
CString strFileName = _T("musicPath.txt");
if (!PathFileExists(strFileName))
{
return FALSE;
}
CStdioFile file;
if (!file.Open(strFileName, CFile::modeRead))
{
return FALSE;
}
CString temp;
CString strValue = _T("");
while (file.ReadString(strValue))
{
int pos = strValue.ReverseFind(_T('\\'));
preLoadMusicPath.push_back(strValue);
preLoadMusicName.push_back(strValue.Mid(pos+1, strValue.GetLength()));
// preLoadBook.push_back(v1);
}
file.Close();
for (int i = 0; i < preLoadMusicPath.size(); i++)
{
listBox.AddString(preLoadMusicName[i]);
}
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
下图就是预加载的结果,
添加歌曲
首先,先来做添加歌曲的部分
第一步自然需要点击按钮然后打开文件框选项选择音乐文件
#include "stdafx.h"
#include "MyMusicPlayer.h"
#include "MyMusicPlayerDlg.h"
#include "afxdialogex.h"
#include "mmsystem.h"
HWND m_hWnd;//记录当前窗口的句柄
//DWORD DeviceID;//指定播放音乐的设备
MCI_OPEN_PARMS mciopenparms;//指定打开音乐文件的参数
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
CString fullPath;
MCIDEVICEID m_IDdevice;
注意我包含了头文件"mmsystem.h"
void CMyMusicPlayerDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CString musicName;
//CString fullPath;
CString m_strLilePath;
CFileDialog dlgFile(TRUE, NULL, NULL,
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_ALLOWMULTISELECT | OFN_EXPLORER,
_T("All files|*.mp3;*.wma;*.wav|MP3 (*.MP3)|*.mp3|WMA (*.wma)|*.wma||"),
NULL);
if (dlgFile.DoModal() == IDOK)
{
//CString img_select;
fullPath = dlgFile.GetPathName();
musicName = dlgFile.GetFileName();
preLoadMusicPath.push_back(fullPath);
int pos = fullPath.ReverseFind(_T('\\'));
preLoadMusicName.push_back(fullPath.Mid(pos + 1, fullPath.GetLength()));
listBox.AddString(musicName);
//Load(this->m_hWnd, fullPath);
SetDlgItemText(IDC_EDIT1, fullPath);//img_select为选择的文件的路径,IDC_IMGSELECTEDIT为edit控件名称
mciSendCommand(m_IDdevice, MCI_CLOSE, 0, 0);
//打开一首歌曲
MCI_OPEN_PARMS mciOpenParms;
mciOpenParms.lpstrElementName = fullPath;
//MessageBox(0strFilePath);
curPlayPos = preLoadMusicName.size() + 1;//当前的播放位置,播放第几首歌曲,这的意思就是播放刚添加的歌曲
mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_ELEMENT | MCI_WAIT, (DWORD)&mciOpenParms);
m_IDdevice = mciOpenParms.wDeviceID;
}
}
这里会报错
这是因为mciSendCommand这个函数需要一个lib来链接,这个lib就是winmm.lib
所以我们还需要如下代码放在最前面
#pragma comment(lib,"winmm.lib")
当然,你也可以找到这个lib文件,然后 ,在 项目->属性->VC++目录->库目录
我把winmm.lib放在下面的文件夹的,所以我在库目录包含了这个文件夹
之后我们需要在 链接器->输入->附加依赖项加上这个lib的名字
播放歌曲
void CMyMusicPlayerDlg::OnBnClickedButton2()
{
SetDlgItemText(IDC_EDIT2,preLoadMusicName[curPlayPos]);
MCI_PLAY_PARMS mciPlayParms;
mciPlayParms.dwCallback = NULL;
mciPlayParms.dwFrom = 0;
mciSendCommand(m_IDdevice, MCI_PLAY, MCI_FROM | MCI_NOTIFY, (DWORD)&mciPlayParms);
}
暂停播放
void CMyMusicPlayerDlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
CString str;
GetDlgItemText(IDC_BUTTON3, str);
if (str == _T("暂停"))
{
mciSendCommand(m_IDdevice, MCI_PAUSE, 0, 0);
SetDlgItemText(IDC_BUTTON3, _T("继续"));
}
else {
mciSendCommand(m_IDdevice, MCI_RESUME, 0, 0);
SetDlgItemText(IDC_BUTTON3, _T("暂停"));
}
}
停止播放
void CMyMusicPlayerDlg::OnBnClickedButton4()
{
// TODO: 在此添加控件通知处理程序代码
//SetDlgItemText(IDC_EDIT2, "");
mciSendCommand(m_IDdevice, MCI_STOP, 0, 0);
mciSendCommand(m_IDdevice, MCI_CLOSE, 0, 0);
}
删除歌曲
大概思路就是先获取Listbox上光标的位置,然后去定位选择的需要删除的歌曲在preloadMusicName中的位置,去vector容器里面先把这首歌删了,然后重新写入listBox展示,最后退出软件时,也会把操作后的结果写入txt保存,以便重新打开软件时进行预加载
void CMyMusicPlayerDlg::OnBnClickedButton7()
{
// TODO: 在此添加控件通知处理程序代码
curPlayPos = 0;
int pos = listBox.GetCurSel();
listBox.ResetContent();
int count = 0;
for (vector<CString>::iterator iter = preLoadMusicPath.begin(); iter != preLoadMusicPath.end(); iter++)
{
if (count == pos)
{
preLoadMusicPath.erase(iter);
break;
}
count++;
}
int count2 = 0;
for (vector<CString>::iterator iter = preLoadMusicName.begin(); iter != preLoadMusicName.end(); iter++)
{
//std::cout << iter->a << iter->b << iter->c << std::endl;
if (count2 == pos)
{
preLoadMusicName.erase(iter);
break;
}
count2++;
}
for (int i = 0; i < preLoadMusicName.size(); i++)
{
listBox.AddString(preLoadMusicName[i]);
}
}
上一曲
void CMyMusicPlayerDlg::OnBnClickedButton5()
{
// TODO: 在此添加控件通知处理程序代码
if (curPlayPos ==0)
{
MessageBox(_T("已经到达第一首歌曲"));
}
if (curPlayPos > 0)
{
curPlayPos = curPlayPos - 1;
listBox.SetCurSel(curPlayPos);
mciSendCommand(m_IDdevice, MCI_CLOSE, 0, 0);
//打开一首歌曲
MCI_OPEN_PARMS mciOpenParms;
mciOpenParms.lpstrElementName = preLoadMusicPath[curPlayPos];
//MessageBox(preLoadMusicPath[pos]);
mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_ELEMENT | MCI_WAIT, (DWORD)&mciOpenParms);
m_IDdevice = mciOpenParms.wDeviceID;
OnBnClickedButton2();//就是调用播放函数
}
}
下一曲
void CMyMusicPlayerDlg::OnBnClickedButton6()
{
// TODO: 在此添加控件通知处理程序代码
if (curPlayPos == preLoadMusicName.size()-1)
{
MessageBox(_T("已经到达最后一首歌曲"));
}
if (curPlayPos < preLoadMusicName.size() - 1)
{
curPlayPos = curPlayPos + 1;
listBox.SetCurSel(curPlayPos);
mciSendCommand(m_IDdevice, MCI_CLOSE, 0, 0);
//打开一首歌曲
MCI_OPEN_PARMS mciOpenParms;
mciOpenParms.lpstrElementName = preLoadMusicPath[curPlayPos];
//MessageBox(preLoadMusicPath[pos]);
mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_ELEMENT | MCI_WAIT, (DWORD)&mciOpenParms);
m_IDdevice = mciOpenParms.wDeviceID;
OnBnClickedButton2();
}
}
音量调节
这一部分是耗我时间最久的了,主要参考别人的,别人的可以,放在我的程序上面就不行了
音量调节的思路是通过滑块调节系统声音
找到 类视图->属性
会生成下面的函数
void CMyMusicPlayerDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
UpdateData(TRUE);
volume = slide.GetPos();
CoInitialize(0);
nVolume = SetVolum(volume);
CoUninitialize();
CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
UpdateData(FALSE);
}
然后我们看看SetVolum函数的定义
#include <Digitalv.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <audioclient.h>
bool SetVolum(int volume)
{
HRESULT hr;
IMMDeviceEnumerator* pDeviceEnumerator = 0;
IMMDevice* pDevice = 0;
IAudioEndpointVolume* pAudioEndpointVolume = 0;
IAudioClient* pAudioClient = 0;
try {
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pDeviceEnumerator);
if (FAILED(hr)) throw "CoCreateInstance";
hr = pDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDevice);
if (FAILED(hr)) throw "GetDefaultAudioEndpoint";
hr = pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void**)&pAudioEndpointVolume);
if (FAILED(hr)) throw "pDevice->Active";
hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient);
if (FAILED(hr)) throw "pDevice->Active";
float fVolume;
fVolume = volume / 100.0f;
hr = pAudioEndpointVolume->SetMasterVolumeLevelScalar(fVolume, &GUID_NULL);
if (FAILED(hr)) throw "SetMasterVolumeLevelScalar";
pAudioClient->Release();
pAudioEndpointVolume->Release();
pDevice->Release();
pDeviceEnumerator->Release();
return true;
}
catch (...) {
if (pAudioClient) pAudioClient->Release();
if (pAudioEndpointVolume) pAudioEndpointVolume->Release();
if (pDevice) pDevice->Release();
if (pDeviceEnumerator) pDeviceEnumerator->Release();
throw;
}
return false;
}
同时我们需要在OnInitDialog()函数进行初始化
slide.SetRange(0, 100);//设置滑动范围为1到20
slide.SetTicFreq(1);//每1个单位画一刻度
CoInitialize(0);
nVolume = GetVolume();
CoUninitialize();
slide.SetPos(nVolume);//设置滑块初始位置为1
volume = nVolume;
UpdateData(FALSE);
int GetVolume()
{
HRESULT hr;
IMMDeviceEnumerator* pDeviceEnumerator = 0;
IMMDevice* pDevice = 0;
IAudioEndpointVolume* pAudioEndpointVolume = 0;
IAudioClient* pAudioClient = 0;
try {
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pDeviceEnumerator);
if (FAILED(hr)) throw "CoCreateInstance";
hr = pDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDevice);
if (FAILED(hr)) throw "GetDefaultAudioEndpoint";
hr = pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void**)&pAudioEndpointVolume);
if (FAILED(hr)) throw "pDevice->Active";
hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient);
if (FAILED(hr)) throw "pDevice->Active";
float fVolume;
hr = pAudioEndpointVolume->GetMasterVolumeLevelScalar(&fVolume);
if (FAILED(hr)) throw "SetMasterVolumeLevelScalar";
pAudioClient->Release();
pAudioEndpointVolume->Release();
pDevice->Release();
pDeviceEnumerator->Release();
int intVolume = fVolume * 100;
if (fVolume > 100)
{
fVolume = 100;
}
return intVolume;
}
catch (...) {
if (pAudioClient) pAudioClient->Release();
if (pAudioEndpointVolume) pAudioEndpointVolume->Release();
if (pDevice) pDevice->Release();
if (pDeviceEnumerator) pDeviceEnumerator->Release();
throw;
}
}
退出软件时把最新结果写入txt文件
需要注意的是,由于txt中有中文,所以需要把项目的字符集改成多字节字符集
同时需要包含头文件
#include <locale>//为了使用setlocale(LC_CTYPE, "chs");便于输入中文到txt中
void CMyMusicPlayerDlg::OnBnClickedCancel()
{
// TODO: 在此添加控件通知处理程序代码
CString filename = _T("musicPath.txt");
CString strWriteData;
//strWriteData.Format(_T("测试写入中文和Endlish¥\n"));
CStdioFile csFile;
CFileException cfException;
if (csFile.Open(filename, CFile::typeText | CFile::modeCreate | CFile::modeReadWrite, &cfException))//以txt方式读取|若没有文件则创建该文件|文件打开时不清除
{
csFile.SeekToEnd();
setlocale(LC_CTYPE, "chs");//为了能用csFile.WriteString(strWriteData)写入中文
for (int i = 0; i < preLoadMusicPath.size(); i++)
{
strWriteData = preLoadMusicPath[i]+_T("\n");
csFile.WriteString(strWriteData);
}
}
csFile.Close();
CDialogEx::OnCancel();
}
注意事项
这个播放器某些mp3音乐是不能播放的,我也不知道为什么,但是大部分还是可以播放的,比较抱歉的是代码不是很规范,比如控件的名字没改,导致生成的响应函数可读性比较差
好了,终于介绍完了,有很多细节是需要自己探索,百度的
附件
完整源代码及参考资料
https://download.csdn.net/download/ningmengshuxiawo/19224419