我正在尝试将声音效果加载到我的游戏中(C#/ XNA 4.0,Win8.1上的Visual Studio 2013)。该游戏是现有MMORPG客户端的“克隆”,应与现有目录结构兼容 - 这意味着所有声音效果都存储在游戏工作目录中名为“sfx”的目录中。
我在尝试完成此任务时遇到了SoundEffect.FromStream
。它适用于我拥有的大多数文件,但有些文件会抛出InvalidOperationException。堆栈跟踪显示错误位于XNA DLL内的内部Wav文件构造函数中。
我很清楚SoundEffect.FromStream
方法的restrictions on wav files:8或16位,单声道或立体声,PCM,采样率介于8kHz和48kHz之间。我遇到的问题是,据我所知,无法加载的特定Wav文件满足所有这些要求。
以下是我加载wav文件的方法:
//SoundInfo is a wrapper around SoundEffect
// implementation details are not relevant to my question
private readonly List<SoundInfo> m_guitarSounds;
private readonly List<SoundInfo> m_harpSounds;
private readonly List<SoundInfo> m_sounds;
//... additional initialization
foreach (string sfx in soundFiles)
{
using (FileStream fs = new FileStream(sfx, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
int samples = getSamplesPerSecond(sfx);
SoundEffect nextEffect;
try
{
nextEffect = SoundEffect.FromStream(fs);
}
catch (InvalidOperationException)
{
//I got this idea from another StackOverflow answer
nextEffect = new SoundEffect(File.ReadAllBytes(sfx), samples,
AudioChannels.Mono);
}
if (sfx.ToLower().Contains("gui"))
m_guitarSounds.Add(new SoundInfo(nextEffect));
else if (sfx.ToLower().Contains("har"))
m_harpSounds.Add(new SoundInfo(nextEffect));
else
m_sounds.Add(new SoundInfo(nextEffect));
}
}
失败时,效果由从文件读取的数据的字节数组构成,并强制为Mono。这似乎适用于InvalidOperationException
引起的所有错误。在这些情况下,声音通常会非常扭曲。
以下是getSamplesPerSecond
的代码(在上面的代码中引用):
private int getSamplesPerSecond(string filename)
{
//this method was in place for debugging purposes but is used to get
// the samplesPerSec for the audio files that can't be
// loaded using FromStream
byte[] wav = File.ReadAllBytes(filename);
// Get past all the other sub chunks to get to the data subchunk:
int pos = 12; // First Subchunk ID from 12 to 16
//get fmt chunk
while (!((char)wav[pos] == 'f' && (char)wav[pos + 1] == 'm' &&
(char)wav[pos + 2] == 't' && (char)wav[pos + 3] == ' '))
{
pos += 4;
int chunkSize = wav[pos] + wav[pos + 1] * 256 +
wav[pos + 2] * 65536 + wav[pos + 3] * 16777216;
pos += 4 + chunkSize;
}
pos += 8;
int format = wav[pos] + wav[pos + 1]*256;
pos += 2;
int channels = wav[pos] + wav[pos + 1]*256;
pos += 2;
int samplesPerSec = wav[pos] + wav[pos + 1]*256 + wav[pos + 2]*65536
+ wav[pos + 3]*16777216;
pos += 4;
int avgBytesPerSec = wav[pos] + wav[pos + 1]*256 + wav[pos + 2]*65536
+ wav[pos + 3] * 16777216;
pos += 4;
int blockAlign = wav[pos] + wav[pos + 1]*256;
pos += 2;
int bitsPerSample = 0;
if (format == 1)
{
bitsPerSample = wav[pos] + wav[pos + 1]*256;
//pos += 2; //necessary if we want to look at other chunks
}
return samplesPerSec;
}
抛出一些调试日志语句,我得到了这些文件内容。这是唯一一个失败的部分,总共只有9个失败(滚动到右边!):
sfx\sfx001.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16
sfx\sfx002.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16 [ FAILED ]
sfx\sfx003.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16 [ FAILED ]
sfx\sfx004.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=22050, BlockAlign=1, BitsPerSample=8
sfx\sfx005.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16 [ FAILED ]
sfx\sfx006.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16 [ FAILED ]
sfx\sfx007.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16
sfx\sfx008.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=22050, BlockAlign=1, BitsPerSample=8
sfx\sfx009.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16
sfx\sfx010.wav: Format=1, Channels=2, SamplesPerSec=22050, AvgBytesPerSec=88200, BlockAlign=4, BitsPerSample=16 [ FAILED ]
sfx\sfx011.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16
sfx\sfx012.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16 [ FAILED ]
sfx\sfx013.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16
sfx\sfx014.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=22050, BlockAlign=1, BitsPerSample=8
sfx\sfx015.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16
sfx\sfx016.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16 [ FAILED ]
sfx\sfx017.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16 [ FAILED ]
sfx\sfx018.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16 [ FAILED ]
sfx\sfx019.wav: Format=1, Channels=1, SamplesPerSec=22050, AvgBytesPerSec=44100, BlockAlign=2, BitsPerSample=16
在读取WAV文件头('fmt'块)后,直接从getSamplesPerSecond方法打印打印到上述日志的值。我使用http://www.neurophys.wisc.edu/auditory/riff-format.txt作为参考来确定WAV文件中数据的含义。
根据我的日志,每个文件都是PCM,8或16位,22050Hz采样率,以及Mono或Stereo(基于通道数)。所有文件在原始游戏客户端中打开都很好,并且在Audacity和Windows Media Player中工作,因此我可以排除文件本身的问题。
我在这里缺少什么?如果它与我的源音频文件不兼容,是否有另一种方法可以加载它们并播放它们,而不使用内容管道?
答案 0 :(得分:1)
由于文件格式错误,无法加载的文件每个都会抛出InvalidOperationException
,但这并不是因为任何WAV格式限制。
文件中出现错误,其中文件的字节4,5,6和7中报告的长度与RIFF数据的实际长度不同。
Windows Media Player和Audacity正在补偿此错误,但SoundEffect.FromStream()
正在检测它并抛出异常。
这是我用来解决问题的一种工作方法:
private void _correctTheFileLength(string filename)
{
byte[] wav = File.ReadAllBytes(filename);
string riff = Encoding.ASCII.GetString(wav.SubArray(0, 4));
if (riff != "RIFF" || wav.Length < 8) //check for RIFF tag and length
return;
int reportedLength = wav[4] + wav[5]*256 + wav[6]*65536 + wav[7]*16777216;
int actualLength = wav.Length - 8;
if (reportedLength != actualLength)
{
wav[4] = (byte) (actualLength & 0xFF);
wav[5] = (byte) ((actualLength >> 8) & 0xFF);
wav[6] = (byte) ((actualLength >> 16) & 0xFF);
wav[7] = (byte) ((actualLength >> 24) & 0xFF);
File.WriteAllBytes(filename, wav);
}
}