如何在 C++ 中 create/play 音频?

How do I create/play audio in C++?

我是 C++ 的新手,但对 Python 有一些经验。我想制作一个根据不同条件播放特定频率的程序,但我很快意识到我什至不知道如何播放音频文件。根据我寻找解决方案的发现,在 Mac 上似乎几乎不可能,而且我尝试复制到 Xcode 中的所有内容都以一堆错误告终。我应该从哪里开始呢?

我用过SFML for this. Although I did it on Linux, I see that its available for mac as well. You can go through the tutorials here.

这里的问题是 C++ 只是一种编程语言。 python 也是如此,尽管 Python 生活在一个不同的模块和包管理生态系统中,这些生态系统被(正确或错误地)混淆为 语言的一部分

C++ 没有相同的历史和相同的生态系统,这是你在学习它时将要经历的战斗的一部分。你没有 pip,你有一系列模糊的框架,headers 和库(一些是标准的,一些需要安装),所有这些都需要链接,path-ed 或编译。如果您尝试像新手 Python 程序员一样接近它,那么它是一个不友好的生态系统。如果您以不可知论的方式来处理它,它既非常强大又异常乏味,这种组合往往会使开发人员两极分化。

这意味着像 Use SFML!、SDL、NSOUND、OpenAL、CoreAudio、AVFoundation、JUCE、&c... 这样的简单答案在技术上都是“正确的”,但在很大程度上是错误的在大部分设置、命名和工作流程中,与 python 仅相差 pip install

抛开武断,如果你想简单

  • 创建一个浮点值数组
  • 表示正弦波样本
  • 然后播放那些样本
  • 在 macOS 上

那么你可能是最好的

  • 创建数组
  • 写一个.wav
  • afplay
  • 打开 .wav

这是最开放、最通用、面向 DSP 的 play-from-RAM 解决方案吗?不,当然不是,但它是 a 解决您在此处提出的问题的方法。另一种正确的答案是每个主要媒体库的详尽列表,cross-platform 和 macOS 特定的,它们的设置,怪癖和最小工作示例,这将导致答案如此冗长我希望你能理解为什么它在 Stack Overflow 上没有得到最好的解决。

一个简单的 CLI 应用程序

你可以在 SO 上找到它的所有组成部分,但是我 tallied-off 有很多 我如何在 C++ 中播放声音 它让我产生了疑问意识到他们不会消失。

Xcode 的设置是创建命令行工具项目(Visual Studio 的控制台应用程序)。

这是一个 header,它将把所有内容包装到一个 playSound 函数中

audio.h

#pragma once

//------------------------------------------------------------------------------
#include <iostream>
#include <fstream>
#include <cstddef>
#include <cstdlib>
#if defined _WIN32 || defined _WIN64
#pragma comment(lib, "Winmm")
#include <windows.h>
#endif
//------------------------------------------------------------------------------

/// <#Description#>
struct WaveHeader
{
    /** waveFormatHeader: The first 4 bytes of a wav file should be the characters "RIFF" */
    char chunkID[4] = { 'R', 'I', 'F', 'F' };
    /** waveFormatHeader: This is the size of the entire file in bytes minus 8 bytes */
    uint32_t chunkSize;
    /** waveFormatHeader" The should be characters "WAVE" */
    char format[4] = { 'W', 'A', 'V', 'E' };
    /** waveFormatHeader" This should be the letters "fmt ", note the space character */
    char subChunk1ID[4] = { 'f', 'm', 't', ' ' };
    /** waveFormatHeader: For PCM == 16, since audioFormat == uint16_t */
    uint32_t subChunk1Size = 16;
    /** waveFormatHeader: For PCM this is 1, other values indicate compression */
    uint16_t audioFormat = 1;
    /** waveFormatHeader: Mono = 1, Stereo = 2, etc. */
    uint16_t numChannels = 1;
    /** waveFormatHeader: Sample Rate of file */
    uint32_t sampleRate = 44100;
    /** waveFormatHeader: SampleRate * NumChannels * BitsPerSample/8 */
    uint32_t byteRate = 44100 * 2;
    /** waveFormatHeader: The number of bytes for one sample including all channels */
    uint16_t blockAlign = 2;
    /** waveFormatHeader: 8 bits = 8, 16 bits = 16 */
    uint16_t bitsPerSample = 16;
    /** waveFormatHeader: Contains the letters "data" */
    char subChunk2ID[4] = { 'd', 'a', 't', 'a' };
    /** waveFormatHeader: == NumberOfFrames * NumChannels * BitsPerSample/8
     This is the number of bytes in the data.
     */
    uint32_t subChunk2Size;
    
