获取最高值的字节数组

时间:2018-10-07 15:13:30

标签: c# arrays audio byte

我有一个包含数百万个字节的数组。这些字节是int值(Int16,Int24或Int32)。现在,我要从字节数中获取具有最大int值的x字节。

因此,为了更好地解释这一点,让我们想象一个包含10个条目的数组:

byte[] arr = {255, 10, 55, 60, 128, 90, 88, 66, 199, 56};

我会知道我们是否使用In16,Int24或Int32,因此对于此示例,假设我们正在使用Int16。这意味着,我们使用2个字节来表示一个Int16。因此Ints包括:

{255, 10},
{55, 60},
{128, 90},
{88, 66},
{199, 56}

问题1:因为这是音频处理所必需的,所以1046低于-2096。因此,需要独立于否定性进行比较

问题2:因为这需要非常有效,所以将字节转换为Ints进行比较似乎效率低下,应该有另一种方法。

这是起点:

    /// <summary>
    /// Gets the maximum value of a number of bytes representing Int-Values
/// </summary>
/// <returns>The channels.</returns>
/// <param name="leftChannel">Left channel.</param>
/// <param name="rightChannel">Right channel.</param>
/// <param name="bytesPerInt">Bytes per int. 2 bytes = Int16, 3 bytes = Int24, 4 bytes = Int32</param>
/// <param name="countBytesToCombine">The number of bytes to look for the highest value</param>
private (byte[] combinedLeft, byte[] combinedRight) CombineChannels(byte[] leftChannel, byte[] rightChannel, int bytesPerInt, int countBytesToCombine)
{

}

/// <summary>
/// Gets the highest byte[] value 
/// </summary>
/// <returns>The highest value. The size of the byte array is equal the bytesPerInt</returns>
/// <param name="bytes">A subarray of the given byte array of the upper method. The size of this array is equals countBytesToCombine</param>
/// <param name="bytesPerInt">The count of bytes representing an Int</param>
private byte[] GetHighestValue(byte[] bytes, int bytesPerInt)
{

}

Edit2

这是一个可行的解决方案,但是执行每个通道的1400万字节大约需要2秒钟的时间,这太过分了。

    /// <summary>
    /// Gets the maximum value of a number of bytes representing Int-Values
    /// </summary>
    /// <returns>The channels.</returns>
    /// <param name="leftChannel">Left channel.</param>
    /// <param name="rightChannel">Right channel.</param>
    /// <param name="bytesPerInt">Bytes per int. 2 bytes = Int16, 3 bytes = Int24, 4 bytes = Int32</param>
    /// <param name="countValuesToCombine">The number of bytes to look for the highest value</param>
    private (byte[] combinedLeft, byte[] combinedRight) CombineChannels(byte[] leftChannel, byte[] rightChannel, int bytesPerInt, int countValuesToCombine)
    {
        var cLeft = new List<byte>();
        var cRight = new List<byte>();

        for (int i = 0; i < leftChannel.Length; i += countValuesToCombine * bytesPerInt)
        {
            var arrLeft = SubArray(leftChannel, i, countValuesToCombine * bytesPerInt);
            var arrRight = SubArray(rightChannel, i, countValuesToCombine * bytesPerInt);

            cLeft.AddRange(GetHighestValue(arrLeft, bytesPerInt));
            cRight.AddRange(GetHighestValue(arrRight, bytesPerInt));
        }

        return (cLeft.ToArray(), cRight.ToArray());
    }

    /// <summary>
    /// Gets the highest byte[] value 
    /// </summary>
    /// <returns>The highest value.</returns>
    /// <param name="bytes">Bytes.</param>
    /// <param name="bytesPerInt">The count of bytes representing an Int</param>
    private byte[] GetHighestValue(byte[] bytes, int bytesPerInt)
    {
        byte[] bytesOfHighestValue = new byte[bytesPerInt];

        for (int i = 0; i < bytes.Length; i += bytesPerInt)
        {
            var arr = SubArray(bytes, i, bytesPerInt);

            if (IsValueHigher(arr, bytesOfHighestValue, bytesPerInt))
            {
                bytesOfHighestValue = arr;
            }
        }

        return bytesOfHighestValue;
    }

    private bool IsValueHigher(byte[] one, byte[] two, int bytesPerInt)
    {
        var o = ConvertToInt(one, bytesPerInt);
        var t = ConvertToInt(two, bytesPerInt);

        return Math.Abs(o) > Math.Abs(t);
    }

    private int ConvertToInt(byte[] bytes, int bytesPerInt)
    {
        switch (bytesPerInt)
        {
            case 2:
                return BitConverter.ToInt16(bytes, 0);
            case 3:
                return Int24.ToInt32(bytes, 0);
            case 4:
                return BitConverter.ToInt32(bytes, 0);
        }

        return 0;
    }

这很难解释,因此请在投票之前询问是否有疑问。

3 个答案:

答案 0 :(得分:1)

好吧,这是4个字节整数的简单实现:

private static int GetHighestValue(byte[] data)
{
  if (data.Length % 4 != 0)
     throw new ArgumentException();

  var maximum = 0, maximumAbs = 0;
  for (var i = 0; i < data.Length; i+=4)
  {
    var current = BitConverter.ToInt32 (data, i);
    var currentAbs = Math.Abs(current);

    if (currentAbs > maximumAbs)
    {
      maximum = current;
      maximumAbs = currentAbs;
    }
  }

  return maximum;
}

