将 byte[] 数组的元素组合成 16 位数字
Combining elements of a byte[] array into 16-bit numbers
这是音乐调谐器应用程序的代码摘录。创建一个 byte[] 数组,将音频数据读入缓冲区数组,然后 for 循环遍历缓冲区并组合索引 n
、n+1
处的值以创建一个包含 16-位数是长度的一半。
byte[] buffer = new byte[2*1200];
targetDataLine.read(buffer, 0, buffer.length)
for ( int i = 0; i < n; i+=2 ) {
int value = (short)((buffer[i]&0xFF) | ((buffer[i+1]&0xFF) << 8)); //**Don't understand**
a[i >> 1] = value;
}
到目前为止,我拥有的是:
从 a different SO post 中,我了解到存储在较大类型中的每个字节都必须是 &
和 0xFF,因为它转换为 32 位数字。我猜前导 24 位用 1
s 填充(虽然我不知道为什么它没有用零填充......不会以 1
s 开头改变数字的值?000000000010
(2)毕竟和111111110010
(-14)不一样,所以0xff
的目的是只抓最后8位(也就是整个字节) ).
当buffer[i+1]
左移8位时,这使得当OR
ing时,buffer[i+1]
的八位在最有效位置,并且 buffer[i]
中的八位在最低有效八位中。我们最终得到一个形式为 buffer[i+1] + buffer[i]
的 16 位数字。 (我正在使用 +
,但我知道它更接近串联。)
首先,我们为什么 OR
ing buffer[i] | buffer[i+1] << 8
?这似乎破坏了原始声音信息,除非我们以相同的方式将其拉回;虽然我知道 OR
会将它们组合成一个值,但我看不出该值在以后的计算中有何用处或用途。以后访问此数据的唯一方法是作为其文字值:
diff += Math.abs(a[j]-a[i+j];
如果我有 101
和 111
,加在一起我应该得到 12,或者 1100
。然而 101 | 111 << 3
给出 111101
,它等于 61。我最接近的理解是 101
(5) |
111000
(56) 是与添加 5+56=61
相同。但是顺序很重要——反过来 101 <<3 | 111
是完全不同的。我真的不明白数据如何在以这种方式进行 OR 时仍然有用。
我遇到的另一个问题是,因为Java使用有符号字节,所以第八位不表示值,而是表示符号。如果我 OR
ing 两个二进制有符号数,那么在生成的 16 位数字中,2⁷ 处的位现在充当值而不是占位符。如果我在 运行 和 OR
之前有一个负字节,那么在我的最终值 post-operation 中,它现在会错误地表现得好像原始数字中有一个正数 2⁷。 0xff
没有去掉这个,因为它保留了第八个带符号的字节,所以这不应该成为问题吗?
例如,1111
(-1) 和 0101
,当进行或运算时,可能会得到 01011111
。但是 1111
不代表 POSITIVE 1111
,它代表签名版本;但在最终答案中,它现在充当正 2³。
更新:我标记了已接受的答案,但是我花了一些额外的时间来找出我哪里出错了。对于将来可能阅读本文的任何人:
就签名而言,我的代码使用了签名字节。我对为什么这不会搞砸的唯一猜测是因为 收到的所有值可能都是正号。除了这没有意义外,给定波形的幅度从 [-1,1] 变化。我将尝试解决这个问题。如果有负号,这里代码的实现似乎不会在 ORing 时删除 1
,所以我怀疑它不会对计算产生太大影响(假设我们正在处理非常大的值(diff +=
意味着 diff
会非常大——一些额外的 1
不应该影响结果给定的代码和它所依赖的比较。 所以这个完全错了。我想了想,其实很简单——这是一个问题的唯一原因是因为我不了解大端,然后一旦我读到它,我就误解了到底是怎么回事它已实现。在下一个要点中解释了 Endian-ness。
关于位的排列顺序,破坏声音等,我使用的代码设置bigEndian=false
,意思是字节顺序从最低有效字节到最重要的字节。因此,合并 buffer
的两个索引需要采用 second 索引,将其位放在第一位,然后将 first 索引作为第二个(所以我们现在处于大端字节顺序)。我遇到的问题之一是 "endian-ness" 决定 位 顺序的印象。我以为 10010101
大端会变成 10101001
小端。事实证明并非如此——每个字节中的位都保持原来的顺序;不同之处在于 字节 是有序的 "backward"。所以 10110101 111000001
big-endian 变成 11100001 10110101
-- 每个字节内的位顺序相同;但是,不同的字节顺序。
- 最后,我不确定为什么,但接受的答案是正确的:
targetDataLine.read()
可能仅将位放入字节数组(不仅在我的代码中,而且在所有 Java 中使用 targetDataLine 的代码——read()
只接受目标 var 是字节数组的参数),但数据实际上是一个 short
分成两个 byte
。正是因为这个原因,每两个指数必须结合在一起。
- 回到签名的话题,现在应该很明显为什么这不是问题。这是我现在在代码中的注释,它更连贯地解释了所有这些 ^ 之前解释的内容:
<pre>/* The Javadoc explains that the targetDataLine will only read to a byte-typed array.
However, because the sample size is 16-bit, it is actually storing 16-bit numbers
there (shorts), auto-parsing them every eight bits. Additionally, because it is storing
them in little-endian, bits [2^0,2^7] are stored in index[i] in normal order (powers <code>76543210
)
while bits [2^8,2^15] are stored in index[i+1]. So, together they currently read as [7-6-5-4-3-2-1-0 15-14-13-12-11-10-9-8],
which is a problem. In the next for loop, we take care of this and re-organize the bytes by swapping every pair (remember the bits are ok, but the bytes are out of order).
Also, although the array is signed, this will not matter when we combine bytes, because the sign-bit (2^15) will be placed
back at the beginning like it normally is; although 2^7 currently exists as the most significant bit in its byte,
it is not a sign-indicating bit,
because it is really the middle of the short which was split. */
这是将低字节第一字节顺序的输入字节流组合到内部字节顺序的短裤流。
符号扩展更多的是原始字节流的符号编码问题。如果原始字节流是无符号的(编码值从 0 到 255),则克服了 java 将值视为有符号时的不良影响。所以有根据的猜测是外部字节 strem 编码无符号字节。
判断代码是否合理需要有关正在处理的外部编码以及使用的内部编码的信息。例如。 (疯狂的猜测可能完全错误!):读作 coud 的两个字节垃圾属于立体声编码的 2 个通道,为了便于内部处理,它们被放入单个短片中。您应该查看正在读取的编码以及应用程序中转换后数据的使用。
这是音乐调谐器应用程序的代码摘录。创建一个 byte[] 数组,将音频数据读入缓冲区数组,然后 for 循环遍历缓冲区并组合索引 n
、n+1
处的值以创建一个包含 16-位数是长度的一半。
byte[] buffer = new byte[2*1200];
targetDataLine.read(buffer, 0, buffer.length)
for ( int i = 0; i < n; i+=2 ) {
int value = (short)((buffer[i]&0xFF) | ((buffer[i+1]&0xFF) << 8)); //**Don't understand**
a[i >> 1] = value;
}
到目前为止,我拥有的是:
从 a different SO post 中,我了解到存储在较大类型中的每个字节都必须是
&
和 0xFF,因为它转换为 32 位数字。我猜前导 24 位用1
s 填充(虽然我不知道为什么它没有用零填充......不会以1
s 开头改变数字的值?000000000010
(2)毕竟和111111110010
(-14)不一样,所以0xff
的目的是只抓最后8位(也就是整个字节) ).当
buffer[i+1]
左移8位时,这使得当OR
ing时,buffer[i+1]
的八位在最有效位置,并且buffer[i]
中的八位在最低有效八位中。我们最终得到一个形式为buffer[i+1] + buffer[i]
的 16 位数字。 (我正在使用+
,但我知道它更接近串联。)
首先,我们为什么 OR
ing buffer[i] | buffer[i+1] << 8
?这似乎破坏了原始声音信息,除非我们以相同的方式将其拉回;虽然我知道 OR
会将它们组合成一个值,但我看不出该值在以后的计算中有何用处或用途。以后访问此数据的唯一方法是作为其文字值:
diff += Math.abs(a[j]-a[i+j];
如果我有 101
和 111
,加在一起我应该得到 12,或者 1100
。然而 101 | 111 << 3
给出 111101
,它等于 61。我最接近的理解是 101
(5) |
111000
(56) 是与添加 5+56=61
相同。但是顺序很重要——反过来 101 <<3 | 111
是完全不同的。我真的不明白数据如何在以这种方式进行 OR 时仍然有用。
我遇到的另一个问题是,因为Java使用有符号字节,所以第八位不表示值,而是表示符号。如果我 OR
ing 两个二进制有符号数,那么在生成的 16 位数字中,2⁷ 处的位现在充当值而不是占位符。如果我在 运行 和 OR
之前有一个负字节,那么在我的最终值 post-operation 中,它现在会错误地表现得好像原始数字中有一个正数 2⁷。 0xff
没有去掉这个,因为它保留了第八个带符号的字节,所以这不应该成为问题吗?
例如,1111
(-1) 和 0101
,当进行或运算时,可能会得到 01011111
。但是 1111
不代表 POSITIVE 1111
,它代表签名版本;但在最终答案中,它现在充当正 2³。
更新:我标记了已接受的答案,但是我花了一些额外的时间来找出我哪里出错了。对于将来可能阅读本文的任何人:
就签名而言,我的代码使用了签名字节。我对为什么这不会搞砸的唯一猜测是因为
收到的所有值可能都是正号。除了这没有意义外,给定波形的幅度从 [-1,1] 变化。我将尝试解决这个问题。如果有负号,这里代码的实现似乎不会在 ORing 时删除所以这个完全错了。我想了想,其实很简单——这是一个问题的唯一原因是因为我不了解大端,然后一旦我读到它,我就误解了到底是怎么回事它已实现。在下一个要点中解释了 Endian-ness。1
,所以我怀疑它不会对计算产生太大影响(假设我们正在处理非常大的值(diff +=
意味着diff
会非常大——一些额外的1
不应该影响结果给定的代码和它所依赖的比较。关于位的排列顺序,破坏声音等,我使用的代码设置
bigEndian=false
,意思是字节顺序从最低有效字节到最重要的字节。因此,合并buffer
的两个索引需要采用 second 索引,将其位放在第一位,然后将 first 索引作为第二个(所以我们现在处于大端字节顺序)。我遇到的问题之一是 "endian-ness" 决定 位 顺序的印象。我以为10010101
大端会变成10101001
小端。事实证明并非如此——每个字节中的位都保持原来的顺序;不同之处在于 字节 是有序的 "backward"。所以10110101 111000001
big-endian 变成11100001 10110101
-- 每个字节内的位顺序相同;但是,不同的字节顺序。- 最后,我不确定为什么,但接受的答案是正确的:
targetDataLine.read()
可能仅将位放入字节数组(不仅在我的代码中,而且在所有 Java 中使用 targetDataLine 的代码——read()
只接受目标 var 是字节数组的参数),但数据实际上是一个short
分成两个byte
。正是因为这个原因,每两个指数必须结合在一起。 - 回到签名的话题,现在应该很明显为什么这不是问题。这是我现在在代码中的注释,它更连贯地解释了所有这些 ^ 之前解释的内容:
<pre>/* The Javadoc explains that the targetDataLine will only read to a byte-typed array.
However, because the sample size is 16-bit, it is actually storing 16-bit numbers
there (shorts), auto-parsing them every eight bits. Additionally, because it is storing
them in little-endian, bits [2^0,2^7] are stored in index[i] in normal order (powers <code>76543210
)
while bits [2^8,2^15] are stored in index[i+1]. So, together they currently read as [7-6-5-4-3-2-1-0 15-14-13-12-11-10-9-8],
which is a problem. In the next for loop, we take care of this and re-organize the bytes by swapping every pair (remember the bits are ok, but the bytes are out of order).
Also, although the array is signed, this will not matter when we combine bytes, because the sign-bit (2^15) will be placed
back at the beginning like it normally is; although 2^7 currently exists as the most significant bit in its byte,
it is not a sign-indicating bit,
because it is really the middle of the short which was split. */
这是将低字节第一字节顺序的输入字节流组合到内部字节顺序的短裤流。
符号扩展更多的是原始字节流的符号编码问题。如果原始字节流是无符号的(编码值从 0 到 255),则克服了 java 将值视为有符号时的不良影响。所以有根据的猜测是外部字节 strem 编码无符号字节。
判断代码是否合理需要有关正在处理的外部编码以及使用的内部编码的信息。例如。 (疯狂的猜测可能完全错误!):读作 coud 的两个字节垃圾属于立体声编码的 2 个通道,为了便于内部处理,它们被放入单个短片中。您应该查看正在读取的编码以及应用程序中转换后数据的使用。