在C++控制台应用程序中播放声音

PlaySound in C++ Console application?

本文关键字:播放声音 应用程序 控制台 C++      更新时间:2023-10-16

已编辑,因此代码正确(感谢ta.speot.IS)-底部的新问题

所以我一直在玩控制台,因为我处于那个水平,我们被要求制作我们的第一个";项目";进行评估。我已经完成了基本的申请。。但我想把它放大一点,并添加一些声音。将从控制台播放的声音。

这个测试工作(有点),因为它会播放一个声音文件,但有2个问题。。。

  1. 当声音开始播放时,应用程序将冻结,直到播放完毕。

  2. 当我试图编译为";释放";其错误为"0";链接错误"-致命错误LNK1120:1个未解析的外部。

#include <iostream>
#include <windows.h>
#include <mmsystem.h>
using namespace std;
int main(){
    //PlaySound(TEXT("mywavsound.wav"), NULL, SND_FILENAME); - My erroring code
    PlaySound(TEXT("mywavsound.wav"), NULL, SND_FILENAME | SND_ASYNC);// - the correct code
        
    int test = 0;
    cin>>test;
    return 0;
}

所以我的问题是。。。

  1. 如何在不冻结控制台的情况下播放声音,这样我就可以示例在项目期间播放循环音乐文件正在运行?如果我能在上面播放其他声音那就太好了例如,当你按下回车键时,它会播放一个声音而不停止音乐

  2. 如何添加wav文件,使其作为发布版进行编译

编辑

我知道SND_ASYNC的东西,但我不知道如何使用它,如果没有不编译的东西,我似乎无法使用它。。有人有使用SND_ASYNC的代码的例子吗?

编辑2

所以我现在有这个工作。。。。使用

PlaySound(TEXT("mysound.wav"), NULL, SND_FILENAME | SND_ASYNC);

现在我想知道如何一次播放一个或多个声音,因为如果我用那个标志调用PlaySound()两次,它会停止第一个,播放第二个<有办法同时播放两个声音吗>

如何在不冻结控制台的情况下播放声音?

如果你在谷歌上搜索PlaySound,这是第一个结果:

fdwSound

SND_ASYNC声音异步播放,PlaySound在声音开始后立即返回。要终止异步播放的波形声音,请调用PlaySoundpszSound设置为NULL

你应该熟悉搜索引擎及其功能。

如何添加wav文件,使其作为发布版进行编译?

您需要在Release和Debug配置中链接winmm.lib。或者,添加

#pragma comment(lib, "winmm.lib")

到源代码的顶部。

使用mciSendstring(),您可以同时播放多种声音:)

示例:

mciSendString("play wave1.wav", NULL, 0, NULL);
mciSendString("play wave2.wav", NULL, 0, NULL);

您的大部分问题都得到了回答。然而,为了同时播放更多的声音。为此,您可以使用SDL_Mixer来执行此操作。到目前为止,我一直在寻找任何播放音频的好方法,并使其发挥作用,我找到了这个。

好的一点是,SDL_Mixer是多平台的(在win和linux上都进行了测试)。如果你知道自己在做什么,你就可以编写在两个操作系统上编译的代码。

另一个好处是,你可以同时播放多个声音(可能最多30个,但请在线查看)。该库可用于Windows(DLL和头文件)和Linux(通过控制台安装)。关于安装,我不再赘述。它支持wav和oog以及更多的文件类型。

然而,缺点如下:工作后,我花了很多时间,我发现音频有裂纹,质量很低,尽管原始文件在媒体播放器中播放得很好。经过研究,我发现这是图书馆里长期存在的一个bug,至今仍未修复。有些人说他们把音量调高到100,但对我来说不起作用。第二个缺点是它需要比键入更多的编码

 PlaySound(TEXT("mywavsound.wav"), NULL, SND_FILENAME | SND_ASYNC);// - the correct code

下一个缺点是我想出来的,一个wav文件没有加载,说格式不正确。尽管我使用了3个转换器,并通过互联网下载了许多文件,但它并不起作用。然而,在下载测试项目并在档案中使用他们的.wav文件时,它确实起了作用。然而,我通过将我所有的音乐转换为.oog 来解决这个问题

像这样,我仍然不知道什么样的音乐库会是最好的使用。取决于你的控制台应该是什么。。。?你是在写游戏还是只是媒体播放器?还是只是测试。。。?

之前的所有答案都很好地解释了这一点。我只想举一个如何做到这一点的基本例子:

