我正在开发一个简单的实时音频库。程序员可以调用可以实时播放音频的方法。此外,程序员可以调用可以在特定频率和持续时间实时播放正弦波的方法。正在播放的音频格式是WAV。问题是,当我调用playSineWave时,它抛出一个内部异常,说PlayerSound.Play不能播放声音,因为它有一个损坏的标题。所以起初我认为我偶然写错了标题。但是我保存了wav文件的实际字节,当然包括标题,它在iTunes中播放时没有错误,听起来像是正确频率的正弦波。所以我做错了什么,因为被抛出的异常是误导性的。
以下是导致问题的代码:
public void playSineWave(int frequency, int totalSampleLength)
{
if (frequency <= 0)
{
throw new InvalidDataException("frequency cannot be zero or negative.");
}
List<byte> data = new List<byte>();
byte[] header = getWavHeader(totalSampleLength);
data.AddRange(header);
for(int x = 0; x < (totalSampleLength); x++)
{
data.Add((byte)(127 * Math.Sin(2 * (Math.PI / sampleRate) * frequency * (x / 2))));
}
using(MemoryStream stream = new MemoryStream())
{
using (SoundPlayer player = new SoundPlayer(stream))
{
stream.Write(data.ToArray(), 0, data.Count);
player.Play();
}
}
}
public int secondsToSampleLength(int seconds)
{
/*
* x sample 2 byte 60 second
* -------- * -------- * --------------- = total in samples
* 1 second 1 second 1 second
*/
return sampleRate * seconds * 2;
}
private byte[] getWavHeader(int totalSamples)
{
MemoryStream stream = new MemoryStream();
byte[] wavHeader;
using (BinaryWriter writer = new BinaryWriter(stream, Encoding.BigEndianUnicode))
{
writer.Write(new byte[] { 0x52, 0x49, 0x46, 0x46 }); // 4
int bitsPerSample = 16;
writer.Write(36 + (totalSamples * numberOfChannels * (bitsPerSample / 8))); // 4
writer.Write(new byte[] { 0x57, 0x41, 0x56, 0x45 }); // 4
writer.Write(new byte[] { 0x66, 0x6d, 0x74, 0x20 }); // 4
writer.Write(16); // 4
writer.Write((short)1); // 2
writer.Write((short)numberOfChannels); // 2
writer.Write(sampleRate); // 4
writer.Write(sampleRate * numberOfChannels * (bitsPerSample / 8)); // 4
writer.Write((short)(numberOfChannels * (bitsPerSample / 8))); // 2
writer.Write((short)(bitsPerSample)); // 2
writer.Write(new byte[] { 0x64, 0x61, 0x74, 0x61 }); // 4
writer.Write((totalSamples * numberOfChannels * (bitsPerSample / 8))); // 4
wavHeader = stream.ToArray();
}
return wavHeader;
}
我是否必须使用可以播放WAV文件的Windows API中的方法?任何建议都会有所帮助。提前致谢。请不要建议NAudio我想从头开始编写WAV文件,我知道那个很棒的库。
忘记提及如果我在写完后将位置设置为零,那么它会在下面给出这个例外:
尝试读取或写入受保护的内存。这通常表明其他内存已损坏。
答案 0 :(得分:0)
我通过使用非托管代码解决了这个问题。下面的这个类允许我在内存中播放一个wav文件,只需使用写入转换方法将字节数组转换为字符串,我就可以实时播放wav文件甚至是正弦波。
class UnManagedSoundPlayer
{
[DllImport("WinMM.dll")]
public static extern bool PlaySound(string data, int Mod, int flag); // these are the SoundFlags we are using here, check mmsystem.h for more
public const int SND_ASYNC = 0x0001; // play asynchronously
public const int SND_FILENAME = 0x00020000; // use file name
public const int SND_PURGE = 0x0040; // purge non-static events
public const int SND_NODEFAULT = 0x00002;
public const int SND_SYNC = 0x00000;
public const int SND_MEMORY = 0x00004;
public static bool Play(string data, int SoundFlags = (SND_ASYNC | SND_MEMORY | SND_NODEFAULT | SND_SYNC))
{
return PlaySound(data, 0, SoundFlags);
}
public static void StopPlay()
{
PlaySound(null, 0, SND_PURGE);
}
}
答案 1 :(得分:-1)
Stream.Write将流的位置设置为写入数据的末尾。尝试在调用SoundPlayer.Play()之前将流的位置重置为0.