使用C ++在运行时生成声音

时间:2012-07-26 21:15:22

标签: c++ runtime audio

所以我经过长时间的中断后才开始使用C ++,我想创建一个可以在运行时基于数字串生成音乐的程序(受到某些人的composition of Pi的启发)最终的目标是某种程序性的音乐生成软件。

到目前为止,我已经能够使用Beep()函数制作一个非常原始的版本,只是将Pi的第一个数字作为测试。像魅力一样。

我现在正在寻找的是如何将它提升到一个档次并获得更高质量的声音(因为Beep()字面上是最原始的声音......永远)我意识到我绝对没有想法如何做到这一点。我需要的是一个库或某种可以的API:

1)在没有预先存在的文件的情况下生成声音。我希望结果100%由代码生成,而不是最佳地依赖于任何样本。

2)如果我能够获得能够一次播放多种声音的东西,比如能够演奏和弦或带有节拍的旋律,那就太好了。

3)并且如果我可以以任何方式通过等式或其他类型的数据来控制它所播放的波(有点像chiptune混音器),这将是非常有用的。

我不知道这是否是一个奇怪的请求,或者我只是用错误的术语对它进行了研究,但我只是无法找到这些行中的任何内容,或者至少没有任何记录在案的内容。 :/

如果有人可以提供帮助,我真的很感激。

编辑:另外,显然我只是超级不习惯在论坛上问东西,我的目标平台是Windows(7,具体来说,虽然我认为不重要)。

2 个答案:

答案 0 :(得分:2)

我使用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.
}

答案 1 :(得分:0)

本周早些时候,我回答了一个非常类似的问题:Note Synthesis, Harmonics (Violin, Piano, Guitar, Bass), Frequencies, MIDI。在您的情况下,如果您不想依赖样本,那么波表方法就不存在了。所以你最简单的选择是随着时间的推移动态地改变正弦曲线的频率和幅度,这很容易但听起来很糟糕(就像便宜的Theremin)。您唯一真正的选择是更复杂的综合算法,例如物理建模算法之一(例如Karplus-Strong)。这将是一个有趣的项目,但要注意它确实需要一些数学背景。

你确实可以像Rafael提到的那样使用像Portaudio这样的东西来实际从PC中获取声音,事实上我觉得Portaudio是最好的选择。但是生成数据使其听起来是音乐的,这是迄今为止最大的挑战。