PCM data flow之六:Frames & Periods

PCM data flow之六:Frames & Periods

分类: ALSA 335人阅读 评论(8) 收藏 举报

前面三章分析了codec、platform、machine驱动的各个组成部分及其注册过程,这三者都是硬件设备相关的,大家应该对音频物理链路有了初步的认知。

往后章节的内容主要集中在pcm native,这是pcm数据流的中间层:往上是与用户态接口的交互,实现音频数据在用户态和内核态之间的拷贝;往下是触发codec、platform、machine的操作函数,实现音频数据在dma_buffer <-> cpu_dai <-> codec之间的传输。

之中会涉及到dma buffer的管理,这需要对音频数据相关概念有一定的了解。因此本章说明下音频数据的几个重要概念:

·          Sample:样本长度,音频数据最基本的单位,常见的有8位和16位。

·          Channel:声道数,分为单声道mono和立体声stereo。

·          Frame:帧,构成一个完整的声音单元,Frame = Sample * channel。

·          Rate:又称Sample rate,采样率,即每秒的采样次数,针对帧而言。

·          Interleaved:交错模式,一种音频数据的记录方式,在交错模式下,数据以连续桢的形式存放,即首先记录完桢1的左声道样本和右声道样本(假设为立体声),再开始桢2的记录。而在非交错模式下,首先记录的是一个周期内所有桢的左声道样本,再记录右声道样本,数据是以连续通道的方式存储。多数情况下使用交错模式。

·          Period size:周期,每次硬件中断处理音频数据的帧数,对于音频设备的数据读写,以此为单位。

·          Buffer size:数据缓冲区大小,这里特指runtime的buffer size,而不是snd_pcm_hardware定义的buffer_bytes_max。一般来说Buffer size =period_size * period_count,period_count相当于处理完一个buffer数据所需的硬件中断次数。

下面一张图直观的表示buffer/period/frame/sample之间的关系:


敏感的读者会察觉到Period和Buffer size在PCM数据搬运中扮演着非常重要角色。下面引用两段来自alsa官网对Period的解释:


Period

The interval between interrupts from the hardware. This defines the input latency, since the CPU will not have any idea that there is data waiting until the audio interface interrupts it.

The audio interface has a"pointer" that marks the current position for read/write in its h/w buffer. The pointer circles around the buffer as long as the interface is running.

Typically, there are an integral number of periods per traversal of the h/w buffer, but not always. There is at least one card (ymfpci) that generates interrupts at a fixed rate independent of the buffer size (which can be changed), resulting in some "odd" effects compared to more traditional designs.

Note: h/w generally defines the interrupt in frames, though not always.

Alsa's period size setting will affect how much work the CPU does. if you set the period size low, there will be more interrupts and the work that is done every interrupt will be done more often.So, if you don't care about low latency, set the period size large as possible and you'll have more CPU cycles for other things. The defaults that ALSA provides are in the middle of the range, typically.

(from an old AlsaDevel thread[1], quoting Paul Davis)

Retrieved from "http://alsa.opensrc.org/Period"


FramesPeriods

A frame is equivalent of one sample being played, irrespective of the number of channels or the number of bits.e.g.

 * 1 frame of a Stereo 48khz 16bit PCM stream is 4 bytes.

 * 1 frame of a 5.1 48khz 16bit PCM stream is 12 bytes.

A period is the number of frames in between each hardware interrupt. The poll() will return once a period.

The buffer is a ring buffer. The buffer size always has to be greater than one period size. Commonly this is 2*period size, but some hardware can do 8 periods per buffer. It is also possible for the buffer size to not be an integer multiple of the period size.

Now, if the hardware has been set to 48000Hz , 2 periods, of 1024 frames each, making a buffer size of 2048 frames.The hardware will interrupt 2 times per buffer. ALSA will endeavor to keep the buffer as full as possible. Once the first period of samples has been played,the third period of samples is transfered into the space the first one occupied while the second period of samples is being played. (normal ring buffer behaviour).

Here is an alternative example for the above discussion.

Say we want to work with a stereo,16-bit, 44.1 KHz stream, one-way (meaning, either in playback or in capture direction). Then we have:

 * 'stereo' = number of channels: 2

 * 1 analog sample is represented with 16 bits = 2 bytes

 * 1 frame represents 1 analog sample from all channels; here we have 2 channels, and so:

     * 1 frame = (num_channels) * (1 sample in bytes) = (2 channels) * (2 bytes (16 bits) per sample) = 4 bytes (32 bits)

 * To sustain 2 x 44.1 KHz analog rate - the system must be capable of data transfer rate, in Bytes/sec:

     * Bps_rate = (num_channels) * (1 sample in bytes) * (analog_rate) = (1 frame) * (analog_rate) = ( 2 channels ) * (2 bytes/sample) * (44100 samples/sec) = 2*2*44100 = 176400 Bytes/sec

Now, if ALSA would interrupt each second, asking for bytes - we'd need to have 176400 bytes ready for it (at end of each second), in order to sustain analog 16-bit stereo @ 44.1Khz.

 * If it would interrupt each half a second, correspondingly for the same stream we'd need 176400/2 = 88200 bytes ready, at each interrupt;

 * if the interrupt hits each 100 ms, we'd need to have 176400*(0.1/1) =17640 bytes ready, at each interrupt.

