【问题标题】:How to play or open *.mp3 or *.wav sound file in c++ program? [closed]如何在 C++ 程序中播放或打开 *.mp3 或 *.wav 声音文件? [关闭]
【发布时间】:2014-04-10 18:38:05
【问题描述】:

我是计算机科学专业的学生。我有一个最后一个学期的项目,要开发一个带有声音的图形短游戏。

【问题讨论】:

  • 要获得有价值且有用的回复,而不是对搜索网络发表评论,您需要指出您尝试过的内容。也就是说,显示一些尝试加载 .mp3 文件的示例代码。您是否绑定使用任何现有库(但失败)。这些更有帮助。
  • 附注你可以看看这些库是如何做到的:en.cppreference.com/w/cpp/links/libs
  • 这个问题已经有了答案。你可以搜索一下,最流行的似乎是FMOD和SDL,这两个都在很多游戏中使用过
  • @Dudeson:谢谢。我相信this 页面是相同的。

标签: c++ audio visual-c++ graphics


【解决方案1】:

首先,编写如下代码:

#include <Mmsystem.h>
#include <mciapi.h>
//these two headers are already included in the <Windows.h> header
#pragma comment(lib, "Winmm.lib")

打开 *.mp3:

mciSendString("open \"*.mp3\" type mpegvideo alias mp3", NULL, 0, NULL);

播放 *.mp3:

mciSendString("play mp3", NULL, 0, NULL);

播放并等待 *.mp3 播放完毕:

mciSendString("play mp3 wait", NULL, 0, NULL);

重播(从头开始重新播放)*.mp3:

mciSendString("play mp3 from 0", NULL, 0, NULL);

重播并等待 *.mp3 播放完毕:

mciSendString("play mp3 from 0 wait", NULL, 0, NULL);

播放 *.mp3 并在每次循环结束时重播:

mciSendString("play mp3 repeat", NULL, 0, NULL);

如果你想在 *.mp3 播放完毕后做某事,那么你需要通过 WNDCLASSEX 结构 CreateWindowEx RegisterClassEx 并使用 GetMessage 处理它的消息, TranslateMessageDispatchMessage 函数在 while 循环和调用中:

mciSendString("play mp3 notify", NULL, 0, hwnd); //hwnd is an handle to the window returned from CreateWindowEx. If this doesn't work, then replace the hwnd with MAKELONG(hwnd, 0).

在窗口程序中,添加case MM_MCINOTIFY:,其中的代码将在mp3播放完毕后执行。

但是如果你编写了一个Console应用程序并且你不处理windows,那么你可以CreateThread在挂起状态下通过在dwCreationFlags 参数中指定CREATE_SUSPENDED 标志,并将返回值保存在static 变量中,并随心所欲地调用它。例如,我称之为 mp3。这个static变量的类型当然是HANDLE

这是该线程的lpStartAddressThreadProc

DWORD WINAPI MP3Proc(_In_ LPVOID lpParameter) //lpParameter can be a pointer to a structure that store data that you cannot access outside of this function. You can prepare this structure before `CreateThread` and give it's address in the `lpParameter`
{
    Data *data = (Data*)lpParameter; //If you call this structure Data, but you can call it whatever you want.
    while (true)
    {
        mciSendString("play mp3 from 0 wait", NULL, 0, NULL);
        //Do here what you want to do when the mp3 playback is over
        SuspendThread(GetCurrentThread()); //or the handle of this thread that you keep in a static variable instead
    }
}

你现在要做的就是每次想重播你的mp3时ResumeThread(mp3);,每次播放完都会发生一些事情。

您可以#define play_my_mp3 ResumeThread(mp3); 使您的代码更具可读性。

当然你可以删除while (true)SuspendThreadfrom 0代码,如果你只想播放你的mp3文件一次并做任何你想做的事想要什么时候结束。

如果您删除SuspendThread 调用,则声音将一遍又一遍地播放,并在结束时执行某些操作。这相当于:

mciSendString("play mp3 repeat notify", NULL, 0, hwnd); //or MAKELONG(hwnd, 0) instead

在窗口中。

要在中间暂停 *.mp3:

