2009-08-19 148 views
5

我有一个程序可以产生应该同时播放的音频信号。为此,我每隔100 ms播放一段100 ms的音频流。但是,在每个100毫秒音频流的开始和结束时(由于DC),我有不希望的信号,因此即使信号值相同,输出声音也不平滑。我的代码附在下面。请帮助我应该做些什么才能获得正确的实时音频。如何从实时流播放音频

using System; 
using System.Windows.Forms; 
using Microsoft.DirectX.DirectSound; 
using System.IO; 

namespace TestSound 
{ 
    class CSound : Form 
    { 
     const int HEADER_SIZE = 44; 
     const bool FLAG_STEREO = true; 
     const short BITS_PER_SAMPLE = 16; 
     const int SAMPLE_RATE = 44100; 

     int numberOfSamples; 
     MemoryStream stream; 
     BinaryWriter writer; 
     Device ApplicationDevice = null; 
     SecondaryBuffer buffer = null; 
     BufferDescription description; 

     public CSound() 
     { 
      try 
      { 
       ApplicationDevice = new Device(); 
      } 
      catch 
      { 
       MessageBox.Show("Unable to create sound device."); 
       ApplicationDevice = null; 
       return; 
      } 
      ApplicationDevice.SetCooperativeLevel(this, CooperativeLevel.Priority); 
      description = new BufferDescription(); 
      description.ControlEffects = false; 
      stream = new MemoryStream(); 
      writer = new BinaryWriter(stream); 
     } 

     private void AddHeader() 
     { 
      stream.Position = 0; 

      writer.Write(0x46464952); // "RIFF" in ASCII 
      writer.Write((int)(HEADER_SIZE + (numberOfSamples * BITS_PER_SAMPLE * (FLAG_STEREO ? 2 : 1)/8)) - 8); 
      writer.Write(0x45564157); // "WAVE" in ASCII 
      writer.Write(0x20746d66); // "fmt " in ASCII 
      writer.Write(16); 
      writer.Write((short)1); 
      writer.Write((short)(FLAG_STEREO ? 2 : 1)); 
      writer.Write(SAMPLE_RATE); 
      writer.Write(SAMPLE_RATE * (FLAG_STEREO ? 2 : 1) * BITS_PER_SAMPLE/8); 
      writer.Write((short)((FLAG_STEREO ? 2 : 1) * BITS_PER_SAMPLE/8)); 
      writer.Write(BITS_PER_SAMPLE); 
      writer.Write(0x61746164); // "data" in ASCII 
      writer.Write((int)(numberOfSamples * BITS_PER_SAMPLE * (FLAG_STEREO ? 2 : 1)/8)); 
     } 

     public void Play(short[] samples) 
     { 
      if (ApplicationDevice == null) 
       return; 

      stream.Position = HEADER_SIZE; 
      numberOfSamples = samples.Length; 
      for (int i = 0; i < numberOfSamples; i++) 
      { 
       writer.Write(samples[i]); 
       if (FLAG_STEREO) 
        writer.Write(samples[i]); 
      } 
      AddHeader(); 
      stream.Position = 0; 

      try 
      { 
       if (buffer != null) 
       { 
        buffer.Dispose(); 
        buffer = null; 
       } 
       buffer = new SecondaryBuffer(stream, description, ApplicationDevice); 
       buffer.Play(0, BufferPlayFlags.Default); 
      } 
      catch (Exception e) 
      { 
       MessageBox.Show(e.Message); 
      } 
     } 

     static short[] samples = new short[4410]; // 100 ms 
     static CSound sound; 

     static void Main() 
     { 
      Form form = new Form(); 
      form.Show(); 

      sound = new CSound(); 
      Random random = new Random(); 
      for (int i = 0; i < samples.Length; i++) 
       samples[i] = 1000; // constant value 

      while (true) 
      { 
       sound.Play(samples); 
       System.Threading.Thread.Sleep(100); // 100 ms 
      } 
     } 
    } 
} 

回答

1

此代码有许多错误。我猜测,当你运行这个代码时,你会听到每100毫秒点击或弹出一个声音。这是因为while(true)循环内的Thread.Sleep(100)调用。基本上,您的应用程序正在等待100毫秒(给予或占用一小部分时间),然后调用Play(),稍后处理,然后将数组排队等待播放。因此,每播放100毫秒的数组之间有一点时间差距,这会产生点击。但是,如果您刚刚注释掉了Thread.Sleep(100)行,则您的应用程序将进入无限循环,并在100 ms阵列后排队等待100 ms数组,直到内存用完。但至少播放不会每100毫秒产生一次伪影。

如果将该行更改为Thread.Sleep(80),它会工作得更好一点,因为它会花费更长的时间来耗尽内存,但这仍然会发生,因为您仍然会倾销缓冲到音频播放系统的速度比系统可以播放的速度快。另外,即使您每隔100 ms摆脱一次咔嗒声,您仍然听不到任何声音从您的扬声器中传出,因为您的代码将每个样本值设置为1000.您将只听到任何声音如果您随时间改变样本值。顺便说一下,您听到单击的唯一原因是因为此示例值设置为1000,并且在块之间的这些小的时间间隔期间,回放值回到0.如果将每个样本值设置为0,听到任何事情。

我可以帮助你进一步了解这一点,但我需要更好地了解你正在尝试做什么,确切地说。你是否试图以某种频率播放连续音?

4

如果您寻找一种方式,通过定义的流播放音频输出,你有没有考虑n音讯http://naudio.codeplex.com/

您可以定义一个流,从文件或其他一些位置(即内存),然后填入你想要播放的数据流。只要您能够在读指针到达缓冲区末尾之前继续向流提供音频数据,您就不会在生成的音频中听到这些文物。

顺便说一句 - 我想你知道,不再被开发为.NET托管的Direct X库和有效的是这种音频发展的死胡同?

0

如果“不良信号”你的意思是在两端有轻微的爆裂声,它可能是一个信封的问题,这在CSound的可以由“麻”操作码或类似的东西来控制。这个想法是,你需要在前端加大幅度,在后端稍微加大一点,以避免扬声器的咔哒声突然停止在中波输出,可以这么说。几ms就足够了 - 试验它,直到你摆脱了爆裂而不知道幅度调制。

看吧:http://www.csounds.com/journal/issue11/csoundEnvelopes.html

如果您正在试图通过阐明相同的波形依次定期产生的无缝信号,那你会一直听到这样的爆裂声,因为一个波形的结尾不排队随着下一个波形的开始。让波形准确排列非常困难,这并不是一个好策略。一个更好的策略是使用一个包络(如上所述)并重叠波形(称为鸽尾),以便旧的关节的衰变与新关节的兴起同时发生。

但是,这种策略不会产生完全纯粹的声音,因为两个有点相同的波形异步重叠的存在将相互抵消一点,并导致在波形的每个交界处振幅减小。