4

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

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

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

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

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

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

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

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

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

4

2 回答 2

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 音频流。回调是 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.
}
于 2012-07-26T21:24:13.530 回答
0

本周早些时候我回答了一个非常相似的问题:音符合成、谐波(小提琴、钢琴、吉他、贝斯)、频率、MIDI。在您的情况下,如果您不想依赖样本,那么波表方法就不适用了。因此,您最简单的选择是随时间动态改变正弦曲线的频率和幅度,这很简单,但听起来很糟糕(就像便宜的 Theremin)。您唯一真正的选择是更复杂的综合算法,例如物理建模算法之一(例如 Karplus-Strong)。那将是一个有趣的项目,但要注意它确实需要一些数学背景。

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

于 2012-07-26T22:48:34.610 回答