    WaveHeader(uint32_t samplingFrequency = 44100, uint16_t bitDepth = 16, uint16_t numberOfChannels = 1)
    {
        numChannels = numberOfChannels;
        sampleRate = samplingFrequency;
        bitsPerSample = bitDepth;
        
        byteRate = sampleRate * numChannels * bitsPerSample / 8;
        blockAlign = numChannels * bitsPerSample / 8;
    };
    
    /// sets the fields that refer to how large the wave file is
    /// @warning This MUST be set before writing a file, or the file will be unplayable.
    /// @param numberOfFrames total number of audio frames. i.e. total number of samples / number of channels
    void setFileSize(uint32_t numberOfFrames)
    {
        subChunk2Size = numberOfFrames * numChannels * bitsPerSample / 8;
        chunkSize = 36 + subChunk2Size;
    }
    
};

/// write an array of float data to a 16-bit, 44100 Hz Mono wav file in the same directory as the program and then play it
/// @param audio audio samples, assumed to be 44100 Hz sampling rate
/// @param numberOfSamples total number of samples in audio
/// @param filename filename, should end in .wav and will be written to your Desktop
void playSound(float* audio,
               uint32_t numberOfSamples,
               const char* filename)
{
    std::ofstream fs;
    std::string filepath {filename};
    
    if (filepath.substr(filepath.size() - 4, 4) != std::string(".wav"))
        filepath += std::string(".wav");
    
    fs.open(filepath, std::fstream::out | std::ios::binary);
    
    WaveHeader* header = new WaveHeader{};
    header->setFileSize(numberOfSamples);
    
    fs.write((char*)header, sizeof(WaveHeader));
    
    int16_t* audioData = new int16_t[numberOfSamples];
    constexpr float max16BitValue = 32768.0f;
    
    for (int i = 0; i < numberOfSamples; ++i)
    {
        int pcm = int(audio[i] * (max16BitValue));
        
        if (pcm >= max16BitValue)
            pcm = max16BitValue - 1;
        else if (pcm < -max16BitValue)
            pcm = -max16BitValue;
        
        audioData[i] = int16_t(pcm);
    }
    
    
    fs.write((char*)audioData, header->subChunk2Size);
    
    fs.close();
    std::cout << filename << " written to:\n" << filepath << std::endl;
    
    
#if defined _WIN32 || defined _WIN64
    // don't forget to add Add 'Winmm.lib' in Properties > Linker > Input > Additional Dependencies
    PlaySound(std::wstring(filepath.begin(), filepath.end()).c_str(), NULL, SND_FILENAME);
#else
    std::system((std::string("afplay ") + filepath).c_str());
#endif
    
}

main.cpp

您的 main 函数可能类似于:

#include <iostream>
#include <cmath>
#include "audio.h"

int main(int argc, const char * argv[])
{
    const int numSamples = 44100;
    float sampleRate = 44100.0f;
    float* sineWave = new float[numSamples];
    float frequency = 440.0f;
    
    float radsPerSamp = 2.0f * 3.1415926536f * frequency / sampleRate;
    
    for (unsigned long i = 0; i < numSamples; i++)
    {
        sineWave[i] = std::sin (radsPerSamp * (float) i);
    }
    
    playSound(sineWave, numSamples, "test.wav");
        
    return 0;
}