TXT音乐播放器与DirectSound与C++,开发笔记与EXE免费下载(一)_c# directsound 程序下载-程序员宅基地

技术标签: c++  

一、前言

之前提到,使用C语言开发TXT音乐播放器、使用PlaySound方法播放wav文件时,无法同时播放多个wav文件:当开始播放下一个wav文件时,之前正在播放的wav文件就会停止,导致音乐播放不连贯,卡顿,体验极差。

通过百度发现,PlaySound方法确实是无法同时播放多个文件的,mciSendString也不行!

在找C语言的其它音乐播放方法的途中,终于找到了一个:DirectSound方法,可以实现同时播放多个wav文件,然后就开始长达多日的踩坑爬坑之旅......

 

二、软件说明

由于各种原因,本次软件采用C++开发,使用了MFC,使用了Microsoft DirectX SDK (June 2010),使用了自定义CWaveFile.h、CWaveFile.cpp、DxErr.h、dxerr.cpp等相关技术与文件,目前将最终生成的exe包与测试用音乐txt上传到了CSDN上,大家可以免费下载;目前仍在完善中,敬请期待后续文章与资源。

CSDN免费下载链接:https://download.csdn.net/download/BHSZZY/12447447

二、软件截图

 

三、开发(踩坑)流程

1.首先,本人是想尝试使用C语言中使用DirectSound方法的,并且也想顺便写个图形界面;然而用C语言搞图形界面实在是有难度(主要是百度不到),更重要的原因是DirectSound方法需要的两个资源文件CWaveFile.h、CWaveFile.cpp,这两个明显是C++写的,如果想直接用这两个文件,那我也只能用C++开发了。至于这两个文件是什么,我会在下面提到。

 

2.决定了用C++开发图形界面,然后开始百度,找了不少直接上代码进行开发图形界面的,然而看了半天还是不懂;这明显比Java的JFrame复杂多了好吧!期间,我都想用VB、C#开发图形界面了,然而又发现不能使用CWaveFile.cpp;最后,终于找到C++快速开发图形界面的方法了:MFC

 

3.决定了用MFC,然而使用Visual Studio 2017新建项目时,发现不能创建MFC项目;又百度了半天,原来还得单独下载这个功能;创建了MFC项目吧,又找不到哪里直接拖控件;琢磨了半天,才发现创建MFC时要选择“基于对话框”;创建完成后什么页面也没有,还得自己从右侧"资源视图"标签中,找到"Dialog"文件夹,打开里面的文件,才能显示图形界面;然后再从左侧工具箱把控件拖过去。

 

4.拖好控件后,双击控件可以进入对应的cpp代码文件,并自动创建一个默认的事件函数,一般是onClick的;实际上这种方法有时不太好用(亲测不好用);我创建的MFC自带2个Dialog(都在同一个cpp中,一个主要的一个关于的,关于的那个窗口是内部class),本来我是在第1个dialog中双击控件的,然而不知道怎么的跳转到了第2个dialog中创建了onClick方法,然后我添加点击事件后发现怎么点按钮都没有触发,程序又不报错,很郁闷(对MFC还是不熟的原因);后来才发现它给我生成的方法属于第2个Dialog,略坑。

 

5.因此,最好右击控件选择“添加事件处理程序”,在“消息列表”中选择需要的监听函数(click、focus等),然后会自动生成相关的方法,在其中编写处理逻辑即可。右击控件菜单中的“添加变量”、“类向导”也挺好用的。

 

6.MFC线程问题:这也是个坑。点击按钮后,如果不使用线程执行,那么在你的方法执行完毕之前,窗口是处于卡死状态的,只有后续方法执行完毕后,窗口才能再次响应。因此必须开启线程。网上有相关线程开启方法,个人觉得还是thread好用,样例如下:

要调用的begin方法:

void CMFC2Dlg::begin(char url[], int sleepTime, HWND nhwnd) {

//内容

}

//其中CMFC2Dlg::的意思是我在头文件CMFC2Dlg.h中声明了函数 static void begin(char url[], int sleepTime, HWND nhwnd);然后在CMFC2Dlg.cpp中实现了函数体。

启动线程的方法:

thread th1(begin, url, sleepTime, m_hWnd);

th1.detach();

//其中thread是线程对象,需要#include<thread>与using namespace std;begin是函数名,之后是参数。

 

