为什么 SoundEffect.FromStream() 在读取 "valid" WAV 文件时抛出 InvalidOperationException?
Why does SoundEffect.FromStream() throw InvalidOperationException when reading "valid" WAV files?
我正在尝试将声音效果加载到我的游戏中(C#/XNA 4.0,Win8.1 上的 Visual Studio 2013)。该游戏是现有 MMORPG 客户端的 "clone",应与现有目录结构兼容 - 这意味着所有音效都存储在游戏工作目录中名为 'sfx' 的目录中。
我在尝试完成此操作时遇到了 SoundEffect.FromStream
。它适用于我拥有的大多数文件,但有些文件会抛出 InvalidOperationException。堆栈跟踪显示错误位于 XNA DLL 的内部 Wav 文件构造函数中。
我很清楚 restrictions on wav files 用于 SoundEffect.FromStream
方法: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 Whosebug 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 采样率以及单声道或立体声(基于通道数)。所有文件都可以在原始游戏客户端中完美打开,并且可以在 Audacity 和 Windows Media Player 中运行,因此我可以排除文件本身的问题。
我在这里错过了什么?如果它与我的源音频文件不兼容,是否有另一种方法可以在不使用内容管道的情况下加载和播放它们?
由于文件格式不正确,加载失败的每个文件都抛出 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);
}
}
我正在尝试将声音效果加载到我的游戏中(C#/XNA 4.0,Win8.1 上的 Visual Studio 2013)。该游戏是现有 MMORPG 客户端的 "clone",应与现有目录结构兼容 - 这意味着所有音效都存储在游戏工作目录中名为 'sfx' 的目录中。
我在尝试完成此操作时遇到了 SoundEffect.FromStream
。它适用于我拥有的大多数文件,但有些文件会抛出 InvalidOperationException。堆栈跟踪显示错误位于 XNA DLL 的内部 Wav 文件构造函数中。
我很清楚 restrictions on wav files 用于 SoundEffect.FromStream
方法: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 Whosebug 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 采样率以及单声道或立体声(基于通道数)。所有文件都可以在原始游戏客户端中完美打开,并且可以在 Audacity 和 Windows Media Player 中运行,因此我可以排除文件本身的问题。
我在这里错过了什么?如果它与我的源音频文件不兼容,是否有另一种方法可以在不使用内容管道的情况下加载和播放它们?
由于文件格式不正确,加载失败的每个文件都抛出 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);
}
}