byte[]上以1百万个字节运行此代码,用Debug进行编译大约需要3毫秒。

我不知道您要针对哪种速度,但对于99%的情况,这应该没问题。


编辑:由于您更新了问题并包含示例代码,因此此更新为

这些是我在某些方面使您的代码慢于需要的地方:

  • 我们不需要在CombineChannels的每次迭代中都创建子数组。我们可以重写GetHighestValue,以使其以arrayoffsetamount作为参数。

  • 与其使用一种CombineChannels方法,不如将其拆分为不同的字节大小。例如CombineChannelsInt32CombineChannelsInt16 ...这样,方法本身可以将最大值存储为int32 / int16 / ...,而不必在每次迭代时都进行转换。

所以这是我们最终会得到如下结果的方法:

(byte[] combinedLeft, byte[] combinedRight) CombineChannels(byte[] leftChannel, byte[] rightChannel, int bytesPerInt, int countValuesToCombine)
{
  switch(bytesPerInt)
  {
    case 2:
      return CombineChannelsInt16(leftChannel, rightChannel, countValuesToCombine);
    case 3:
      return CombineChannelsInt24(leftChannel, rightChannel, countValuesToCombine);
    case 4:
      return CombineChannelsInt32(leftChannel, rightChannel, countValuesToCombine);
  }
}

(byte[] combinedLeft, byte[] combinedRight) CombineChannelsInt16(byte[] leftChannel, byte[] rightChannel, int countValuesToCombine);
(byte[] combinedLeft, byte[] combinedRight) CombineChannelsInt24(byte[] leftChannel, byte[] rightChannel, int countValuesToCombine);
(byte[] combinedLeft, byte[] combinedRight) CombineChannelsInt32(byte[] leftChannel, byte[] rightChannel, int countValuesToCombine);

short GetHighestValueInt16(byte[] bytes, int offset, int amount);
Int24 GetHighestValueInt24(byte[] bytes, int offset, int amount);
int GetHighestValueInt32(byte[] bytes, int offset, int amount);

答案 1 :(得分:0)

我做了一个返回最大索引的方法。它首先比较高字节,当相等时则比较低字节。使用更大的整数,它的运行速度甚至更快。

static int getMaxIndex(byte[] data, int byteLenght)
        {
            int MaxIndex = 0;
            int signMax = data[byteLenght - 1] >> 7;// get sign
            for (int i = byteLenght; i < data.Length; i += byteLenght)
            {
                int step = byteLenght - 1;
                int compResult = 0;

                while (compResult == 0 && step > -1)
                {
                    if (step == byteLenght -1)
                    {
                        int signData = data[i + step] >> 7;

                        compResult = signData - signMax;
                        if (compResult == 0) compResult = data[MaxIndex + step] & 127 - data[i + step] & 127;
                    }
                    else compResult = data[MaxIndex + step] - data[i + step];
                    if (compResult < 0)
                    {
                        MaxIndex = i;
                        signMax = data[MaxIndex + step] >> 7;
                    }
                    step--;
                }
            }
            return MaxIndex;
        }

答案 2 :(得分:0)

正如已经提到过几次,请避免在阅读内容中出现“ if”语句;只需为读取Int16Int24Int32做一个单独的功能,然后预先选择要使用的功能即可。

我个人会使用System.IO.BinaryReader;它已经包含用于从流中读取整数的函数,并且与BitConverter(从技术上取决于系统字节序)不同,BinaryReader实际上可以保证读取的值是little-endian。它在MSDN规范中。

这是使用BinaryReader的基本功能,以Int32为例。在此版本中,我让EndOfStreamException负责结局。他们说异常抛出/处理是一项繁重的操作,但是在这种情况下,它替换了两次读取之间的 lot 检查,因此可能是有道理的。

您可以通过对流指针进行实​​际检查来替换while (true)来适应这种情况。要么只是根据输入字节数组的长度检查ms.Position,要么跟踪自己变量中的位置,就可以在每一步中增加读取的字节数。

public static Int32 GetHighestValueInt32(Byte[] bytes)
{
    Int32 maxval = 0;
    try
    {
        using (MemoryStream ms = new MemoryStream(bytes))
        using (BinaryReader reader = new BinaryReader(ms))
        {
            while (true)
            {
                // Clear highest bit so the value's always a positive Int32.
                Int32 val = (Int32)(reader.ReadUInt32() & 0x7FFFFFFF);
                if (val > maxval)
                    maxval = val;
            }
        }
    }
    catch (EndOfStreamException ex)
    {
        // Finished reading!
    }
    return maxval;
}

对于Int16,实际的行val应该简单地替换为

Int16 val = (Int16)(reader.ReadUInt16() & 0x7FFF);

同样,maxval和返回类型也应更改为Int16

但是,

BinaryReader本身无法从流中读取Int24。但是,解决方法并不难。您可以简单地使用Int32并将其下移8位,然后手动调整流指针以补偿两个额外的读取字节:

while (true)
{
    Int32 val = (Int32)((reader.ReadUInt32() >> 8) & 0x7FFFFF);
    ms.Position -= 2;
    if (val > maxval)
        maxval = val;
}