如何在 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;
}
我是 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;
}