我知道网上有很多资源可以解释如何对PCM数据进行解交织。在我目前的项目中,我已经查看了大部分内容......但我没有音频处理的背景,而且我很难找到完全这种常见形式的详细解释存储音频。
我明白我的音频将有两个频道,因此样本将以[左] [右] [左] [右]格式存储... 我不明白的是这究竟是什么意思。我还读过每个样本以[左MSB] [左LSB] [右MSB] [右LSB]的格式存储。这是否意味着每个16位整数实际上编码两个8位帧,或者每个16位整数是自己的帧,目的地是左声道还是右声道?
谢谢大家。感谢任何帮助。
方法上下文
具体来说,我要做的是将交错的short []转换为两个float [],每个float []代表左或右通道。我将用Java实现它。
public static float[][] deinterleaveAudioData(short[] interleavedData) {
//initialize the channel arrays
float[] left = new float[interleavedData.length / 2];
float[] right = new float[interleavedData.length / 2];
//iterate through the buffer
for (int i = 0; i < interleavedData.length; i++) {
//THIS IS WHERE I DON'T KNOW WHAT TO DO
}
//return the separated left and right channels
return new float[][]{left, right};
}
我目前的实施
我试过播放由此产生的音频。它非常接近,足够近以至于你能理解一首歌的单词,但显然仍然不是正确的方法。
public static float[][] deinterleaveAudioData(short[] interleavedData) {
//initialize the channel arrays
float[] left = new float[interleavedData.length / 2];
float[] right = new float[interleavedData.length / 2];
//iterate through the buffer
for (int i = 0; i < left.length; i++) {
left[i] = (float) interleavedData[2 * i];
right[i] = (float) interleavedData[2 * i + 1];
}
//return the separated left and right channels
return new float[][]{left, right};
}
格式
如果有人想了解有关音频格式的更多信息,以下是我所拥有的一切。
答案 0 :(得分:7)
我明白我的音频将有两个频道,因此样本将以[左] [右] [左] [右]的格式存储...我不明白的是这究竟是什么装置
交错式PCM数据按通道顺序存储每个通道一个样本,然后再进入下一个样本。 PCM 帧由每个通道的一组样本组成。如果你有左右声道的立体声音频,那么每个声道的一个样本组成一个帧。
每个样本都是瞬时压力的测量和数字量化。也就是说,如果每个样本有8位,则可以获得256个可能的压力精度级别。知道声波是......波浪......有山峰和山谷,我们希望能够测量距离中心的距离。因此,我们可以将中心定义为127左右,并从那里减去并加上(0到255,无符号),或者我们可以将这8位视为有符号(相同的值,只是它们的不同解释)并从-128到127。
每个样本使用8位和单通道(单声道)音频,我们每个样本使用一个字节,这意味着在44.1kHz采样的一秒音频正好使用了4410个字节的存储空间。
现在,让我们假设每个样本8位,但在立体声44.1.kHz。每隔一个字节将用于左边,而其他所有字节都用于R。
LRLRLRLRLRLRLRLRLRLRLR...
将其扩展到16位,每个样本有两个字节(样本设置为括号[
和]
,空格表示帧边界)
[LL][RR] [LL][RR] [LL][RR] [LL][RR] [LL][RR] [LL][RR]...
我还读过每个样本都以[左侧MSB] [左侧LSB] [右侧MSB] [右侧LSB]格式存储。
不一定。音频可以以任何字节顺序存储。小端是最常见的,但这不是一个神奇的规则。我确实认为所有频道总是顺序排列,而左前方在大多数情况下都是频道0。
这是否意味着每个16位整数实际上对两个8位帧进行编码,或者每个16位整数都是自己的帧,目的地是左声道还是右声道?
每个值(在这种情况下为16位整数)的目的地是单个通道。你永远不会将两个多字节值相互冲撞。
我希望这对你有所帮助。我无法运行您的代码但是根据您的描述,我怀疑您有一个字节序问题,而且您的样本并不是真正的大端。
答案 1 :(得分:2)
实际上,您正在处理音频CD质量的几乎典型的WAVE文件,也就是说:
我说几乎是因为大端内容通常用于AIFF文件(Mac世界),而不是WAVE文件(PC世界)。我不知道如何处理Java中的字节顺序,所以我将把这一部分留给你。
关于如何存储样本非常简单:
然后要提供音频回调,通常需要提供32位浮点,范围从-1到+1。也许这就是你的算法中可能缺少某些东西的地方。将整数除以32768(2 ^(16-1))应该使它听起来像预期的那样。
答案 2 :(得分:1)
让我们先从一些术语中解脱出来
存在big和little endian音频格式,并且取决于用例。但是,在系统之间交换数据时,这通常是一个问题 - 在处理或与操作系统音频组件连接时,您将始终使用本机字节顺序。
你没有说你是使用一个小端还是大端系统,但我怀疑它可能是前者。在这种情况下,您需要对样本进行字节反转。
虽然不是一成不变的,但使用浮点样本通常在-1.0<x<+1.0
范围内,因此您希望将样本除以1<<15
。当使用16位线性类型时,它们通常是带符号的。
负责字节交换和格式转换:
int s = (int) interleavedData[2 * i];
short revS = (short) (((s & 0xff) << 8) | ((s >> 8) & 0xff))
left[i] = ((float) revS) / 32767.0f;
答案 3 :(得分:0)
我遇到了一个类似的问题,即通过Spotify Android SDK的short[] frames
onAudioDataDelivered().
进行去交错
一年前,onAudioDelivered
的文档编写得很糟糕。见Github issue。他们使用更好的描述和更准确的参数名称更新了文档:
onAudioDataDelivered(short[] samples, int sampleCount, int sampleRate, int channels)
可能令人困惑的是samples.length
可以是4096.但是,它只包含sampleCount
个有效样本。如果您正在接收立体声音频,sampleCount = 2048
samples
数组中只有1024帧(每帧有两个样本)的音频!
因此,您需要更新您的实施,以确保您使用的是sampleCount
,而不是samples.length
。