7.说起线程,不得不说一个坑,那就是C++线程中的方法(例如上方的begin)必须是静态的(static),否则总会报错(不支持begin与参数啥的),然而你直接搜C++使用线程时,网上的教程很少告诉你这一点,代码例子中也没有提到(他们的方法为啥不加static呢?),就算是常识,个人觉得也应该写明白,不然像我这样的萌新是半天找不出来哪里错了的,一直以为是thread的参数哪里出问题了,换个其他开启线程的方法能不能行(当然都不行,只要你的方法没有写static)。C++这个报错报的也不明显,你说不支持begin与参数啥的,我怎样才能想到是由于没有static呢?你就不能直说“该方法方法不是static,不能使用线程”吗?

 

8.与线程相关的一个坑,由于我在线程中需要使用HWND的m_hWnd对象;众所周知在Dialog的普通方法中是可以直接使用m_hWnd这个参数的,它在afxwin.h中(我的cpp中居然没有引用,我怀疑创建时自动生成的代码 #include "afxdialogex.h" 中包含了),就是一个窗体相关的对象;然而线程必须使用静态方法(static),在static方法中使用m_hWnd会报错,使用相关的获取窗体m_hWnd的方法也会报错,总之就是不能用;我猜是静态方法创建时窗体还没生成,导致不让使用这个对象;那怎么办呢?卡了我半天,才反应过来,可以在普通方法中,在启动线程时,把m_hWnd当成参数传过去就可以了。

 

9.与C++报错相关的一个坑,这是关于网上下载的CWaveFile.h、CWaveFile.cpp这两个文件的;好不容易在网上找到大佬的这两个文件的代码,赶紧创建文件,复制、粘贴进去;放到C++里一用,发现各种报错,找不到对应的标识符什么的;例如这一句:

        WAVEFORMATEX* m_pwfx;

然后报错"无法识别的标识符WAVEFORMATEX",大概就这个意思,点开相关文件,发现"WAVEFORMATEX"被红波浪线标注了;

然后我想,是缺少什么相关的文件需要include吗?

找了半天,添加了一堆头文件,又出了一堆错误,依然不知道为什么;新增的错误是缺少其它头文件,拜它影响,这个本质的错误我几乎都忽略了。

后来我又删除了项目,重新建立了一个,添加最少的头文件,终于重新锁定了这个错误,但是怎么解决呢?"WAVEFORMATEX"还是被红波浪线标注突。

突然灵光一现,想到了网上复制的代码有非空格的空白符的问题,于是删掉了那句之前的空白,改为:

WAVEFORMATEX* m_pwfx;//删掉了前面的空格

居然就通过了!

在此不得不吐槽C++的报错机制,你发现无法识别的非空格空白符了,那你直接标红那些空白符就行了,你标红"WAVEFORMATEX"是几个意思?太容易让人误解了吧?

这还是.h文件的,内容较少,好修改;关于.cpp文件中的无法识别的非空格空白符,报错也不明显,语句又多,把每句之前和之后的多余的空白符删掉有些难度。

因此下方我会直接提供代码的压缩包,而不是源码,导致出现非空格空白符错误让人莫名其妙。

 

10.关于网上下载的CWaveFile.h、CWaveFile.cpp;首先,当你安装Microsoft DirectX SDK (June 2010)时,我的在C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Samples\C++\DXUT\Optional下,会有SDKwavefile.h与SDKwavefile.cpp两个文件,意思一样,不过使用时还需要  #include "DXUT.h"  等相关的头文件,我整了半天,还是不会用;

因此找到了网上大佬自己封装的无需DXUT.h的文件,使用时自己再加上#include "DxErr.h"就行。

 

11.关于CWaveFile.h、CWaveFile.cpp​​​​​​​的第二点:这两个文件主要是用来读取wav文件的,使用时要记得#include "CWaveFile.h"
#include <CWaveFile.cpp>;本人就是忘了包含cpp文件导致使用时找不到Open方法(还有包含cpp这种操作,百度这两文件的使用方法居然不写明白);还有关于#include "DxErr.h",#include <dxerr.h>;虽然CWaveFile.cpp中已经写过了,可是会报错,因此我删掉了那一句并写到了自己的主cpp中;虽然有大佬说直接删掉就行,可是后续的相关变量就又要报错了,为了省事还是写上吧。关于播放wav文件的Play方法,使用的是dsound.h中的方法。

 

12.关于C++静态变量的坑:在C++中,需要在.h文件中声明静态变量(例如static int isPlay;),然后需要在.cpp文件中初始化(例如int CMFC2Dlg::isPlay = 1;),然后才能正常在cpp其它方法中使用。(与java不同,我想在方法中直接使用,结果就报错了,还得初始化。)

 

13.关于VS2017使用DirectSound方法的环境的配置:这是个大坑,因此全程加粗。

本人配置了好几次,总是出各种莫名其妙还很难百度的问题,因此删了好几次项目重新搭建,现在把可以使用的配置流程写在下方:

(1)安装Microsoft DirectX SDK (June 2010),如果出现s1023错误,就卸载更高的版本“Microsoft Visual C++ 2010 x86 Redistributable - 1010.0.40219”和“Microsoft Visual C++ 2010 x64 Redistributable - 1010.0.40219”,再重新安装DXSDK_Jun10

(2)打开VS2017

(3)项目 - XXX(你的项目名)属性 - VC++目录,包含目录 中增加 $(DXSDK_DIR) Include ;库目录中增加 $(DXSDK_DIR)Lib\x86

(4)项目 - XXX(你的项目名)属性 - C/VC++ - 常规,附加包含目录中增加 D:\headFile 。这是我自己创建的目录,其中包含CWaveFile.h、CWaveFile.cpp​​​​​​​ 这两个文件。顺便,我在其中也放了dxerr.cpp和DxErr.h这两个文件。

(5)CWaveFile.h、CWaveFile.cpp​​​​​​​这两个文件从网上找代码复制粘贴,有个大佬的CSDN中有,或者下载我之后的源代码;dxerr.cpp和DxErr.h这两个文件通过搜索你安装的Microsoft DirectX SDK (June 2010)就能找到,复制出来放到自定义目录下。

(6)项目 - XXX(你的项目名)属性 - C/VC++ - 预处理器,预处理器定义中添加 “_CRT_SECURE_NO_WARNINGS” ,可以让你正常使用例如fopen等方法,否则会报错不安全而不让使用。

(7)项目 - XXX(你的项目名)属性 - 链接器 - 输入,附加依赖项添加

d3d9.lib
d3dx10d.lib
d3dx9d.lib
dxguid.lib
winmm.lib
comctl32.lib
dsound.lib
DxErr.lib
legacy_stdio_definitions.lib

这是我用到的(反正添加后没报错,不碍事)。

(8)在主cpp中添加
#include <iostream>
#include<thread>
using namespace std;
#include <string.h>
#include <stdio.h>
#include  <direct.h>
#include <mmsystem.h>
#include <dsound.h>
#include "CWaveFile.h"
#include <CWaveFile.cpp>
#include "DxErr.h"
#include <dxerr.h>

还有一些是创建MFC自动生成的include,在此我就不写了(不同版本的VS可能不同,我猜)

(9)终于可以使用DirectSound方法播放wav文件了(应该),在此贴一个播放代码:

boolean CMFC2Dlg::playMusic(LPWSTR url, HWND nhwnd)
{
    
    LPDIRECTSOUNDBUFFER8 g_pDSBuffer8 = NULL; //buffer
    LPDIRECTSOUND8 g_pDsd = NULL; //dsound
    CWaveFile *g_pWaveFile = NULL;
    //下面初始化DirectSound工作。
    HRESULT hr;
    if (FAILED(hr = DirectSoundCreate8(NULL, &g_pDsd, NULL)))
        return FALSE;
    //设置设备的协作度
    if (FAILED(hr = g_pDsd->SetCooperativeLevel(nhwnd, DSSCL_PRIORITY)))
        return FALSE;
    
    g_pWaveFile = new CWaveFile;
    g_pWaveFile->Open((url), NULL, WAVEFILE_READ);
    DSBUFFERDESC dsbd;
    ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));
    dsbd.dwSize = sizeof(DSBUFFERDESC);
    dsbd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLFX | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2;
    dsbd.dwBufferBytes = g_pWaveFile->GetSize();//MAX_AUDIO_BUF * BUFFERNOTIFYSIZE ; 
    dsbd.lpwfxFormat = g_pWaveFile->m_pwfx;
    LPDIRECTSOUNDBUFFER lpbuffer;
    //创建辅助缓冲区对象
    if (FAILED(hr = g_pDsd->CreateSoundBuffer(&dsbd, &lpbuffer, NULL)))
        return false;
    if (FAILED(hr = lpbuffer->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*)&g_pDSBuffer8)))
        return false;
    lpbuffer->Release();
    //准备工作做完了,下面就开始播放了
    LPVOID lplockbuf;
    DWORD len;
    DWORD dwWrite;

    g_pDSBuffer8->Lock(0, 0, &lplockbuf, &len, NULL, NULL, DSBLOCK_ENTIREBUFFER);
    g_pWaveFile->Read((BYTE*)lplockbuf, len, &dwWrite);
    g_pDSBuffer8->Unlock(lplockbuf, len, NULL, 0);
    g_pDSBuffer8->SetCurrentPosition(0);
    g_pDSBuffer8->Play(0, 0, NULL);

    return true;
}