mciSendString("pause mp3", NULL, 0, NULL);

并恢复它:

mciSendString("resume mp3", NULL, 0, NULL);

中途停止:

mciSendString("stop mp3", NULL, 0, NULL);

请注意,您无法恢复已停止的声音,只能暂停,但您可以通过执行 play 命令重播。 播放完这个 *.mp3 后,别忘了:

mciSendString("close mp3", NULL, 0, NULL);

所有这些操作也适用于(使用)波形文件,但对于波形文件,您可以使用“waveaudio”而不是“mpegvideo”。 您也可以直接播放它们而无需打开它们:

PlaySound("*.wav", GetModuleHandle(NULL), SND_FILENAME);

如果您不想指定模块的句柄:

sndPlaySound("*.wav", SND_FILENAME);

如果您不想等到播放结束:

PlaySound("*.wav", GetModuleHandle(NULL), SND_FILENAME | SND_ASYNC);
//or
sndPlaySound("*.wav", SND_FILENAME | SND_ASYNC);

反复播放波形文件:

PlaySound("*.wav", GetModuleHandle(NULL), SND_FILENAME | SND_ASYNC | SND_LOOP);
//or
sndPlaySound("*.wav", SND_FILENAME | SND_ASYNC | SND_LOOP);

请注意,您必须同时指定 SND_ASYNCSND_LOOP 标志,因为您永远不会等到重复无数次的声音结束!

您还可以使用fread 函数fopen 波形文件并将其所有字节复制到缓冲区(一个巨大/巨大(非常大)的字节数组),然后:

PlaySound(buffer, GetModuleHandle(NULL), SND_MEMORY);
//or
PlaySound(buffer, GetModuleHandle(NULL), SND_MEMORY | SND_ASYNC);
//or
PlaySound(buffer, GetModuleHandle(NULL), SND_MEMORY | SND_ASYNC | SND_LOOP);
//or
sndPlaySound(buffer, SND_MEMORY);
//or
sndPlaySound(buffer, SND_MEMORY | SND_ASYNC);
//or
sndPlaySound(buffer, SND_MEMORY | SND_ASYNC | SND_LOOP);

可以使用OpenFileCreateFileCreateFile2 以及ReadFileReadFileEx 函数来代替fopenfread 函数。

希望这能完美回答您的问题。

【讨论】:

  • 哇,我不敢相信这个答案到目前为止还没有真正被注意到!对于像我这样希望在其 C++ 代码中轻松实现 .mp3 声音的人来说,这是一个很好且详细的资源!
  • *.mp3 必须替换为 mp3 文件的完整路径和文件名。
  • 头文件mciapi.h在我的VS2008系统上不存在;但是,所需的定义包含在MMSystem.h 中(又包含在Windows.h 中)。
  • 我相信这只是 Windows
  • 对不起,但是没有 mciSendString("... wait") 的 mciSendString("play ...") 什么都不做,程序一开始就结束,至少对我来说,我必须做更多的事情吗?
【解决方案2】:

http://sfml-dev.org/documentation/2.0/classsf_1_1Music.php

SFML 没有其他人建议的 mp3 支持。我一直在做的就是用 Audacity 把我所有的音乐都变成 ogg,然后把我所有的音效都保留为 wav。

加载和播放 wav 很简单(粗略的例子):

http://www.sfml-dev.org/tutorials/2.0/audio-sounds.php

#include <SFML/Audio.hpp>
...
sf::SoundBuffer buffer;
if (!buffer.loadFromFile("sound.wav")){
    return -1;
}
sf::Sound sound;
sound.setBuffer(buffer);
sound.play();

流式传输 ogg 音乐文件也很简单:

#include <SFML/Audio.hpp>
...
sf::Music music;
if (!music.openFromFile("music.ogg"))
    return -1; // error
music.play();

【讨论】:

  • 使用预编译的二进制文件为我工作
  • 可能是 Windows 版本。 Unix 需要 sudo aptitude install libcs​​fml-dev 然后查看 github.com/SFML/CSFML/tree/master/include/SFML/Audio 以获取说明。为什么答案被关闭?这是一个重要而明确的问题。试试 sfSoundBuffer* buffer = sfSoundBuffer_createFromFile("/mysounds/morse.wav"); sfSound*sound = sfSound_create(); sfSound_setBuffer(声音,缓冲区); sfSound_play(声音);
