在服务器端的 C# .NET 应用程序中发出蜂鸣声

Beep in C# .NET application on server side

要调试防火墙延迟问题,我需要一个应用程序,它会在检测到 HTTP GET 请求时在服务器端发出蜂鸣声。

此代码(test.ashx):

<%@ WebHandler Language="C#" Class="TestHandler" %>

using System;
using System.Web;

public class TestHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        HttpResponse Response = context.Response;
        try
        {
            Response.Write("Before beep");
            Console.Beep();
            Response.Write("After beep");
        }
        catch (Exception ex)
        {
            Response.Write(ex.Message + "<br />\n" + ex.InnerException.Message);
        }
    }

    public bool IsReusable { get { return false; } }
}

仅在 IIS Express 中调试时发出声音。将 web 应用程序移动到 IIS 后,声音消失了。

试试这个 System.Media.SystemSounds.Beep.Play();

三种简单的发声方法是System.Console.Beep(), System.Media.SoundPlayer, and System.Media.SystemSounds.Beep()

遗憾的是,这些方法仅适用于桌面应用程序,不适用于服务应用程序。当 ASP.Net 应用程序在 IIS Express(桌面应用程序)下 运行 时,这些声音方法有效。但是,当 ASP.Net 应用程序 运行 在 IIS 服务下时,声音方法不起作用。

System.Console.Beep()最终调用了kernel32.dllBeep()函数。它仅限于桌面应用程序(向下滚动到要求部分)。

System.Media.SoundPlayer 和 System.Media.SystemSounds.Beep() 相同。他们分别调用 kernel32.dll MessageBeep() and the winmm.dll PlaySound() 函数。它们也仅限于桌面应用程序。

在服务中播放声音的一种方法是使用 NAudio。通过 NuGet 很容易安装。

这段代码是我播放声音的唯一方法。它必须在单独的工作线程上播放,并且需要暂停工作线程的执行以让.wav 文件完成播放。

using System;
using System.Diagnostics;
using System.Threading;

using NAudio.Dsp;
using NAudio.Wave;

...

protected void Button1_Click(object sender, EventArgs e)
{
  var waveFilename = @"c:\Windows\Media\tada.wav";

  /* Trying to play the .wav file on the main thread
     doesn't seem to work. */
  ThreadPool.QueueUserWorkItem(
    (state) =>
    {
      using (var audioPlayback = new AudioPlayback())
      {
        audioPlayback.Load(waveFilename);
        audioPlayback.Play(); // Asynchronous.

        /* Need to sleep for the approximate length of .wav file,
           otherwise no sound is produced because of the
           asynchronous Play() call. */
        Thread.Sleep(2000);
      }
    });
}

以下是从 NAudio 的 NAudioWPFDemo 项目中获取的支持代码:

public class MaxSampleEventArgs : EventArgs
{
  [DebuggerStepThrough]
  public MaxSampleEventArgs(float minValue, float maxValue)
  {
    this.MaxSample = maxValue;
    this.MinSample = minValue;
  }
  public float MaxSample { get; private set; }
  public float MinSample { get; private set; }
}

public class FftEventArgs : EventArgs
{
  [DebuggerStepThrough]
  public FftEventArgs(Complex[] result)
  {
    this.Result = result;
  }
  public Complex[] Result { get; private set; }
}

public class SampleAggregator : ISampleProvider
{
  // volume
  public event EventHandler<MaxSampleEventArgs> MaximumCalculated;
  private float maxValue;
  private float minValue;
  public int NotificationCount { get; set; }
  int count;

  // FFT
  public event EventHandler<FftEventArgs> FftCalculated;
  public bool PerformFFT { get; set; }
  private readonly Complex[] fftBuffer;
  private readonly FftEventArgs fftArgs;
  private int fftPos;
  private readonly int fftLength;
  private int m;
  private readonly ISampleProvider source;

  private readonly int channels;

  public SampleAggregator(ISampleProvider source, int fftLength = 1024)
  {
    channels = source.WaveFormat.Channels;
    if (!IsPowerOfTwo(fftLength))
      throw new ArgumentException("FFT Length must be a power of two");

    this.m = (int) Math.Log(fftLength, 2.0);
    this.fftLength = fftLength;
    this.fftBuffer = new Complex[fftLength];
    this.fftArgs = new FftEventArgs(fftBuffer);
    this.source = source;
  }

  private bool IsPowerOfTwo(int x)
  {
    return (x & (x - 1)) == 0;
  }

  public void Reset()
  {
    count = 0;
    maxValue = minValue = 0;
  }

  private void Add(float value)
  {
    if (PerformFFT && FftCalculated != null)
    {
      fftBuffer[fftPos].X = (float) (value * FastFourierTransform.HammingWindow(fftPos, fftLength));
      fftBuffer[fftPos].Y = 0;
      fftPos++;
      if (fftPos >= fftBuffer.Length)
      {
        fftPos = 0;
        // 1024 = 2^10
        FastFourierTransform.FFT(true, m, fftBuffer);
        FftCalculated(this, fftArgs);
      }
    }

    maxValue = Math.Max(maxValue, value);
    minValue = Math.Min(minValue, value);
    count++;
    if (count >= NotificationCount && NotificationCount > 0)
    {
      if (MaximumCalculated != null)
        MaximumCalculated(this, new MaxSampleEventArgs(minValue, maxValue));

      Reset();
    }
  }

  public WaveFormat WaveFormat { get { return source.WaveFormat; } }

  public int Read(float[] buffer, int offset, int count)
  {
    var samplesRead = source.Read(buffer, offset, count);

    for (int n = 0; n < samplesRead; n += channels)
      Add(buffer[n + offset]);

    return samplesRead;
  }
}

public class AudioPlayback : IDisposable
{
  private IWavePlayer _playbackDevice;
  private WaveStream _fileStream;

  public void Load(string fileName)
  {
    Stop();
    CloseFile();
    EnsureDeviceCreated();
    OpenFile(fileName);
  }

  private void CloseFile()
  {
    if (_fileStream != null)
    {
      _fileStream.Dispose();
      _fileStream = null;
    }
  }

  private void OpenFile(string fileName)
  {
    try
    {
      var inputStream = new AudioFileReader(fileName);
      _fileStream = inputStream;
      var aggregator = new SampleAggregator(inputStream);
      aggregator.NotificationCount = inputStream.WaveFormat.SampleRate / 100;
      aggregator.PerformFFT = true;
      _playbackDevice.Init(aggregator);
    }
    catch
    {
      CloseFile();
      throw;
    }
  }

  private void EnsureDeviceCreated()
  {
    if (_playbackDevice == null)
      CreateDevice();
  }

  private void CreateDevice()
  {
    _playbackDevice = new WaveOut { DesiredLatency = 200 };
  }

  public void Play()
  {
    if (_playbackDevice != null && _fileStream != null && _playbackDevice.PlaybackState != PlaybackState.Playing)
      _playbackDevice.Play();
  }

  public void Pause()
  {
    if (_playbackDevice != null)
      _playbackDevice.Pause();
  }

  public void Stop()
  {
    if (_playbackDevice != null)
      _playbackDevice.Stop();

    if (_fileStream != null)
      _fileStream.Position = 0;
  }

  public void Dispose()
  {
    Stop();
    CloseFile();
    if (_playbackDevice != null)
      _playbackDevice.Dispose();
  }
}