7

C# を使用して、外部ライブラリを一切使用せず、またサウンド ファイルをハードディスクに保存する必要もなく、動的に波形を生成して再生できる必要があります。レイテンシーは問題ではありません。サウンドは、アプリケーションが必要とするかなり前に生成されます。

実際、Console.Beep() メソッドは、Microsoft が Windows の 64 ビット バージョンではサポートされていないと言っているという事実がなければ、私のニーズを満たすかもしれません。

独自のサウンドを動的に生成すると、単純なビープ音よりもファンシーなサウンドを得ることができます。たとえば、音量が減衰しながら周波数が 2 KHz から 4 KHz に増加する三角波から波形を作成できます。派手な 16 ビット ステレオは必要ありません。8 ビット モノラルで十分です。ボリュームとピッチを動的に制御する必要はありません。基本的には、メモリ内にサウンドファイルを生成し、保存せずに再生するだけです。

最後にサウンドを生成する必要があったのは、何年も前の Apple II、HP ワークステーション、そして私の古い Amiga コンピュータでした。それ以来、それを行う必要はありませんでした。私が説明する単純なものが、はるかに複雑になっているようです。こんなに単純なものがとても難しいように見えるとは信じられません。私が見る答えのほとんどは、NAudioまたは同様のライブラリを参照しており、それはこのプロジェクトのオプションではありません(トーンを再生するためだけにライブラリ全体をプルするのは無駄に思えるという事実を除けば)。

4

4 に答える 4

11

受け取った回答のリンクの 1 つと、.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();
        }
    }
}

これが、サウンド ファイルを必要とせずに単純なアラート サウンドを動的に生成しようとしている他の人に役立つことを願っています。

于 2012-08-02T23:00:02.697 に答える
2

これは役に立つかもしれません: http://channel9.msdn.com/coding4fun/articles/Generating-Sound-Waves-with-C-Wave-Oscillators

于 2012-08-01T23:10:45.467 に答える
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

于 2012-08-01T23:01:09.187 に答える