交错立体声PCM线性Int16大端音频是什么样的?

时间:2015-08-20 20:53:50

标签: java audio pcm audioformat interleave

我知道网上有很多资源可以解释如何对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};
}

格式

如果有人想了解有关音频格式的更多信息,以下是我所拥有的一切。

  • 格式为PCM 2通道交错大端线性int16
  • 采样率为44100
  • 每个short []缓冲区的短路数为2048
  • 每个short []缓冲区的帧数为1024
  • 每个数据包的帧数为1

4 个答案:

答案 0 :(得分:7)

  

我明白我的音频将有两个频道,因此样本将以[左] [右] [左] [右]的格式存储...我不明白的是这究竟是什么装置

交错式PCM数据按通道顺序存储每个通道一个样本,然后再进入下一个样本。 PCM 由每个通道的一组样本组成。如果你有左右声道的立体声音频,那么每个声道的一个样本组成一个帧。

  • 第0帧:[左侧样本] [右侧样本]
  • 第1帧:[左侧样本] [右侧样本]
  • 第2帧:[左侧样本] [右侧样本]
  • 第3帧:[左侧样本] [右侧样本]
  • 等...

每个样本都是瞬时压力的测量和数字量化。也就是说,如果每个样本有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文件,也就是说:

  • 2个频道
  • 采样率44100 kHz
  • 在16位有符号整数上量化的每个幅度样本

我说几乎是因为大端内容通常用于AIFF文件(Mac世界),而不是WAVE文件(PC世界)。我不知道如何处理Java中的字节顺序,所以我将把这一部分留给你。

关于如何存储样本非常简单:

  • 每个样本采用16位(整数从-32768到+32767)
  • 如果通道是交错的:(L,1),(R,1),(L,2),(R,2),...,(L,n),(R,n)
  • 如果通道不是:(L,1),(L,2),...,(L,n),(R,1),(R,2),...,(R,n )

然后要提供音频回调,通常需要提供32位浮点,范围从-1到+1。也许这就是你的算法中可能缺少某些东西的地方。将整数除以32768(2 ^(16-1))应该使它听起来像预期的那样。

答案 2 :(得分:1)

让我们先从一些术语中解脱出来

  • 频道是单声道的样本流。该术语不一定意味着样本在数据流中是连续的。
  • frame 是一组共事事件样本。对于立体声音频(例如L&amp; R声道),帧包含两个样本。
  • 数据包是1帧或更多帧,通常是系统可以同时处理的最小帧数。对于PCM音频,数据包通常包含1帧,但对于压缩音频,它会更大。
  • 交错是通常用于立体声音频的术语,其中数据流由连续的音频帧组成。因此流看起来像L1R1L2R2L3R3 ...... LnRn

存在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