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

5 c++ audio runtime

因此,在长时间中断之后,我开始学习 C++,并且想到创建一个程序,该程序可以在运行时根据数字字符串生成音乐(受到某些人完成的Pi 组合的启发),最终目标是一些一种程序化音乐生成软件。

到目前为止,我已经能够使用 Beep() 函数制作一个非常原始的版本,并仅输入 Pi 的第一个某某数字作为测试。奇迹般有效。

我现在正在寻找的是如何将其提升一个档次并获得更高质量的声音(因为 Beep() 字面上是最原始的声音......有史以来),我意识到我完全不知道如何做这个。我需要的是一个库或某种 API,它们可以:

1) 无需预先存在的文件即可生成声音。我希望结果 100% 由代码生成,并且最好不依赖于任何样本。

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

3)如果我可以通过方程或其他类型的数据以任何方式控制它播放的波形(有点像chiptune混音器可以),那将非常有帮助。

我不知道这是一个奇怪的请求,还是我只是使用了错误的术语进行了研究,但我只是无法找到任何类似的内容,或者至少没有找到任何有据可查的内容。:/

如果有人可以提供帮助,我将非常感激。

编辑:另外,显然我只是超级不习惯在论坛上提问,我的目标平台是Windows(特别是7,尽管我认为这并不重要)。

Raf*_*sta 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;
Run Code Online (Sandbox Code Playgroud)

然后当你想播放声音时,你可以创建一个流,2 个立体声通道,44khz,适合 mp3 音频:

PaError err = Pa_OpenStream( &mPaStream,
                             NULL, // no input
                             &mPaParams,
                             44100, // params
                             NUM_FRAMES, // frames per buffer
                             0,
                             sndCallback,
                             this
                           );
Run Code Online (Sandbox Code Playgroud)

然后,您实现回调来填充 PCM 音频流。回调是 ac 函数,但我只是调用我的 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.
}
Run Code Online (Sandbox Code Playgroud)