We can control when this PCM interrupt is generated, by setting a period size, which is set in frames.

 * Thus, if we set 16-bit stereo @ 44.1Khz, and the period_size to 4410 frames => (for 16-bit stereo @ 44.1Khz, 1 frame equals 4 bytes - so 4410 frames equal 4410*4 = 17640 bytes) => an interrupt will be generated each 17640 bytes - that is, each 100 ms.

 * Correspondingly, buffer_size should be at least 2*period_size = 2*4410= 8820 frames (or 8820*4 = 35280 bytes).

It seems (writing-an-alsa-driver.pdf),however, that it is the ALSA runtime that decides on the actual buffer_size and period_size, depending on: the requested number of channels, and their respective properties (rate and sampling resolution) - as well as the parameters set in the snd_pcm_hardware structure (in the driver).

Also, the following quote may be relevant, from http://mailman.alsa-project.org/pipermail/alsa-devel/2007-April/000474.html:

> > The "frame"represents the unit, 1 frame = # channels x sample_bytes.

> > In your case, 1 frame corresponds to 2 channels x 16 bits = 4 bytes.

> >

> > The periods is the number of periods in a ring-buffer.  In OSS, called

> > as "fragments".

> >

> > So,

> >  - buffer_size = period_size * periods

> >  - period_bytes = period_size *bytes_per_frame

> >  - bytes_per_frame = channels *bytes_per_sample

> >

>

> I still don't understand what'period_size' and a 'period' is?

The "period" defines thefrequency to update the status, usually viathe invokation of interrupts.  The "period_size" defines the framesizes corresponding to the "period time".  This term corresponds to the "fragmentsize" on OSS.  On major sound hardwares, a ring-buffer isdivided to several parts and an irq is issued on each boundary. The period_sizedefines the size of this chunk.

On some hardwares, the irq iscontrolled on the basis of a timer.  Inthis case, the period is defined as the timer frequency to invoke an irq.

Retrieved from "http://alsa-project.org/main/index.php/FramesPeriods"


这里不做翻译了,简单说下Frame和Period要点:

·          Frame:帧,构成一个完整的声音单元,它的大小等于量化位数sample_bits * 声道数channels。

·           Peroid:周期大小,即每次dma硬件中断处理音频数据的帧数。如果周期设定得较大,则单次处理的数据较多,这意味着单位时间内硬件中断的次数较少,CPU也就有更多时间处理其他任务,功耗也更低,但这样也带来一个显著的弊端——数据处理的时延(latency)会增大。后面章节会针对这点介绍Android的deep_buffer和low_latency两种处理方式。


再说说period bytes,对于dma处理来说,它关心的是数据大小,而不管period size和period count,因此有个转换关系:

period_bytes = period_size * sample_bits* channels / 8

代码如下:

  1. static inline unsigned int  
  2. params_period_bytes(const struct snd_pcm_hw_params *p)  
  3. {  
  4.     return (params_period_size(p) *  
  5.         snd_pcm_format_physical_width(params_format(p)) *  
  6.         params_channels(p)) / 8;  

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这个错误提示是因为在使用xtfrm函数时,不能对数据框进行转换。xtfrm函数是用于对数据进行排序和比较的,但是数据框不支持这种操作。如果需要对数据框进行排序或比较,可以使用sort函数或者order函数。 ### 回答2: 在R编程中,当我们使用xtfrm函数对数据进行排序时,可能会遇到“in xtfrm.data.frame(x) : cannot xtfrm data frames”的错误提示。 这个错误提示的意思是xtfrm函数无法对数据框进行排序,因为它们包含多种数据类型,如字符、数值和逻辑等。xtfrm函数只能对具有单一数据类型的向量进行排序。 为解决这个问题,我们可以将数据框转换为单一数据类型的向量,例如将每列的数据合并到一个向量中,然后使用该向量进行排序。这种方法可以使用unlist函数。 另一种解决方法是使用dplyr或tidyr包中的函数进行数据转换和排序。这些包提供了丰富的数据处理功能,可以轻松地处理各种数据类型,避免了xtfrm函数对数据框的限制。 最后,我们需要注意,在使用xtfrm函数对数据框进行排序时,如果数据框中的列具有不同的数据类型,则结果可能会不正确。因此,在进行排序时,应确保数据框中的所有列具有相同的数据类型。 ### 回答3: xtfrm.data.frame(x) : cannot xtfrm data frames这个错误信息是在处理R语言的数据分析时常见的错误。一般情况下,如果在对一个数据框进行排序时出现这个错误信息,那么原因可能是数据框中存在非数字类型的变量,如字符型、逻辑型或因子型变量。 在进行排序操作时,R语言需要对数据框的每一列进行比较,以决定如何排序。然而,非数字型变量无法比较,因此会出现错误信息。 要解决这个问题,可以用以下两种方式: 1. 删除非数字型变量:在进行排序操作前,将非数字型变量删除,只对数字型变量进行排序。可以使用函数select()来选择需要的变量。 例如: library(dplyr) data %>% select_if(is.numeric) %>% arrange(desc(x)) 这个例子中,用select_if函数选择了数值型变量,并按照变量x倒序排序。 2. 将非数字型变量转换为数字型变量:将非数字型变量转换为数字型变量后,可以继续对数据框进行排序操作。可以使用函数as.numeric()将字符型变量转换为数字型变量,使用函数as.integer()将逻辑型或因子型变量转换为整数型。 例如: data$y <- as.numeric(data$y) data %>% arrange(desc(x), y) 这个例子中,将变量y由字符型转换为数字型后,对x和y进行降序排列。 总之,在进行数据框的排序操作前,一定要注意数据框中的变量类型,以免出现xtfrm data frames的错误信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值