#include<iostream>
#include<windows.h>
int main(){
    std::string name;
    std::string a1 = "happy.wav";
    std::string a2 = "apple.wav";
    PlaySound((a1.c_str()),NULL,SND_SYNC);
    PlaySound((a2.c_str()),NULL,SND_SYNC);
    return 0;
}

上面给出的是一个代码片段,我正在使用PlaySound()播放两个wav声音文件。

注意。目前,这两个wav文件位于cpp代码文件所在的同一目录中。

有办法同时播放两个声音吗?

您需要使用比PlaySound()更强大的多媒体API。例如,您可以使用XAudio2代替:

XAudio2是一个低级音频API。它为游戏提供了一个类似于其前身DirectSound和XAudio的信号处理和混音基础。

以下是XAudio2功能和新功能的列表,这些功能使开发人员能够提高游戏性能。

  • 提交

    提交将多个声音组合成一个音频流——例如,由复合部件组成的发动机声音,所有这些部件都在同时播放。此外,您可以使用子混合来处理和组合游戏的类似部分。例如,您可以组合所有游戏音效,以允许应用用户音量设置,而单独的设置控制音乐音量。与DSP相结合,submixing提供了当今游戏所需的数据路由和处理类型。XAudio2允许任意级别的子混音,可以创建复杂的声音和游戏混音。

  • 无阻塞API型号

    除了少数例外,XAudio2方法调用不会阻止音频处理引擎这意味着客户端可以在任何时候安全地进行一组方法调用,而不会阻塞导致延迟的长时间调用。例外情况是IXAudio2Voice::DestroyVoice方法(它可能会阻塞引擎,直到被破坏的语音完成处理)和终止音频线程的方法:IXAudio2::StopEngineIXAudio2::Release。请注意,虽然XAudio2方法调用不会阻止音频处理引擎,但XAudi02方法包含关键部分,在某些情况下可能会被阻止。

以下解决方案仅适用于wav文件

在大学的第二学期,我用C++制作了一个控制台(ASCII)游戏作为学期项目。在制作该游戏时,它需要播放多个音频文件。谷歌搜索后,我写了以下代码:

音频.h

#pragma once
#include <xaudio2.h>
#include <iostream>
#include <string>
using namespace std;
#ifdef _XBOX 
#define fourccRIFF 'RIFF'
#define fourccDATA 'data'
#define fourccFMT 'fmt '
#define fourccWAVE 'WAVE'
#define fourccXWMA 'XWMA'
#define fourccDPDS 'dpds'
#endif
#ifndef _XBOX
#define fourccRIFF 'FFIR'
#define fourccDATA 'atad'
#define fourccFMT ' tmf'
#define fourccWAVE 'EVAW'
#define fourccXWMA 'AMWX'
#define fourccDPDS 'sdpd'
#endif
class Audio
{
private:
    HRESULT hr;
    IXAudio2* pXAudio2;
    IXAudio2MasteringVoice* pMasterVoice;
    HRESULT FindChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition);
    HRESULT ReadChunkData(HANDLE hFile, void* buffer, DWORD buffersize, DWORD bufferoffset);
public:
    Audio();
    int Play(string path, float volume = 1, bool ShouldLoop = false); // plays the audio file with specified volume and can be looped
    string BasePath; // Directory where all audio files (relevant to project) are stored i.e if all audio files are stored in "D:game" than set BasePath to "D:game", this will be automatically added in path of every audio file
};

音频.cpp

