本文介绍了从多通道WAV文件读取单个通道的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要从wav文件中提取单个通道的样本,该文件最多包含12个(11.1格式)通道.我知道在普通的立体声文件中,样本是交错插入的,首先是左,然后是右,就像这样,

I need to extract the samples of a single channel from a wav file that will contain up to 12 (11.1 format) channels. I know that within a normal stereo file samples are interleaved, first left, and then right, like so,

[1st L] [1st R] [2nd L] [2nd R]...

所以,要读取左声道,我会这样做,

So, to read the left channel I'd do this,

for (var i = 0; i < myByteArray.Length; i += (bitDepth / 8) * 2)
{
    // Get bytes and convert to actual samples.
}

要获得正确的频道,我只需执行for (var i = (bitDepth / 8)....

And to get the right channel I'd simply do for (var i = (bitDepth / 8)....

但是,具有2个以上通道的文件使用什么顺序?

But, what order is used for files with more than 2 channels?

推荐答案

Microsoft已创建标准,最多可覆盖18个频道.根据他们的意见,wav文件需要具有特殊meta子-chunk (在可扩展格式"部分下),用于指定通道掩码"(dwChannelMask).该字段的长度为4个字节(a uint),其中包含每个通道的相应位,因此指示文件中使用了18个通道中的哪个.

Microsoft have created a standard that covers up to 18 channels. According to them, the wav file needs to have a special meta sub-chunk (under the "Extensible Format" section) that specifies a "channel mask" (dwChannelMask). This field is 4 bytes long (a uint) which contains the corresponding bits of each channel that is present, therefore indicating which of the 18 channels are used within the file.

下面是MCL,即,现有通道的插入顺序以及每个通道的位值.如果不存在通道,则存在的下一个通道将下拉"到丢失通道的位置,并且将使用其顺序号代替,但从不使用该位值. (无论通道存在与否,位值对于每个通道都是唯一的),

Below is the MCL, that is, the order in which existing channels should be interleaved, along with the bit value for each channel. If a channel is not present, the next channel that is there will "drop down" into the place of the missing channel and its order number will be used instead, but never the bit value. (Bit values are unique to each channel regardless of the channel's existence),

Order | Bit | Channel

 1.     0x1  Front Left
 2.     0x2  Front Right
 3.     0x4  Front Center
 4.     0x8  Low Frequency (LFE)
 5.    0x10  Back Left (Surround Back Left)
 6.    0x20  Back Right (Surround Back Right)
 7.    0x40  Front Left of Center
 8.    0x80  Front Right of Center
 9.   0x100  Back Center
10.   0x200  Side Left (Surround Left)
11.   0x400  Side Right (Surround Right)
12.   0x800  Top Center
13.  0x1000  Top Front Left
14.  0x2000  Top Front Center
15.  0x4000  Top Front Right
16.  0x8000  Top Back Left
17. 0x10000  Top Back Center
18. 0x20000  Top Back Right

例如,如果通道掩码为0x63F(1599),则表明文件包含8个通道(FL,FR,FC,LFE,BL,BR,SL和SR).

For example, if the channel mask is 0x63F (1599), this would indicate that the file contains 8 channels (FL, FR, FC, LFE, BL, BR, SL & SR).

要获取遮罩,您需要阅读第40 ,41 ,42 和43 字节(假设基本索引为0,并且您正在读取标准的wav标头).例如,

To get the mask, you'll need to read the 40, 41, 42 and 43 byte (assuming a base index of 0, and you're reading a standard wav header). For example,

var bytes = new byte[50];

using (var stream = new FileStream("filepath...", FileMode.Open))
{
    stream.Read(bytes, 0, 50);
}

var speakerMask = BitConverter.ToUInt32(new[] { bytes[40], bytes[41], bytes[42], bytes[43] }, 0);

然后,您需要检查所需通道是否实际存在.为此,我建议创建一个包含所有通道(及其各自值)的enum(用[Flags]定义).

Then, you need to check if the desired channel actually exists. To do this, I'd suggest creating an enum (defined with [Flags]) that contains all the channels (and their respective values).

[Flags]
public enum Channels : uint
{
    FrontLeft = 0x1,
    FrontRight = 0x2,
    FrontCenter = 0x4,
    Lfe = 0x8,
    BackLeft = 0x10,
    BackRight = 0x20,
    FrontLeftOfCenter = 0x40,
    FrontRightOfCenter = 0x80,
    BackCenter = 0x100,
    SideLeft = 0x200,
    SideRight = 0x400,
    TopCenter = 0x800,
    TopFrontLeft = 0x1000,
    TopFrontCenter = 0x2000,
    TopFrontRight = 0x4000,
    TopBackLeft = 0x8000,
    TopBackCenter = 0x10000,
    TopBackRight = 0x20000
}

然后最后检查该频道是否存在.

And then finally check if the channel is present.

创建一个自己的人!根据文件的通道数,您将不得不猜测使用了哪些通道,或者只是盲目地遵循MCL.在下面的代码片段中,我们同时做了这两者,

Create one yourself! Based on the file's channel count you will either have to guess which channels are used, or just blindly follow the MCL. In the below code snippet we're doing a bit of both,

public static uint GetSpeakerMask(int channelCount)
{
    // Assume setup of: FL, FR, FC, LFE, BL, BR, SL & SR. Otherwise MCL will use: FL, FR, FC, LFE, BL, BR, FLoC & FRoC.
    if (channelCount == 8)
    {
        return 0x63F;
    }

    // Otherwise follow MCL.
    uint mask = 0;
    var channels = Enum.GetValues(typeof(Channels)).Cast<uint>().ToArray();

    for (var i = 0; i < channelCount; i++)
    {
        mask += channels[i];
    }

    return mask;
}

提取样品

要实际读取特定通道的样本,您所做的操作与立体声文件完全相同,也就是说,您要按帧大小(以字节为单位)递增循环计数器.

Extracting the samples

To actually read samples of a particular channel, you do exactly the same as if the file were stereo, that is, you increment your loop's counter by frame size (in bytes).

frameSize = (bitDepth / 8) * channelCount

您还需要偏移循环的起始索引.这使事情变得更加复杂,因为您必须从基于现有通道的通道编号 开始读取数据,再乘以字节深度.

You also need to offset your loop's starting index. This is where things become more complicated, as you have to start reading data from the channel's order number based on existing channels, times byte depth.

基于现有渠道"是什么意思?好了,您需要从1重新分配现有渠道的订单号,从而增加现有渠道的订单.例如,通道掩码0x63F表示FL,FR,FC,LFE,BL,BR,SL&使用SR通道,因此各个通道的新通道顺序编号应如下所示(请注意,位值不可更改,并且永远不应更改)

What do I mean "based on existing channels"? Well, you need to reassign the existing channels' order number from 1, incrementing the order for each channel that is present. For example, the channel mask 0x63F indicates that the FL, FR, FC, LFE, BL, BR, SL & SR channels are used, therefore the new channel order numbers for the respective channels would look like this (note, the bit values are not and should not ever be changed),

Order | Bit | Channel

 1.     0x1  Front Left
 2.     0x2  Front Right
 3.     0x4  Front Center
 4.     0x8  Low Frequency (LFE)
 5.    0x10  Back Left (Surround Back Left)
 6.    0x20  Back Right (Surround Back Right)
 7.   0x200  Side Left (Surround Left)
 8.   0x400  Side Right (Surround Right)

您会注意到FLoC,FRoC& BC都丢失了,因此SL& SR渠道下拉"到下一个最低可用订单号,而不是使用SL& SR的默认顺序(10、11).

You'll notice that the FLoC, FRoC & BC are all missing, therefore the SL & SR channels "drop down" into the next lowest available order numbers, rather than using the SL & SR's default order (10, 11).

因此,要读取单个通道的字节,您需要执行类似的操作,

So, to read the bytes of a single channel you'd need to do something similar to this,

// This code will only return the bytes of a particular channel. It's up to you to convert the bytes to actual samples.
public static byte[] GetChannelBytes(byte[] audioBytes, uint speakerMask, Channels channelToRead, int bitDepth, uint sampleStartIndex, uint sampleEndIndex)
{
    var channels = FindExistingChannels(speakerMask);
    var ch = GetChannelNumber(channelToRead, channels);
    var byteDepth = bitDepth / 8;
    var chOffset = ch * byteDepth;
    var frameBytes = byteDepth * channels.Length;
    var startByteIncIndex = sampleStartIndex * byteDepth * channels.Length;
    var endByteIncIndex = sampleEndIndex * byteDepth * channels.Length;
    var outputBytesCount = endByteIncIndex - startByteIncIndex;
    var outputBytes = new byte[outputBytesCount / channels.Length];
    var i = 0;

    startByteIncIndex += chOffset;

    for (var j = startByteIncIndex; j < endByteIncIndex; j += frameBytes)
    {
        for (var k = j; k < j + byteDepth; k++)
        {
            outputBytes[i] = audioBytes[(k - startByteIncIndex) + chOffset];
            i++;
        }
    }

    return outputBytes;
}

private static Channels[] FindExistingChannels(uint speakerMask)
{
    var foundChannels = new List<Channels>();

    foreach (var ch in Enum.GetValues(typeof(Channels)))
    {
        if ((speakerMask & (uint)ch) == (uint)ch)
        {
            foundChannels.Add((Channels)ch);
        }
    }

    return foundChannels.ToArray();
}

private static int GetChannelNumber(Channels input, Channels[] existingChannels)
{
    for (var i = 0; i < existingChannels.Length; i++)
    {
        if (existingChannels[i] == input)
        {
            return i;
        }
    }

    return -1;
}

这篇关于从多通道WAV文件读取单个通道的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-07 05:56