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

标签 c# .net audio signal-processing wav

我了解如何从 .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,因此您所要做的就是用 声明一个 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;
}

为了实现更快的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/

相关文章:

c# - 拉伸(stretch)四边形以适合矩形

c# - 如何使用 C# 从 IIS 获取当前的 Worker Request 列表?

java - 一起分享音频和文本

c# - 为什么在 ADO.NET/C# 中对 DbTransaction 使用 using 语句?

c# - 将 Objective C 转换为 C# - 此代码的等效项是什么?

c# - 是否可以在 App.xaml.cs 上自动注册 ViewModels 和 Views

.net - 许多-'small'组件的特定缺点?

c# - System.Decimal 是否一致?

javascript - 是否可以捕获音频并将其存储在阵列中?

audio - C++ 读取 wav 文件,subchunk1size = 65536?