#include "Audio.h"
Audio::Audio()
{
    BasePath = "";
    hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (FAILED(hr))
        cout << hr;
    pXAudio2 = nullptr;
    if (FAILED(hr = XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR)))
        cout << hr;
    pMasterVoice = nullptr;
    if (FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasterVoice)))
        cout << hr;
}
int Audio::Play(string path, float volume, bool ShouldLoop) {
    path = BasePath + "\" + path;
    WAVEFORMATEXTENSIBLE wfx = { 0 };
    XAUDIO2_BUFFER buffer = { 0 };
#ifdef _XBOX
    char* strFileName = path;
#else
    TCHAR* strFileName = new TCHAR[path.size() + 1];
    strFileName[path.size()] = 0;
    std::copy(path.begin(), path.end(), strFileName);
#endif
    // Open the file
    HANDLE hFile = CreateFile(
        strFileName,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);
    if (INVALID_HANDLE_VALUE == hFile)
        return HRESULT_FROM_WIN32(GetLastError());
    if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
        return HRESULT_FROM_WIN32(GetLastError());
    DWORD dwChunkSize;
    DWORD dwChunkPosition;
    //check the file type, should be fourccWAVE or 'XWMA'
    FindChunk(hFile, fourccRIFF, dwChunkSize, dwChunkPosition);
    DWORD filetype;
    ReadChunkData(hFile, &filetype, sizeof(DWORD), dwChunkPosition);
    if (filetype != fourccWAVE)
        return S_FALSE;
    FindChunk(hFile, fourccFMT, dwChunkSize, dwChunkPosition);
    ReadChunkData(hFile, &wfx, dwChunkSize, dwChunkPosition);
    FindChunk(hFile, fourccDATA, dwChunkSize, dwChunkPosition);
    BYTE* pDataBuffer = new BYTE[dwChunkSize];
    ReadChunkData(hFile, pDataBuffer, dwChunkSize, dwChunkPosition);
    buffer.AudioBytes = dwChunkSize;  // size of the audio buffer in bytes
    buffer.pAudioData = pDataBuffer;  // buffer containing audio data
    buffer.Flags = XAUDIO2_END_OF_STREAM; // tell the source voice not to expect any data after this buffer
    if (ShouldLoop) buffer.LoopCount = XAUDIO2_LOOP_INFINITE;
    IXAudio2SourceVoice* pSourceVoice;
    if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&wfx))) cout << hr;
    //if (FAILED(hr = pSourceVoice->SetSourceSampleRate(20000))) cout << hr;
    if (FAILED(hr = pSourceVoice->SubmitSourceBuffer(&buffer))) cout << hr;
    //if (FAILED(hr = pSourceVoice->SetFrequencyRatio(1.2))) cout << hr;
    pSourceVoice->SetVolume(volume);
    if (FAILED(hr = pSourceVoice->Start(0)))
        cout << hr;
    return 0;
}

HRESULT Audio::FindChunk(HANDLE hFile, DWORD fourcc, DWORD& dwChunkSize, DWORD& dwChunkDataPosition)
{
    HRESULT hr = S_OK;
    if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
        return HRESULT_FROM_WIN32(GetLastError());
    DWORD dwChunkType;
    DWORD dwChunkDataSize;
    DWORD dwRIFFDataSize = 0;
    DWORD dwFileType;
    DWORD bytesRead = 0;
    DWORD dwOffset = 0;
    while (hr == S_OK)
    {
        DWORD dwRead;
        if (0 == ReadFile(hFile, &dwChunkType, sizeof(DWORD), &dwRead, NULL))
            hr = HRESULT_FROM_WIN32(GetLastError());
        if (0 == ReadFile(hFile, &dwChunkDataSize, sizeof(DWORD), &dwRead, NULL))
            hr = HRESULT_FROM_WIN32(GetLastError());
        switch (dwChunkType)
        {
        case fourccRIFF:
            dwRIFFDataSize = dwChunkDataSize;
            dwChunkDataSize = 4;
            if (0 == ReadFile(hFile, &dwFileType, sizeof(DWORD), &dwRead, NULL))
                hr = HRESULT_FROM_WIN32(GetLastError());
            break;
        default:
            if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, dwChunkDataSize, NULL, FILE_CURRENT))
                return HRESULT_FROM_WIN32(GetLastError());
        }
        dwOffset += sizeof(DWORD) * 2;
        if (dwChunkType == fourcc)
        {
            dwChunkSize = dwChunkDataSize;
            dwChunkDataPosition = dwOffset;
            return S_OK;
        }
        dwOffset += dwChunkDataSize;
        if (bytesRead >= dwRIFFDataSize) return S_FALSE;
    }
    return S_OK;
}
HRESULT Audio::ReadChunkData(HANDLE hFile, void* buffer, DWORD buffersize, DWORD bufferoffset)
{
    HRESULT hr = S_OK;
    if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, bufferoffset, NULL, FILE_BEGIN))
        return HRESULT_FROM_WIN32(GetLastError());
    DWORD dwRead;
    if (0 == ReadFile(hFile, buffer, buffersize, &dwRead, NULL))
        hr = HRESULT_FROM_WIN32(GetLastError());
    return hr;
}

在主文件中,像这样使用

#include "Audio.h"
string getExePath(string x) // removes fileName from path i.e "D:pathtoexesample.exe" changes it into "D:pathtoexe"
{
    std::string f = x;
    return f.substr(0, f.find_last_of("\/"));
}
int main(int argc, char* argv[]) {
    string exeLocation = argv[0]; // returns path of currently running program i.e "D:pathtoexesample.exe"
    Audio audioManager;
    audioManager.BasePath = getExePath(exeLocation);
    audioManager.Play("bg.wav");
    while(1){}
    return 0;
}

注意

我不知道使用此代码的缺点,因为我很久以前就写过了,而且大多数代码都来自XAudio2的文档或其他一些源