//其中最后的Play方法可以传参数循环播放(NULL那里),url是wav地址,我用的绝对路径;nhwnd是HWND对象,在普通方法中直接传入m_hWnd即可(不用声明,直接就有)

//DirectSound播放wav的方法比PlaySound等好太多了,优点上方有写

(10)如果运行时发现DXGI_STATUS_OCCLUDED错误,在项目属性-->VC++目录-->包含目录:将 $(WindowsSDK_IncludePath) 放在 $(DXSDK_DIR)Include 前面(网上这么说)

不过现在,我的包含目录是这样的:$(DXSDK_DIR) Include;$(IncludePath)

并没有这个$(WindowsSDK_IncludePath);

这个错误我之前遇到过,重新搭建项目就没有发现了。

 

四、总结

本文主要讲述了在Visual Studio 2017环境下基于C++中使用DirectSound播放wav文件的方法,由于是事后总结的,可能会有遗漏的地方,如果大家按照以上方法还是不能使用DirectSound,还请指出来,作者会查明原因并补充环境搭建流程,谢谢!

本文还免费分享了作者自制的C++版TXT音乐播放器.exe,按照指定格式写好txt简谱后就可以播放,便于扒谱获得简谱后测试是否正确。如有bug,还请指出,作者会继续完善,谢谢!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/BHSZZY/article/details/106288213

