我了解如何从 .wav
文件中读取 8 位、16 位和 32 位样本(PCM 和 float ),因为(方便地).Net Framework 有一个那些精确尺寸的内置积分类型。但是,我不知道如何读取(和存储)24 位(3 字节)样本。
如何读取 24 位音频?有没有什么方法可以改变我当前读取 32 位音频的方法(如下)来解决我的问题?
private List<float> Read32BitSamples(FileStream stream, int sampleStartIndex, int sampleEndIndex)
{
var samples = new List<float>();
var bytes = ReadChannelBytes(stream, Channels.Left, sampleStartIndex, sampleEndIndex); // Reads bytes of a single channel.
if (audioFormat == WavFormat.PCM) // audioFormat determines whether to process sample bytes as PCM or floating point.
{
for (var i = 0; i < bytes.Length / 4; i++)
{
samples.Add(BitConverter.ToInt32(bytes, i * 4) / 2147483648f);
}
}
else
{
for (var i = 0; i < bytes.Length / 4; i++)
{
samples.Add(BitConverter.ToSingle(bytes, i * 4));
}
}
return samples;
}
最佳答案
读取(和存储)24 位样本非常简单。现在,正如您所说的那样,框架中不存在 3 字节整数类型,这意味着您有两种选择;创建您自己的类型,或者,您可以通过在样本字节数组的开头插入一个空字节 (0
) 来填充 24 位样本,从而使它们成为 32 位样本(这样您就可以然后使用 int
来存储/操作它们)。
我将在后面解释和演示如何做(我认为这也是更简单的方法)。
首先,我们必须了解 24 位样本如何存储在 int
中,
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ MSB ~ ~ 2ndMSB ~ ~ 2ndLSB ~ ~ LSB ~ ~
24-bit sample:
11001101 01101001 01011100 00000000
32-bit sample:
11001101 01101001 01011100 00101001
MSB = 最高有效字节,LSB = 最低有效字节。
正如您所看到的 24 位样本的 LSB 是 0
,因此您所要做的就是用 声明一个
元素,然后将示例的 3 个字节读入数组(从元素 byte[]
41
开始),这样您的数组如下所示 (有效地向左移动 8 位),
myArray[0]:
00000000
myArray[1]:
01011100
myArray[2]:
01101001
myArray[3]:
11001101
一旦你的字节数组已满,你就可以将它传递给 BitConverter.ToInt32(myArray, 0);
,然后您需要将样本向8
位置移动到右边,以获得正确的 24 位整数表示形式的样本(来自-8388608
到8388608
);然后除以 8388608
将其作为浮点值。
所以,将所有这些放在一起你应该得到这样的结果,
请注意,我编写以下代码的目的是“易于理解”,因此这不是是最高效的方法,要获得更快的解决方案,请参阅代码在这个下面。
private List<float> Read24BitSamples(FileStream stream, int startIndex, int endIndex)
{
var samples = new List<float>();
var bytes = ReadChannelBytes(stream, Channels.Left, startIndex, endIndex);
var temp = new List<byte>();
var paddedBytes = new byte[bytes.Length / 3 * 4];
// Right align our samples to 32-bit (effectively bit shifting 8 places to the left).
for (var i = 0; i < bytes.Length; i += 3)
{
temp.Add(0); // LSB
temp.Add(bytes[i]); // 2nd LSB
temp.Add(bytes[i + 1]); // 2nd MSB
temp.Add(bytes[i + 2]); // MSB
}
// BitConverter requires collection to be an array.
paddedBytes = temp.ToArray();
temp = null;
bytes = null;
for (var i = 0; i < paddedBytes.Length / 4; i++)
{
samples.Add(BitConverter.ToInt32(paddedBytes, i * 4) / 2147483648f); // Skip the bit shift and just divide, since our sample has been "shited" 8 places to the right we need to divide by 2147483648, not 8388608.
}
return samples;
}
为了实现更快的1 实现,您可以改为执行以下操作,
private List<float> Read24BitSamples(FileStream stream, int startIndex, int endIndex)
{
var bytes = ReadChannelBytes(stream, Channels.Left, startIndex, endIndex);
var samples = new float[bytes.Length / 3];
for (var i = 0; i < bytes.Length; i += 3)
{
samples[i / 3] = (bytes[i] << 8 | bytes[i + 1] << 16 | bytes[i + 2] << 24) / 2147483648f;
}
return samples.ToList();
}
1 根据之前的方法对上述代码进行基准测试后,该解决方案的速度大约提高了 450% 到 550%。
关于c# - 从 .WAV 文件中读取 24 位样本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24151973/