在没有外部库的情况下在C#中播放动态创建的简单声音

Ana*_*ist 7 c# audio dynamic

我需要能够使用C#动态生成波形并播放它,无需任何外部库,也无需将声音文件存储在硬盘上.延迟不是问题; 声音将在应用程序需要之前生成.

实际上,Console.Beep()方法可能满足我的需求,如果不是因为微软表示在64位版本的Windows中不支持它.

如果我动态地生成自己的声音,我可以得到一个比简单的嘟嘟声更精彩的声音.例如,我可以从三角波制作波形,频率从2 KHz增加到4 KHz,同时衰减音量.我不需要花哨的16位立体声,只需8位单声道即可.我不需要动态控制音量和音高,只需在内存中生成一个声音文件并播放它而不存储它.

许多年前,我需要在Apple II,HP工作站和我的旧Amiga计算机上生成声音.从那时起就不需要这样做,而且我所描述的简单的事情似乎变得更加复杂.我很难相信这么简单的东西看起来很难.我看到的大部分答案都是指NAudio或类似的库,这不是这个项目的选项(除了拉动整个库只是为了播放音调这一事实似乎是浪费).

Ana*_*ist 10

基于我收到的答案中的一个链接,以及我发现的.wav标题格式的其他一些页面,这是我的工作代码,用于生成8位"ding!"的小类.用户指定的频率和持续时间的声音.它基本上是一个蜂鸣声,在指定的持续时间内在幅度上线性衰减到零.

public class AlertDing {
    private SoundPlayer player = null;
    private BinaryWriter writer = null;

    /// <summary>
    /// Dynamically generate a "ding" sound and save it to a memory stream
    /// </summary>
    /// <param name="freq">Frequency in Hertz, e.g. 880</param>
    /// <param name="tenthseconds">Duration in multiple of 1/10 second</param>
    public AlertDing(double freq, uint tenthseconds) {

        string header_GroupID = "RIFF";  // RIFF
        uint header_FileLength = 0;      // total file length minus 8, which is taken up by RIFF
        string header_RiffType = "WAVE"; // always WAVE

        string fmt_ChunkID = "fmt "; // Four bytes: "fmt "
        uint fmt_ChunkSize = 16;     // Length of header in bytes
        ushort fmt_FormatTag = 1;        // 1 for PCM
        ushort fmt_Channels = 1;         // Number of channels, 2=stereo
        uint fmt_SamplesPerSec = 14000;  // sample rate, e.g. CD=44100
        ushort fmt_BitsPerSample = 8;   // bits per sample
        ushort fmt_BlockAlign =
            (ushort)(fmt_Channels * (fmt_BitsPerSample / 8)); // sample frame size, in bytes
        uint fmt_AvgBytesPerSec =
            fmt_SamplesPerSec * fmt_BlockAlign; // for estimating RAM allocation

        string data_ChunkID = "data";  // "data"
        uint data_ChunkSize;           // Length of header in bytes
        byte [] data_ByteArray;

        // Fill the data array with sample data

        // Number of samples = sample rate * channels * bytes per sample * duration in seconds
        uint numSamples = fmt_SamplesPerSec * fmt_Channels * tenthseconds / 10;
        data_ByteArray = new byte[numSamples];

        //int amplitude = 32760, offset=0; // for 16-bit audio
        int amplitude = 127, offset = 128; // for 8-audio
        double period = (2.0*Math.PI*freq) / (fmt_SamplesPerSec * fmt_Channels);
        double amp;
        for (uint i = 0; i < numSamples - 1; i += fmt_Channels) {
            amp = amplitude * (double)(numSamples - i) / numSamples; // amplitude decay
            // Fill with a waveform on each channel with amplitude decay
            for (int channel = 0; channel < fmt_Channels; channel++) {
                data_ByteArray[i+channel] = Convert.ToByte(amp * Math.Sin(i*period) + offset);
            }
        }

        // Calculate file and data chunk size in bytes
        data_ChunkSize = (uint)(data_ByteArray.Length * (fmt_BitsPerSample / 8));
        header_FileLength = 4 + (8 + fmt_ChunkSize) + (8 + data_ChunkSize);

        // write data to a MemoryStream with BinaryWriter
        MemoryStream audioStream = new MemoryStream();
        BinaryWriter writer = new BinaryWriter(audioStream);

        // Write the header
        writer.Write(header_GroupID.ToCharArray());
        writer.Write(header_FileLength);
        writer.Write(header_RiffType.ToCharArray());

        // Write the format chunk
        writer.Write(fmt_ChunkID.ToCharArray());
        writer.Write(fmt_ChunkSize);
        writer.Write(fmt_FormatTag);
        writer.Write(fmt_Channels);
        writer.Write(fmt_SamplesPerSec);
        writer.Write(fmt_AvgBytesPerSec);
        writer.Write(fmt_BlockAlign);
        writer.Write(fmt_BitsPerSample);

        // Write the data chunk
        writer.Write(data_ChunkID.ToCharArray());
        writer.Write(data_ChunkSize);
        foreach (byte dataPoint in data_ByteArray) {
            writer.Write(dataPoint);
        }
        player = new SoundPlayer(audioStream);
    }

    /// <summary>
    /// Call this to clean up when program is done using this sound
    /// </summary>
    public void Dispose() {
        if (writer != null) writer.Close();
        if (player != null) player.Dispose();
        writer = null;
        player = null;
    }

    /// <summary>
    /// Play "ding" sound
    /// </summary>
    public void Play() {
        if (player != null) {
            player.Stream.Seek(0, SeekOrigin.Begin); // rewind stream
            player.Play();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助那些试图动态生成简单警报声音而不需要声音文件的人.


Ram*_*ich 0

以下文章介绍了如何使用 SoundPlayer 生成和播放 *.wav 文件。请注意,SoundPlayer 可以将流作为参数,因此您可以在 MemoryStream 中生成 wav 文件内容并避免保存到文件中。

http://blogs.msdn.com/b/dawate/archive/2009/06/24/intro-to-audio-programming-part-3-synthesizing-simple-wave-audio-using-c.aspx