智能推荐

Python 入门的60个基础练习_练习python基础语法-程序员宅基地

文章浏览阅读4.2w次,点赞329次,收藏2.7k次。Python 入门的60个基础练习_练习python基础语法

iOS6和iOS7代码的适配(2)——status bar_ios7 statusbar-程序员宅基地

文章浏览阅读1w次。用Xcode5运行一下应用,第一个看到的就是status bar的变化。在iOS6中,status bar是系统在处理,应用_ios7 statusbar

gdb调试时No symbol "var" defined in current context && No Register_no registers调试显示-程序员宅基地

文章浏览阅读2.1k次。问题描述:,在gdb调试程序输出变量:p var,会提示No symbol "var" in current context.原因:程序编译时开启了优化选项,那么在用GDB调试被优化过的程序时,可能会发生某些变量不能访问,或是取值错误码的情况。这个是很正常的,因为优化程序会删改程序,整理程序的语句顺序,剔除一些无意义的变量等,所以在GDB调试这种程序时,运行时的指令和你所编写指_no registers调试显示

IDGeneratorUtil 主键id生成工具类_idgeneratorutils.generateid()-程序员宅基地

文章浏览阅读3.4k次。import java.util.Random;import org.drools.util.UUIDGenerator;/** * * * 类名称:GenerateIdUtil * 类描述: 主键生成工具类 * @author chenly * 创建时间:Jul 10, 2012 8:10:43 AM * 修改人: * 修改时间:Jul 10, 2012 8..._idgeneratorutils.generateid()

关于汇编 BX 和 BLX 跳转指令_汇编blx-程序员宅基地

文章浏览阅读5k次。BX:跳转到寄存器reg给出的目的地址处,如:BX R2BLX:跳转到寄存区reg给出的目的地址处并将返回地址存储到LR(R14)使用这两个指令时有一点特别需要注意:跳转的目的地址必须是奇数,若不是奇数则在后面加1,如某函数的起始地址是0x80000f00,则要跳转到此函数则应该跳转到0x80000f01处!否则会进入硬件错误中断!..._汇编blx

前端vue,打包整合进后端springboot的resources里面后,运行只要刷新就报404_前端项目放入resource-程序员宅基地

文章浏览阅读2.6k次,点赞2次,收藏4次。vue打包后,其实就剩index.html和一堆静态资源,页面的加载和替换都是通过刷新index.html种的dom来实现的(应该是这样,可能表述不是很好),所以做个重定向就可以了。(博主是这么解决的,网上还有很多人是各种路径错误,大家可以尝试下自己是哪个原因)import org.springframework.boot.web.server.ConfigurableWebServerFa..._前端项目放入resource

随便推点

添加远程github仓库时报错 Warning: Permanently added the RSA host key for IP address 52.74.223.119_cmd warning: permanently added-程序员宅基地

文章浏览阅读9.7k次。1.问题展示2.解决方案1.任意窗口, 打开git bash2.命令行界面, 输入cd C:3.cat ~/.ssh/id_rsa.pub正常下面应该显示一大串公钥如果没有,显示如下图, 则进行下一步, 创建公钥4.创建公钥, 输入 ssh-keygen5.然后一直下一步, 直到出现6.再次输入cat ~/.ssh/id_rsa.pub下面一大串数字便是公钥,复制这些字符串, 打开github, 点击头像, 打开settings, 打开SSH and GPG Keys_cmd warning: permanently added

SQL*Plus 使用技巧1-程序员宅基地

文章浏览阅读154次。[code="java"]1. SQL/Plus 常用命令 a. help [topic] 查看命令的使用方法,topic表示需要查看的命令名称。 如: help desc; b. host 该命令可以从SQL*Plus环境切换到操作系统环境,以便执行操作系统命名。 c. host [command] 在sql*plus环境中执行操作系统命令,如:host notepad.exe..._sql+plus的使用方法

域控服务器搭建与管理论文,校园网络服务器的配置与管理 毕业论文.doc-程序员宅基地

文章浏览阅读441次。该文档均来自互联网,如果侵犯了您的个人权益,请联系我们将立即删除!**学校毕 业 论 文**学校园网络服务器的配置与管理姓 名: **学 号: **指导老师:系 名:专 业: 计算机网络技术班 级:二0一一年十二月十五日摘 要随着网络技术的不断发展和Internet的日益普及,许多学校都建立了校园网络并投入使用,这无疑对加快信息处理,提高工作效..._服务器配置与应用论文

mysql单实例多库与多实例单库_数据库单实例和多实例-程序员宅基地

文章浏览阅读1k次。一、单实例多库:一个mysql实例,创建多个数据目录。规划:实例路径:/usr/local/mysql数据目录路径:(1)/usr/local/mysql/data(2)/usr/local/mysql/data2步骤:安装mysql。配置my.cnf文件。初始化各个数据库。用mysqld_multi启动。1、安装mysql。平常安装。2、m..._数据库单实例和多实例

MFC解决找不到MFC90.DLL的问题_microsoft v90.debugmfc-程序员宅基地

文章浏览阅读6.3k次。今天装了第三方的MFC软件库Xtreme ToolkitPro v15.0.1,听说搞MFC的人都知道它的强大,我刚学习,所以装了一个,然后想运行一下它自带的例子看看。出现一个“找不到mfc90.dll“的问题,百度一下,记录如下:vs2008已经打过sp1补丁,编译C++程序会提示找不到mfc90.dll文件的错误,但是如果是release版的话就能正常运行csdn看到解决方案,粘贴_microsoft v90.debugmfc

XeLaTeX-中文排版解决方案_latex 中文排版 texlive-程序员宅基地

文章浏览阅读2.1k次。以前使用CJK进行中文的排版,需要自己生成字体库,近日,出现了XeTeX,可以比较好的解决中文字体问题,不需要额外生成LaTeX字体库,直接使用计算机系统里的字体,本文以在Linux下为例说明XeTeX的使用。操作系统: UbuntuTeX:除了texlive包外,还需要安装的包是texlive-xetex。字体:可以使用fc-list查看你自己的字体库,注意字体的完整名称,在XeTe..._latex 中文排版 texlive