我理解如何读取8位,16位&来自.wav
文件的32位样本(PCM和浮点),因为(方便地).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;
}
答案 0 :(得分:7)
读取(和存储)24位样本非常简单。现在,正如你所说的那样,框架中不存在3字节整数类型,这意味着你有两种选择;或者创建自己的类型,或者,您可以通过在样本的字节数组的开头插入一个空字节(0
)来填充24位样本,从而使它们成为32位样本(这样您就可以使用int
来存储/操纵它们。)
我将解释并演示如何做到以后(这也是我认为更简单的方法)。
首先,我们必须查看如何将 24位样本存储在int
中,
MSB~~ 2 nd MSB~~2 nd LSB~~ LSB~~ <〜> ~~~~~~~~24位样本:
11001101 01101001 01011100 00000000
32位样本:
11001101 01101001 01011100 00101001
MSB =最高有效字节,LSB =无效有效字节。
正如您所看到的,24位样本的LSB为0
,因此您只需要使用 byte[]
元素声明4
,然后将样本的3个字节读入数组(从元素 1
开始),这样你的数组就像下面一样(有效地向左移位8位),
myArray [0]:
00000000
myArray [1]:
01011100
myArray [2]:
01101001
myArray [3]:
11001101
将字节数组填满后,您可以将其传递给BitConverter.ToInt32(myArray, 0);
,然后您需要将 8
位置的样本移至右侧< / em>获取样本中的正确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%。