从.WAV文件中读取24位样本

Sam*_*Sam 7 .net c# audio signal-processing wav

我理解如何从文件中读取8位,16位和32位样本(PCM和浮点).wav,因为(方便地).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;
}
Run Code Online (Sandbox Code Playgroud)

Sam*_*Sam 8

读取(和存储)24位样本非常简单.现在,正如你所说的那样,框架中不存在3字节整数类型,这意味着你有两种选择; 要么创建自己的类型,要么可以通过0在样本的字节数组的开头插入一个空字节()来填充24位样本,从而使它们成为32位样本(这样您就可以使用它int来存储/操作它们) ).

我将解释并演示如何做到以后(这也是我认为更简单的方法).


首先,我们必须看看如何将24位样本存储在一个中int,

〜〜〜〜〜〜〜〜〜〜〜MSB〜2 MSB ~~ 2 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右边的位置以获得样本的正确的24位字节表示(从)-83886088388608; 然后除以将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;
}
Run Code Online (Sandbox Code Playgroud)


对于更快的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();
}
Run Code Online (Sandbox Code Playgroud)



1将上述代码与之前的方法进行基准测试后,此解决方案的速度提高了约450%至550%.

  • 你应该留下你的数据.IOW,将零置于底部而不是顶部.这样做的一个原因是它会将符号位置于正确的位置.正如你现在的答案所示,你不会得到负数表示.第二个原因是,当数据左对齐时,无论原始位深度(24位或32位)如何,都可以使用相同的缩放系数(1.0/0x80000000)转换为浮点数. (2认同)
  • 24 位有符号整数的最大值是 8388607,而不是 8388608。 (2认同)