使用 C++ 在运行时生成声音
Posted
技术标签:
【中文标题】使用 C++ 在运行时生成声音【英文标题】:Generating Sounds at Runtime with C++ 【发布时间】:2012-07-26 21:15:21 【问题描述】:所以我在长时间的中断后开始使用 C++,我想创建一个程序,它可以在运行时根据数字字符串生成音乐(受到某些人所做的 composition of Pi 的启发),最终目标是某种程序音乐生成软件。
到目前为止,我已经能够使用 Beep() 函数制作一个非常原始的版本,并且只是通过输入 Pi 的第一个某某数字作为测试。像魅力一样工作。
我现在正在寻找的是如何将它提升一个档次并获得更高质量的声音(因为 Beep() 确实是最原始的声音......),我意识到我绝对没有想法如何做到这一点。我需要的是一个库或某种 API,它们可以:
1) 在没有预先存在的文件的情况下生成声音。我希望结果是 100% 由代码生成,而不是依赖任何样本,最好。
2) 如果我能开发出能够同时播放多种声音的东西,比如能够演奏和弦或带有节拍的旋律,那就太好了。
3) 如果我可以通过方程式或其他类型的数据以任何方式控制它播放的波(有点像 Chiptune 混音器可以),那将非常有帮助。
我不知道这是否是一个奇怪的请求,或者我只是使用错误的术语对其进行了研究,但我无法找到任何符合这些原则的内容,或者至少没有任何有据可查的内容。 :/
如果有人能提供帮助,我将不胜感激。
编辑:另外,显然我只是超级不习惯在论坛上提问,我的目标平台是 Windows(具体来说是 7,尽管我认为这并不重要)。
【问题讨论】:
所以,我假设是 windows,因为你提到了beep()
,但你真的应该告诉我们你的目标是什么平台。 C++ 没有音频的概念,你使用的任何东西都将依赖于平台(除非你找到一个库可以从你那里抽象出来,但这可能不是必需的)。
它将在 Windows 上,感谢您指出我会编辑它。
@AniMerrill,我知道没有简单的方法(在 Windows API 中)让您的程序生成要播放的声音数据。做这件事的图书馆可能是你最好的选择。
FMOD Ex 应该符合您的要求(自定义 DSP,playback from back from mem buffer)。
您可以使用相当简单的 Windows 的 waveOut API。或者您可以选择更完善的库,例如 BASS。
【参考方案1】:
我使用 portaudio (http://www.portaudio.com/)。它将让您以便携的方式创建 PCM 流。然后你只需将样本推送到流中,它们就会播放。
@edit:使用 PortAudio 非常简单。您初始化库。我使用浮点样本让它变得超级简单。我是这样做的:
PaError err = Pa_Initialize();
if ( err != paNoError )
return false;
mPaParams.device = Pa_GetDefaultOutputDevice();
if ( mPaParams.device == paNoDevice )
return false;
mPaParams.channelCount = NUM_CHANNELS;
mPaParams.sampleFormat = paFloat32;
mPaParams.suggestedLatency =
Pa_GetDeviceInfo( mPaParams.device )->defaultLowOutputLatency;
mPaParams.hostApiSpecificStreamInfo = NULL;
然后当你想播放声音时,你创建一个流,2 个立体声通道,44khz,适合 mp3 音频:
PaError err = Pa_OpenStream( &mPaStream,
NULL, // no input
&mPaParams,
44100, // params
NUM_FRAMES, // frames per buffer
0,
sndCallback,
this
);
然后你实现回调来填充 PCM 音频流。回调是一个 c 函数,但我只是调用我的 C++ 类来处理音频。我从我的代码中删除了这个,现在它可能不是 100% 正确,因为我删除了很多你不会关心的东西。但它的工作原理是这样的:
static int sndCallback( const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData )
Snd* snd = (Snd*)userData;
return snd->callback( (float*)outputBuffer, framesPerBuffer );
u32 Snd::callback( float* outbuf, u32 nFrames )
mPlayMutex.lock(); // use mutexes because this is asyc code!
// clear the output buffer
memset( outbuf, 0, nFrames * NUM_CHANNELS * sizeof( float ));
// mix all the sounds.
if ( mChannels.size() )
// I have multiple audio sources I'm mixing. That's what mChannels is.
for ( s32 i = mChannels.size(); i > 0; i-- )
for ( u32 j = 0; j < frameCount * NUM_CHANNELS; j++ )
float f = outbuf[j] + getNextSample( i ) // <------------------- your code here!!!
if ( f > 1.0 ) f = 1.0; // clamp it so you don't get clipping.
if ( f < -1.0 ) f = -1.0;
outbuf[j] = f;
mPlayMutex.unlock_p();
return 1; // when you are done playing audio return zero.
【讨论】:
我已经看到 PortAudio 出现在与此相关的几个不同主题中,所以我想我可以试一试。下载了最新版本,真正让我难过的一件事是所有代码都是 C 语言而不是 C++。我知道可以做到,但我没有跨两种语言的经验。你对此有什么建议吗?我目前正在使用带有 MinGW GCC 编译器的 Code::Blocks IDE,如果有帮助的话。 我从我的一个应用程序中提取了关键部分。 感谢您提供的所有帮助,我只是想知道您是否可以再帮我做一件事。出于某种原因,我一生都无法弄清楚如何将 PortAudio 与我的 IDE 完全链接。我已经编译了它,尝试在网上搜索我可能遗漏的内容,但无论如何我似乎总是会错过重要函数的定义。就像我可以得到的那样......初始化并因某种原因终止工作,但它不识别其他任何东西。 忽略最后的评论...想通了 XD 感谢您的帮助。【参考方案2】:本周早些时候我回答了一个非常相似的问题:Note Synthesis, Harmonics (Violin, Piano, Guitar, Bass), Frequencies, MIDI。在您的情况下,如果您不想依赖样本,那么波表方法就不适用了。因此,您最简单的选择是随时间动态改变正弦曲线的频率和幅度,这很容易,但听起来很糟糕(就像便宜的 Theremin)。您唯一真正的选择是更复杂的综合算法,例如物理建模算法之一(例如 Karplus-Strong)。那将是一个有趣的项目,但请注意,它确实需要一些数学背景。
您确实可以使用 Rafael 提到的 Portaudio 之类的东西来物理地从 PC 中获取声音,事实上我认为 Portaudio 是最好的选择。但生成数据以使其听起来悦耳是迄今为止您面临的最大挑战。
【讨论】:
现在拥有一些听起来像真正的乐器,或者比便宜的特雷敏更好的东西,是我最不担心的,因为 - 至少在这个东西变得更复杂之前 - 我有点喜欢这个想法拥有听起来像俗气的雅达利音乐的歌曲。我想要的是基本的构图,并希望某种可移植性最终允许我使用更复杂的东西,甚至放弃和使用样本。我觉得 Portaudio 听起来不错,我只需要现在就让它工作。 好的。请记住,尽管 Portaudio 实际上只是从声卡中获取原始音频信号的一种方式 - 它不会帮助您生成任何频率。您必须手动完成,或者找到实现这些低级功能的信号处理库。 我也很好。所有这个项目更多的是关于实验,而不是生产任何具有商业价值的东西或类似的东西。只是最近引起了我的兴趣。以上是关于使用 C++ 在运行时生成声音的主要内容,如果未能解决你的问题,请参考以下文章
iOS UILocalNotification 仅在应用未运行时发出声音(无警报)