我需要能够使用 C# 动态生成波形并播放它,无需任何外部库,也无需在硬盘上存储声音文件。延迟不是问题;声音将在应用程序需要之前生成。
实际上,如果不是微软表示 64 位版本的 Windows 不支持 Console.Beep() 方法,它可能会满足我的需求。
如果我动态地生成自己的声音,我可以获得比简单的蜂鸣声更奇特的声音。例如,我可以用三角波制作一个波形,频率从 2 KHz 增加到 4 KHz,同时音量衰减。我不需要花哨的 16 位立体声,只需 8 位单声道就可以了。我不需要对音量和音调进行动态控制,只需在内存中生成一个声音文件并播放它而不存储它。
上次我需要生成声音是在很多年前,在 Apple II、HP 工作站和我的旧 Amiga 计算机上。从那以后就不需要这样做了,而且我描述的简单的事情似乎变得复杂了很多。我很难相信如此简单的事情看起来如此困难。我看到的大多数答案都涉及 NAudio 或类似的库,这不是这个项目的一个选项(除了事实上,只是为了播放一个音调而拉入整个库似乎是一种浪费)。
根据我收到的答案中的一个链接以及我发现的有关 .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();
}
}
}
希望这可以帮助那些尝试在不需要声音文件的情况下动态生成简单警报声音的其他人。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)