如何根据频率生成声音?

21

可能的重复问题:
在C#中创建正弦波或方波

我想要生成声音。可以是像以下两者之一的东西:

MakeSound(frequency, duration);

或:

MakeSound(MyArrayOfSamples);

我找到了一个叫做:Microsoft.Directx.DirectSound的东西,但是听说已经停用了。在Visual Studio(2010)中,我没能找到它作为一个引用选项。我找到了这个链接,根据我所读的内容,应该包含它,但我不确定是否要使用自2006年以来就不再得到支持的东西。而且这个链接表示它是针对C/C++的(尽管还说:“托管代码”),我不想浪费几周时间去理解如何将其封装为托管代码,只是为了发现我无法实现。我找到的最有希望的链接是WaveFormat ,但我找不到如何使用它。

并不是在询问如何获得声波的数学表示。我也不是在询问如何播放mp3文件或类似的东西。我也不是在寻找第三方软件或包装器。只是在寻找一个C# / .net解决方案,以满足非常具体的目标。


1
你是不是想要在指定的频率和持续时间内播放一个音调? - Jon B
1
太棒了。尽管我已经明确指出我不要找的内容,但我的问题仍然被投票为重复! - ispiro
1
@JüriRuut 从我的问题来看,我也不是在问如何播放mp3文件或类似的东西。 - ispiro
1
这篇帖子对我很有帮助 http://social.msdn.microsoft.com/Forums/vstudio/en-US/18fe83f0-5658-4bcf-bafc-2e02e187eb80/beep-beep - Pro-grammar
@vternal3 非常感谢!我现在已经点赞了你类似的答案这里。:) 再次感谢。 - ispiro
显示剩余8条评论
2个回答

29

不管怎样,除非你想使用非托管代码,否则你都需要构建一个WAV文件来播放它。然而,在Eric Lippert的博客中有一段代码片段,可以向你展示如何使用频率制作自己的WAV文件。

namespace Wave
{
   using System;
   using System.IO;
   class MainClass {
      public static void Main() {
         FileStream stream = new FileStream("test.wav", FileMode.Create);
         BinaryWriter writer = new BinaryWriter(stream);
         int RIFF = 0x46464952;
         int WAVE = 0x45564157;
         int formatChunkSize = 16;
         int headerSize = 8;
         int format = 0x20746D66;
         short formatType = 1;
         short tracks = 1;
         int samplesPerSecond = 44100;
         short bitsPerSample = 16;
         short frameSize = (short)(tracks * ((bitsPerSample + 7)/8));
         int bytesPerSecond = samplesPerSecond * frameSize;
         int waveSize = 4;
         int data = 0x61746164;
         int samples = 88200 * 4;
         int dataChunkSize = samples * frameSize;
         int fileSize = waveSize + headerSize + formatChunkSize + headerSize + dataChunkSize;
         writer.Write(RIFF);
         writer.Write(fileSize);
         writer.Write(WAVE);
         writer.Write(format);
         writer.Write(formatChunkSize);
         writer.Write(formatType);
         writer.Write(tracks); 
         writer.Write(samplesPerSecond);
         writer.Write(bytesPerSecond);
         writer.Write(frameSize);
         writer.Write(bitsPerSample); 
         writer.Write(data);
         writer.Write(dataChunkSize);
         double aNatural = 220.0;
         double ampl = 10000;
         double perfect = 1.5;
         double concert = 1.498307077;
         double freq = aNatural * perfect;
         for (int i = 0; i < samples / 4; i++) {
            double t = (double)i / (double)samplesPerSecond;
            short s = (short)(ampl * (Math.Sin(t * freq * 2.0 * Math.PI)));
            writer.Write(s);
         }
         freq = aNatural * concert;
         for (int i = 0; i < samples / 4; i++) {
            double t = (double)i / (double)samplesPerSecond;
            short s = (short)(ampl * (Math.Sin(t * freq * 2.0 * Math.PI)));
            writer.Write(s);
         }
         for (int i = 0; i < samples / 4; i++) {
            double t = (double)i / (double)samplesPerSecond;
            short s = (short)(ampl * (Math.Sin(t * freq * 2.0 * Math.PI) + Math.Sin(t * freq * perfect * 2.0 * Math.PI)));
            writer.Write(s);
         }
         for (int i = 0; i < samples / 4; i++) {
            double t = (double)i / (double)samplesPerSecond;
            short s = (short)(ampl * (Math.Sin(t * freq * 2.0 * Math.PI) + Math.Sin(t * freq * concert * 2.0 * Math.PI)));
            writer.Write(s);
         }
         writer.Close();
         stream.Close();
      }
   }
}

根据您的需求进行拆分,但请注意aNatural变量 - 它是一个频率 - 就像您正在寻找的那样。

现在,您可以将其放入MemoryStream中,然后使用SoundPlayer播放它(如果需要)。


1
无论如何,除非你想使用非托管代码,否则你必须构建一个WAV文件来播放它。但并不一定如此。似乎“DirectSound”可以做到我想要的。但正如我所写的那样 - 除非有人告诉我如何运行它(以及:它是否能够运行!)。 - ispiro
3
@ispiro,我提供的代码是一个“.NET原始”解决方案,完全符合你的要求。此外,DirectSound将使用非托管代码与声卡和其他低级设备通信。我认为,使用上述代码轻松生成一个简单的WAV文件,可以获得你想要的结果,而且没有额外的负担和需要担心的任何依赖项。 - Mike Perrenoud
使用这段代码,我该如何改变波形文件的长度?目前我只能处理8秒的音频。 - Code Disease

17
这是一个围绕内核中 Beep 函数的包装器,它需要持续时间和频率。现在的 Beep 使用声卡;在我那个年代,它使用一些粘在主板上的压电装置。
using System;
using System.Runtime.InteropServices;

class BeepSample
{
    [DllImport("kernel32.dll", SetLastError=true)]
    static extern bool Beep(uint dwFreq, uint dwDuration);

    static void Main()
    {
        Console.WriteLine("Testing PC speaker...");
        for (uint i = 100; i <= 20000; i++)
        {
            Beep(i, 5);
        }
        Console.WriteLine("Testing complete.");
    }
}

在我的Win10电脑上,我需要将持续时间(最后一个参数)设置得更大,而不是这里的5毫秒,更接近50毫秒,但要得到合理的结果,我必须将其设置为100毫秒。
或者使用Console.Beep(Int32, Int32)方法,如果您想走捷径。该方法是在.Net 2.0中引入的。

蜂鸣器在x64系统上无法工作。 - Ivan Kochurkin
4
@KvanTTT,你能提供证据支持你的论断吗?Msdn上说,它已经从win xp x64和vista中全部删除(x86和x64),但在Windows 7中通过默认声卡进行了完全重新实现。我引用了该功能的实现者Larry Osterman的话语。Larry Osterman - rene
不,我的Win 7 x64和Win 8 x64上的蜂鸣声无法工作。这里是一个解释链接:http://blogs.msdn.com/b/larryosterman/archive/2010/01/04/what-s-up-with-the-beep-driver-in-windows-7.aspx,以及一个在SO上的问题链接:https://dev59.com/GEnSa4cB1Zd3GeqPO43I。 - Ivan Kochurkin
有趣的是,我们都引用了同一篇博客来证明我们的观点 :-) - rene
4
我刚在我的win7 x64上验证了它,它确实像广告中所说的那样工作。我想我得赞同Larry Osterman。 - rene
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接