【解决方案3】:

如果你想播放 *.mp3 或 *.wav 文件,我认为最简单的方法是使用SFML

【讨论】:

  • 请记住,由于许可问题,SFML 不支持 MP3 格式。但是,您也可以使用鲜为人知但同样出色的 OGG 格式。
【解决方案4】:

在 VC++ 中尝试使用简单的 c++ 代码。

#include <windows.h>
#include <iostream>
#pragma comment(lib, "winmm.lib")

int main(int argc, char* argv[])
{
std::cout<<"Sound playing... enjoy....!!!";
PlaySound("C:\\temp\\sound_test.wav", NULL, SND_FILENAME); //SND_FILENAME or SND_LOOP
return 0;
}

【讨论】:

    【解决方案5】:

    我会使用 FMOD 为您的游戏执行此操作。它能够播放任何主要用于声音的文件,并且在 C++ 中实现起来非常简单。将 FMOD 和 Dir3ect X 一起使用会很强大,而且并不难。如果您熟悉 Singleton 类,我会在您的 win 主 cpp 中创建一个声音管理器的 Singleton 类,然后在加载或播放新音乐或声音效果时访问它。 这是一个音频管理器示例

        #pragma once
    
    #ifndef H_AUDIOMANAGER
    #define H_AUDIOMANAGER
    
    #include <string>
    #include <Windows.h>
    #include "fmod.h"
    #include "fmod.hpp"
    #include "fmod_codec.h"
    #include "fmod_dsp.h"
    #include "fmod_errors.h"
    #include "fmod_memoryinfo.h"
    #include "fmod_output.h"
    
    class AudioManager
    {
    public:
        // Destructor
        ~AudioManager(void);
    
        void Initialize(void);  // Initialize sound components
        void Shutdown(void);    // Shutdown sound components
    
        // Singleton instance manip methods
        static AudioManager* GetInstance(void);
        static void DestroyInstance(void);
    
        // Accessors
        FMOD::System* GetSystem(void)
            {return soundSystem;}
    
        // Sound playing
        void Play(FMOD::Sound* sound);  // Play a sound/music with default channel
        void PlaySFX(FMOD::Sound* sound);   // Play a sound effect with custom channel
        void PlayBGM(FMOD::Sound* sound);   // Play background music with custom channel
    
        // Volume adjustment methods
        void SetBGMVolume(float volume);
        void SetSFXVolume(float volume);
    
    private:
        static AudioManager* instance;  // Singleton instance
        AudioManager(void);  // Constructor
    
        FMOD::System* soundSystem;  // Sound system object
        FMOD_RESULT result;
        FMOD::Channel* bgmChannel;  // Channel for background music
        static const int numSfxChannels = 4;
        FMOD::Channel* sfxChannels[numSfxChannels]; // Channel for sound effects
    };
    
    #endif
    

    【讨论】:

    • 这与操作的问题并没有真正的关系,但你能解释一下为什么你有单独的关闭方法而不是依赖析构函数吗?
    • 因为拥有与声音系统一样多的组件,您可以在那里处理声音系统的所有对象,而不是在析构函数中,因为析构函数会破坏声音管理器,但不会破坏从说声音类,只是它自己的类。
    • 那么你可以把你放入shutdown()方法的代码放入析构函数中。这就是析构函数的真正目的,释放对象持有的所有资源。我经常看到这种两步破坏的设计,但永远无法理解其背后的原因。为什么不使用 C++ 提供的默认功能?除此之外,您始终可以使用智能指针,而不必担心显式释放资源。我只是看不到单独的 shutdown() 函数的好处。
    【解决方案6】:

    使用库 (a) 读取声音文件并 (b) 播放它们。 (我建议您在空闲时间自己尝试一下,但是...)

    也许(*nix):

    Windows:DirectX。

    【讨论】:

      猜你喜欢
      • 2014-10-26
      • 2016-07-19
      • 2011-03-30
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-11
      相关资源
      最近更新 更多