当前位置: 首页 > news >正文

网站的建站风格/网上推广平台有哪些

网站的建站风格,网上推广平台有哪些,刷流水兼职日结1000,饰品网站建设策划书Linux 用户空间应用程序通过声卡驱动程序(一般牵涉到多个设备驱动程序)和 Linux 内核 ALSA 框架导出的 PCM 设备文件,如 /dev/snd/pcmC0D0c 和 /dev/snd/pcmC0D0p 等,与 Linux 内核音频设备驱动程序和音频硬件进行数据传递。PCM 设…

Linux 用户空间应用程序通过声卡驱动程序(一般牵涉到多个设备驱动程序)和 Linux 内核 ALSA 框架导出的 PCM 设备文件,如 /dev/snd/pcmC0D0c/dev/snd/pcmC0D0p 等,与 Linux 内核音频设备驱动程序和音频硬件进行数据传递。PCM 设备文件的文件操作定义 (位于 sound/core/pcm_native.c) 如下:

const struct file_operations snd_pcm_f_ops[2] = {{.owner =		THIS_MODULE,.write =		snd_pcm_write,.write_iter =		snd_pcm_writev,.open =			snd_pcm_playback_open,.release =		snd_pcm_release,.llseek =		no_llseek,.poll =			snd_pcm_poll,.unlocked_ioctl =	snd_pcm_ioctl,.compat_ioctl = 	snd_pcm_ioctl_compat,.mmap =			snd_pcm_mmap,.fasync =		snd_pcm_fasync,.get_unmapped_area =	snd_pcm_get_unmapped_area,},{.owner =		THIS_MODULE,.read =			snd_pcm_read,.read_iter =		snd_pcm_readv,.open =			snd_pcm_capture_open,.release =		snd_pcm_release,.llseek =		no_llseek,.poll =			snd_pcm_poll,.unlocked_ioctl =	snd_pcm_ioctl,.compat_ioctl = 	snd_pcm_ioctl_compat,.mmap =			snd_pcm_mmap,.fasync =		snd_pcm_fasync,.get_unmapped_area =	snd_pcm_get_unmapped_area,}
};

大多数情况下,音频设备会同时提供播放和录制功能,用于播放和录制的 PCM 设备文件是一起导出的,播放和录制的 PCM 设备文件的文件操作也是一起定义的,其中索引为 SNDRV_PCM_STREAM_PLAYBACK,也就是 0 的文件操作用于播放,索引为 SNDRV_PCM_STREAM_CAPTURE,也就是 1 的文件操作用于录制。

Linux 用户空间有 alsa-libtinyalsa 等库可用于与 Linux 内核 ALSA 框架交互。alsa-libtinyalsa 等库主要通过 ioctl 命令与 Linux 内核 ALSA 框架交互。PCM 设备文件的 ioctl 操作 snd_pcm_ioctl() 函数定义 (位于 sound/core/pcm_native.c) 如下:

static int snd_pcm_common_ioctl(struct file *file,struct snd_pcm_substream *substream,unsigned int cmd, void __user *arg)
{struct snd_pcm_file *pcm_file = file->private_data;int res;if (PCM_RUNTIME_CHECK(substream))return -ENXIO;res = snd_power_wait(substream->pcm->card, SNDRV_CTL_POWER_D0);if (res < 0)return res;switch (cmd) {case SNDRV_PCM_IOCTL_PVERSION:return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;case SNDRV_PCM_IOCTL_INFO:return snd_pcm_info_user(substream, arg);case SNDRV_PCM_IOCTL_TSTAMP:	/* just for compatibility */return 0;case SNDRV_PCM_IOCTL_TTSTAMP:return snd_pcm_tstamp(substream, arg);case SNDRV_PCM_IOCTL_USER_PVERSION:if (get_user(pcm_file->user_pversion,(unsigned int __user *)arg))return -EFAULT;return 0;case SNDRV_PCM_IOCTL_HW_REFINE:return snd_pcm_hw_refine_user(substream, arg);case SNDRV_PCM_IOCTL_HW_PARAMS:return snd_pcm_hw_params_user(substream, arg);case SNDRV_PCM_IOCTL_HW_FREE:return snd_pcm_hw_free(substream);case SNDRV_PCM_IOCTL_SW_PARAMS:return snd_pcm_sw_params_user(substream, arg);case SNDRV_PCM_IOCTL_STATUS32:return snd_pcm_status_user32(substream, arg, false);case SNDRV_PCM_IOCTL_STATUS_EXT32:return snd_pcm_status_user32(substream, arg, true);case SNDRV_PCM_IOCTL_STATUS64:return snd_pcm_status_user64(substream, arg, false);case SNDRV_PCM_IOCTL_STATUS_EXT64:return snd_pcm_status_user64(substream, arg, true);case SNDRV_PCM_IOCTL_CHANNEL_INFO:return snd_pcm_channel_info_user(substream, arg);case SNDRV_PCM_IOCTL_PREPARE:return snd_pcm_prepare(substream, file);case SNDRV_PCM_IOCTL_RESET:return snd_pcm_reset(substream);case SNDRV_PCM_IOCTL_START:return snd_pcm_start_lock_irq(substream);case SNDRV_PCM_IOCTL_LINK:return snd_pcm_link(substream, (int)(unsigned long) arg);case SNDRV_PCM_IOCTL_UNLINK:return snd_pcm_unlink(substream);case SNDRV_PCM_IOCTL_RESUME:return snd_pcm_resume(substream);case SNDRV_PCM_IOCTL_XRUN:return snd_pcm_xrun(substream);case SNDRV_PCM_IOCTL_HWSYNC:return snd_pcm_hwsync(substream);case SNDRV_PCM_IOCTL_DELAY:{snd_pcm_sframes_t delay;snd_pcm_sframes_t __user *res = arg;int err;err = snd_pcm_delay(substream, &delay);if (err)return err;if (put_user(delay, res))return -EFAULT;return 0;}case __SNDRV_PCM_IOCTL_SYNC_PTR32:return snd_pcm_ioctl_sync_ptr_compat(substream, arg);case __SNDRV_PCM_IOCTL_SYNC_PTR64:return snd_pcm_sync_ptr(substream, arg);
#ifdef CONFIG_SND_SUPPORT_OLD_APIcase SNDRV_PCM_IOCTL_HW_REFINE_OLD:return snd_pcm_hw_refine_old_user(substream, arg);case SNDRV_PCM_IOCTL_HW_PARAMS_OLD:return snd_pcm_hw_params_old_user(substream, arg);
#endifcase SNDRV_PCM_IOCTL_DRAIN:return snd_pcm_drain(substream, file);case SNDRV_PCM_IOCTL_DROP:return snd_pcm_drop(substream);case SNDRV_PCM_IOCTL_PAUSE:return snd_pcm_pause_lock_irq(substream, (unsigned long)arg);case SNDRV_PCM_IOCTL_WRITEI_FRAMES:case SNDRV_PCM_IOCTL_READI_FRAMES:return snd_pcm_xferi_frames_ioctl(substream, arg);case SNDRV_PCM_IOCTL_WRITEN_FRAMES:case SNDRV_PCM_IOCTL_READN_FRAMES:return snd_pcm_xfern_frames_ioctl(substream, arg);case SNDRV_PCM_IOCTL_REWIND:return snd_pcm_rewind_ioctl(substream, arg);case SNDRV_PCM_IOCTL_FORWARD:return snd_pcm_forward_ioctl(substream, arg);}pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd);return -ENOTTY;
}static long snd_pcm_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
{struct snd_pcm_file *pcm_file;pcm_file = file->private_data;if (((cmd >> 8) & 0xff) != 'A')return -ENOTTY;return snd_pcm_common_ioctl(file, pcm_file->substream, cmd,(void __user *)arg);
}

alsa-libtinyalsa 等库主要通过 SNDRV_PCM_IOCTL_WRITEI_FRAMESSNDRV_PCM_IOCTL_READI_FRAMESSNDRV_PCM_IOCTL_WRITEN_FRAMESSNDRV_PCM_IOCTL_READN_FRAMES 等四个 ioctl 命令与 Linux 内核 ALSA 框架交换数据。这几个 ioctl 命令主要的区别在于,传递的数据的格式不同,SNDRV_PCM_IOCTL_READI_FRAMESSNDRV_PCM_IOCTL_WRITEI_FRAMES 命令用于读写 interleaved 格式的音频数据,SNDRV_PCM_IOCTL_READN_FRAMESSNDRV_PCM_IOCTL_WRITEN_FRAMES 命令则用于读写 noninterleaved 格式的音频数据。

SNDRV_PCM_IOCTL_READI_FRAMESSNDRV_PCM_IOCTL_WRITEI_FRAMES 命令由 snd_pcm_xferi_frames_ioctl() 函数处理,SNDRV_PCM_IOCTL_READN_FRAMESSNDRV_PCM_IOCTL_WRITEN_FRAMES 命令由 snd_pcm_xfern_frames_ioctl() 函数处理,这两个函数定义 (位于 sound/core/pcm_native.c) 如下:

static int snd_pcm_xferi_frames_ioctl(struct snd_pcm_substream *substream,struct snd_xferi __user *_xferi)
{struct snd_xferi xferi;struct snd_pcm_runtime *runtime = substream->runtime;snd_pcm_sframes_t result;if (runtime->status->state == SNDRV_PCM_STATE_OPEN)return -EBADFD;if (put_user(0, &_xferi->result))return -EFAULT;if (copy_from_user(&xferi, _xferi, sizeof(xferi)))return -EFAULT;if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);elseresult = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);if (put_user(result, &_xferi->result))return -EFAULT;return result < 0 ? result : 0;
}static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream,struct snd_xfern __user *_xfern)
{struct snd_xfern xfern;struct snd_pcm_runtime *runtime = substream->runtime;void *bufs;snd_pcm_sframes_t result;if (runtime->status->state == SNDRV_PCM_STATE_OPEN)return -EBADFD;if (runtime->channels > 128)return -EINVAL;if (put_user(0, &_xfern->result))return -EFAULT;if (copy_from_user(&xfern, _xfern, sizeof(xfern)))return -EFAULT;bufs = memdup_user(xfern.bufs, sizeof(void *) * runtime->channels);if (IS_ERR(bufs))return PTR_ERR(bufs);if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)result = snd_pcm_lib_writev(substream, bufs, xfern.frames);elseresult = snd_pcm_lib_readv(substream, bufs, xfern.frames);kfree(bufs);if (put_user(result, &_xfern->result))return -EFAULT;return result < 0 ? result : 0;
}

snd_pcm_xferi_frames_ioctl()snd_pcm_xfern_frames_ioctl() 函数将参数复制到内核空间栈上,并通过 snd_pcm_lib_write()snd_pcm_lib_read()snd_pcm_lib_writev()snd_pcm_lib_readv() 四个函数执行读写操作。这四个函数定义 (位于 include/sound/pcm.h) 如下:

static inline snd_pcm_sframes_t
snd_pcm_lib_write(struct snd_pcm_substream *substream,const void __user *buf, snd_pcm_uframes_t frames)
{return __snd_pcm_lib_xfer(substream, (void __force *)buf, true, frames, false);
}static inline snd_pcm_sframes_t
snd_pcm_lib_read(struct snd_pcm_substream *substream,void __user *buf, snd_pcm_uframes_t frames)
{return __snd_pcm_lib_xfer(substream, (void __force *)buf, true, frames, false);
}static inline snd_pcm_sframes_t
snd_pcm_lib_writev(struct snd_pcm_substream *substream,void __user **bufs, snd_pcm_uframes_t frames)
{return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames, false);
}static inline snd_pcm_sframes_t
snd_pcm_lib_readv(struct snd_pcm_substream *substream,void __user **bufs, snd_pcm_uframes_t frames)
{return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames, false);
}

所有的音频数据传递操作最终都由 __snd_pcm_lib_xfer() 函数完成。__snd_pcm_lib_xfer() 函数定义 (位于 sound/core/pcm_lib.c) 如下:

typedef int (*pcm_transfer_f)(struct snd_pcm_substream *substream,int channel, unsigned long hwoff,void *buf, unsigned long bytes);typedef int (*pcm_copy_f)(struct snd_pcm_substream *, snd_pcm_uframes_t, void *,snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f);. . . . . .
/* sanity-check for read/write methods */
static int pcm_sanity_check(struct snd_pcm_substream *substream)
{struct snd_pcm_runtime *runtime;if (PCM_RUNTIME_CHECK(substream))return -ENXIO;runtime = substream->runtime;if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area))return -EINVAL;if (runtime->status->state == SNDRV_PCM_STATE_OPEN)return -EBADFD;return 0;
}static int pcm_accessible_state(struct snd_pcm_runtime *runtime)
{switch (runtime->status->state) {case SNDRV_PCM_STATE_PREPARED:case SNDRV_PCM_STATE_RUNNING:case SNDRV_PCM_STATE_PAUSED:return 0;case SNDRV_PCM_STATE_XRUN:return -EPIPE;case SNDRV_PCM_STATE_SUSPENDED:return -ESTRPIPE;default:return -EBADFD;}
}/* update to the given appl_ptr and call ack callback if needed;* when an error is returned, take back to the original value*/
int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream,snd_pcm_uframes_t appl_ptr)
{struct snd_pcm_runtime *runtime = substream->runtime;snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr;int ret;if (old_appl_ptr == appl_ptr)return 0;runtime->control->appl_ptr = appl_ptr;if (substream->ops->ack) {ret = substream->ops->ack(substream);if (ret < 0) {runtime->control->appl_ptr = old_appl_ptr;return ret;}}trace_applptr(substream, old_appl_ptr, appl_ptr);return 0;
}/* the common loop for read/write data */
snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,void *data, bool interleaved,snd_pcm_uframes_t size, bool in_kernel)
{struct snd_pcm_runtime *runtime = substream->runtime;snd_pcm_uframes_t xfer = 0;snd_pcm_uframes_t offset = 0;snd_pcm_uframes_t avail;pcm_copy_f writer;pcm_transfer_f transfer;bool nonblock;bool is_playback;int err;err = pcm_sanity_check(substream);if (err < 0)return err;is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;if (interleaved) {if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&runtime->channels > 1)return -EINVAL;writer = interleaved_copy;} else {if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)return -EINVAL;writer = noninterleaved_copy;}if (!data) {if (is_playback)transfer = fill_silence;elsereturn -EINVAL;} else if (in_kernel) {if (substream->ops->copy_kernel)transfer = substream->ops->copy_kernel;elsetransfer = is_playback ?default_write_copy_kernel : default_read_copy_kernel;} else {if (substream->ops->copy_user)transfer = (pcm_transfer_f)substream->ops->copy_user;elsetransfer = is_playback ?default_write_copy : default_read_copy;}if (size == 0)return 0;nonblock = !!(substream->f_flags & O_NONBLOCK);snd_pcm_stream_lock_irq(substream);err = pcm_accessible_state(runtime);if (err < 0)goto _end_unlock;runtime->twake = runtime->control->avail_min ? : 1;if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)snd_pcm_update_hw_ptr(substream);/** If size < start_threshold, wait indefinitely. Another* thread may start capture*/if (!is_playback &&runtime->status->state == SNDRV_PCM_STATE_PREPARED &&size >= runtime->start_threshold) {err = snd_pcm_start(substream);if (err < 0)goto _end_unlock;}avail = snd_pcm_avail(substream);while (size > 0) {snd_pcm_uframes_t frames, appl_ptr, appl_ofs;snd_pcm_uframes_t cont;if (!avail) {if (!is_playback &&runtime->status->state == SNDRV_PCM_STATE_DRAINING) {snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);goto _end_unlock;}if (nonblock) {err = -EAGAIN;goto _end_unlock;}runtime->twake = min_t(snd_pcm_uframes_t, size,runtime->control->avail_min ? : 1);err = wait_for_avail(substream, &avail);if (err < 0)goto _end_unlock;if (!avail)continue; /* draining */}frames = size > avail ? avail : size;appl_ptr = READ_ONCE(runtime->control->appl_ptr);appl_ofs = appl_ptr % runtime->buffer_size;cont = runtime->buffer_size - appl_ofs;if (frames > cont)frames = cont;if (snd_BUG_ON(!frames)) {err = -EINVAL;goto _end_unlock;}if (!atomic_inc_unless_negative(&runtime->buffer_accessing)) {err = -EBUSY;goto _end_unlock;}snd_pcm_stream_unlock_irq(substream);err = writer(substream, appl_ofs, data, offset, frames,transfer);snd_pcm_stream_lock_irq(substream);atomic_dec(&runtime->buffer_accessing);if (err < 0)goto _end_unlock;err = pcm_accessible_state(runtime);if (err < 0)goto _end_unlock;appl_ptr += frames;if (appl_ptr >= runtime->boundary)appl_ptr -= runtime->boundary;err = pcm_lib_apply_appl_ptr(substream, appl_ptr);if (err < 0)goto _end_unlock;offset += frames;size -= frames;xfer += frames;avail -= frames;if (is_playback &&runtime->status->state == SNDRV_PCM_STATE_PREPARED &&snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {err = snd_pcm_start(substream);if (err < 0)goto _end_unlock;}}_end_unlock:runtime->twake = 0;if (xfer > 0 && err >= 0)snd_pcm_update_state(substream, runtime);snd_pcm_stream_unlock_irq(substream);return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
}
EXPORT_SYMBOL(__snd_pcm_lib_xfer);

__snd_pcm_lib_xfer() 函数的执行过程如下:

  1. 调用 pcm_sanity_check() 函数执行合理性检查,pcm_sanity_check() 函数检查 DMA buffer 和状态。音频设备驱动程序需要保证,在 __snd_pcm_lib_xfer() 函数执行时,DMA buffer 已经准备好。

  2. 根据传入的参数,即数据格式是 interleaved 还是 noninterleaved,播放还是录制,数据缓冲区是否为空,操作是由内核空间代码发起还是由用户空间代码发起等,选择 writertransfer 操作。writertransfer 操作主要用于在传入的数据缓冲区和 DMA buffer 之间传递数据。

  3. 检查 runtime 的状态,当状态不为 SNDRV_PCM_STATE_PREPAREDSNDRV_PCM_STATE_RUNNINGSNDRV_PCM_STATE_PAUSED 时,报错并返回。

  4. runtime 状态为 SNDRV_PCM_STATE_RUNNING 时,调用 snd_pcm_update_hw_ptr(substream) 函数更新 hw_ptr。

  5. 对于音频录制,如果 runtime 状态为 SNDRV_PCM_STATE_PREPARED,且请求录制的帧数超过发起阈值,则调用 snd_pcm_start(substream) 函数触发录制。

  6. 调用 snd_pcm_avail(substream) 函数获得 DMA buffer 中以帧为单位的可用空间大小。
    用户空间应用程序和 Linux 内核设备驱动程序对 DMA buffer 的访问是典型的读者-写者模型。对于播放来说,用户空间应用程序向 DMA buffer 写入数据,Linux 内核设备驱动程序从 DMA buffer 读取数据并发送给硬件设备。对于录制来说,Linux 内核设备驱动程序从硬件设备获得数据并写入 DMA buffer,用户空间应用程序从 DMA buffer 读取数据并作进一步处理。
    这里的可用空间大小是站在用户空间应用程序的视角来说的,即对于播放来说,可用空间大小指还可以向 DMA buffer 写入的数据量,对于录制来说,则指可以从 DMA buffer 读取的数据量。

  7. 通过一个循环,处理所有的数据传递请求。
    (1). 当 DMA buffer 的可用空间为 0 时,则等待直到可用空间大于 0。
    (2). 计算拷贝的数据量大小,拷贝的目的内存在 DMA buffer 中的位置等。
    (3). 调用 writertransfer 操作在 DMA buffer 和传入的缓冲区之间传递数据,appl_ofs 作为 hwoff 参数传给 writer 操作。
    (4). 检查 runtime 的状态。
    (5). 更新并调用 pcm_lib_apply_appl_ptr() 函数应用新的 appl_ptr。
    (6). 根据传递的帧数,更新偏移量、要传递的帧数、已经传递的帧数和 DMA buffer 中可用的空间大小。
    (7). 对于音频播放,如果 runtime 状态为 SNDRV_PCM_STATE_PREPARED,且 DMA buffer 中可以播放的数据量超过发起阈值,则调用 snd_pcm_start(substream) 函数触发播放。

不同情况下,用于在传入的数据缓冲区和 DMA buffer 之间传递数据的 writertransfer 操作定义 (位于 sound/core/pcm_lib.c) 如下:

/* calculate the target DMA-buffer position to be written/read */
static void *get_dma_ptr(struct snd_pcm_runtime *runtime,int channel, unsigned long hwoff)
{return runtime->dma_area + hwoff +channel * (runtime->dma_bytes / runtime->channels);
}/* default copy_user ops for write; used for both interleaved and non- modes */
static int default_write_copy(struct snd_pcm_substream *substream,int channel, unsigned long hwoff,void *buf, unsigned long bytes)
{if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff),(void __user *)buf, bytes))return -EFAULT;return 0;
}/* default copy_kernel ops for write */
static int default_write_copy_kernel(struct snd_pcm_substream *substream,int channel, unsigned long hwoff,void *buf, unsigned long bytes)
{memcpy(get_dma_ptr(substream->runtime, channel, hwoff), buf, bytes);return 0;
}/* fill silence instead of copy data; called as a transfer helper* from __snd_pcm_lib_write() or directly from noninterleaved_copy() when* a NULL buffer is passed*/
static int fill_silence(struct snd_pcm_substream *substream, int channel,unsigned long hwoff, void *buf, unsigned long bytes)
{struct snd_pcm_runtime *runtime = substream->runtime;if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)return 0;if (substream->ops->fill_silence)return substream->ops->fill_silence(substream, channel,hwoff, bytes);snd_pcm_format_set_silence(runtime->format,get_dma_ptr(runtime, channel, hwoff),bytes_to_samples(runtime, bytes));return 0;
}/* default copy_user ops for read; used for both interleaved and non- modes */
static int default_read_copy(struct snd_pcm_substream *substream,int channel, unsigned long hwoff,void *buf, unsigned long bytes)
{if (copy_to_user((void __user *)buf,get_dma_ptr(substream->runtime, channel, hwoff),bytes))return -EFAULT;return 0;
}/* default copy_kernel ops for read */
static int default_read_copy_kernel(struct snd_pcm_substream *substream,int channel, unsigned long hwoff,void *buf, unsigned long bytes)
{memcpy(buf, get_dma_ptr(substream->runtime, channel, hwoff), bytes);return 0;
}/* call transfer function with the converted pointers and sizes;* for interleaved mode, it's one shot for all samples*/
static int interleaved_copy(struct snd_pcm_substream *substream,snd_pcm_uframes_t hwoff, void *data,snd_pcm_uframes_t off,snd_pcm_uframes_t frames,pcm_transfer_f transfer)
{struct snd_pcm_runtime *runtime = substream->runtime;/* convert to bytes */hwoff = frames_to_bytes(runtime, hwoff);off = frames_to_bytes(runtime, off);frames = frames_to_bytes(runtime, frames);return transfer(substream, 0, hwoff, data + off, frames);
}/* call transfer function with the converted pointers and sizes for each* non-interleaved channel; when buffer is NULL, silencing instead of copying*/
static int noninterleaved_copy(struct snd_pcm_substream *substream,snd_pcm_uframes_t hwoff, void *data,snd_pcm_uframes_t off,snd_pcm_uframes_t frames,pcm_transfer_f transfer)
{struct snd_pcm_runtime *runtime = substream->runtime;int channels = runtime->channels;void **bufs = data;int c, err;/* convert to bytes; note that it's not frames_to_bytes() here.* in non-interleaved mode, we copy for each channel, thus* each copy is n_samples bytes x channels = whole frames.*/off = samples_to_bytes(runtime, off);frames = samples_to_bytes(runtime, frames);hwoff = samples_to_bytes(runtime, hwoff);for (c = 0; c < channels; ++c, ++bufs) {if (!data || !*bufs)err = fill_silence(substream, c, hwoff, NULL, frames);elseerr = transfer(substream, c, hwoff, *bufs + off,frames);if (err < 0)return err;}return 0;
}

transfer 操作用于执行数据拷贝。根据发起数据传递的来源是内核还是用户空间应用程序,以及是播放还是录制,transfer 操作有四个默认实现,分别为 default_write_copy_kernel()default_read_copy_kernel()default_write_copy()default_read_copy(),这几个函数根据 channelhwoff 通过 get_dma_ptr() 函数获得 DMA 指针,并通过内核提供的 copy_from_user()memcpy()copy_to_user() 等函数在传入的数据缓冲区和 DMA buffer 之间传递数据。

get_dma_ptr() 函数可以看到,当数据格式为 noninterleaved 时,在 DMA buffer 中,各个 channel 的数据的布局。

writer 操作用于为数据拷贝做准备,它将帧为单位的偏移量和大小等数据拷贝参数转为以字节为单位,并调用 transfer 操作执行数据拷贝。writer 操作有两个默认实现,分别为 interleaved_copy()noninterleaved_copy()。当数据格式为 noninterleaved 时,writer 操作分别拷贝各个 channel 的数据,传入的音频数据保存在多个不同的缓冲区中,每个通道一个,音频数据通过指针数组的方式传递。

writertransfer 操作中用到的指向 DMA buffer 的 hwoff 偏移量,由 runtime->control->appl_ptr 计算而来,在关于 DMA buffer 访问的读者-写者模型中,对于播放,runtime->control->appl_ptr 是写指针,对于录制,它是读指针。

snd_pcm_avail() 函数用于获得 DMA buffer 中,用户空间应用程序视角的,以帧为单位的可用空间大小,Linux 内核还提供了另一个函数 snd_pcm_hw_avail(),用于获得音频硬件设备驱动视角的,以帧为单位的可用空间大小,这两个函数定义 (位于 sound/core/pcm_local.h) 如下:

static inline snd_pcm_uframes_t
snd_pcm_avail(struct snd_pcm_substream *substream)
{if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)return snd_pcm_playback_avail(substream->runtime);elsereturn snd_pcm_capture_avail(substream->runtime);
}static inline snd_pcm_uframes_t
snd_pcm_hw_avail(struct snd_pcm_substream *substream)
{if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)return snd_pcm_playback_hw_avail(substream->runtime);elsereturn snd_pcm_capture_hw_avail(substream->runtime);
}

这两个函数根据流的类型是播放还是录制,将操作分派给另外四个函数 snd_pcm_playback_avail()snd_pcm_capture_avail()snd_pcm_playback_hw_avail()snd_pcm_capture_hw_avail()snd_pcm_playback_avail() 等函数定义 (位于 include/sound/pcm.h) 如下:

/*** snd_pcm_playback_avail - Get the available (writable) space for playback* @runtime: PCM runtime instance** Result is between 0 ... (boundary - 1)*/
static inline snd_pcm_uframes_t snd_pcm_playback_avail(struct snd_pcm_runtime *runtime)
{snd_pcm_sframes_t avail = runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr;if (avail < 0)avail += runtime->boundary;else if ((snd_pcm_uframes_t) avail >= runtime->boundary)avail -= runtime->boundary;return avail;
}/*** snd_pcm_capture_avail - Get the available (readable) space for capture* @runtime: PCM runtime instance** Result is between 0 ... (boundary - 1)*/
static inline snd_pcm_uframes_t snd_pcm_capture_avail(struct snd_pcm_runtime *runtime)
{snd_pcm_sframes_t avail = runtime->status->hw_ptr - runtime->control->appl_ptr;if (avail < 0)avail += runtime->boundary;return avail;
}/*** snd_pcm_playback_hw_avail - Get the queued space for playback* @runtime: PCM runtime instance*/
static inline snd_pcm_sframes_t snd_pcm_playback_hw_avail(struct snd_pcm_runtime *runtime)
{return runtime->buffer_size - snd_pcm_playback_avail(runtime);
}/*** snd_pcm_capture_hw_avail - Get the free space for capture* @runtime: PCM runtime instance*/
static inline snd_pcm_sframes_t snd_pcm_capture_hw_avail(struct snd_pcm_runtime *runtime)
{return runtime->buffer_size - snd_pcm_capture_avail(runtime);
}

关于 DMA buffer 访问的读者-写者模型中,对于播放,runtime->status->hw_ptr 是读指针,(runtime->control->appl_ptr - runtime->status->hw_ptr) 是已经写入,但还未播放的数据量,对于录制,runtime->status->hw_ptr 是写指针。

__snd_pcm_lib_xfer() 函数调用 snd_pcm_start() 函数触发数据传递开始执行,这个函数定义 (位于 sound/core/pcm_native.c) 如下:

/** start callbacks*/
static int snd_pcm_pre_start(struct snd_pcm_substream *substream,snd_pcm_state_t state)
{struct snd_pcm_runtime *runtime = substream->runtime;if (runtime->status->state != SNDRV_PCM_STATE_PREPARED)return -EBADFD;if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&!snd_pcm_playback_data(substream))return -EPIPE;runtime->trigger_tstamp_latched = false;runtime->trigger_master = substream;return 0;
}static int snd_pcm_do_start(struct snd_pcm_substream *substream,snd_pcm_state_t state)
{if (substream->runtime->trigger_master != substream)return 0;return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
}static void snd_pcm_undo_start(struct snd_pcm_substream *substream,snd_pcm_state_t state)
{if (substream->runtime->trigger_master == substream)substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
}static void snd_pcm_post_start(struct snd_pcm_substream *substream,snd_pcm_state_t state)
{struct snd_pcm_runtime *runtime = substream->runtime;snd_pcm_trigger_tstamp(substream);runtime->hw_ptr_jiffies = jiffies;runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) / runtime->rate;runtime->status->state = state;if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&runtime->silence_size > 0)snd_pcm_playback_silence(substream, ULONG_MAX);snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
}static const struct action_ops snd_pcm_action_start = {.pre_action = snd_pcm_pre_start,.do_action = snd_pcm_do_start,.undo_action = snd_pcm_undo_start,.post_action = snd_pcm_post_start
};/*** snd_pcm_start - start all linked streams* @substream: the PCM substream instance** Return: Zero if successful, or a negative error code.* The stream lock must be acquired before calling this function.*/
int snd_pcm_start(struct snd_pcm_substream *substream)
{return snd_pcm_action(&snd_pcm_action_start, substream,SNDRV_PCM_STATE_RUNNING);
}

这里调用音频硬件设备驱动程序的 trigger 操作,触发发起硬件的数据传输。

相关文章:

Linux 内核音频数据传递主要流程

Linux 用户空间应用程序通过声卡驱动程序&#xff08;一般牵涉到多个设备驱动程序&#xff09;和 Linux 内核 ALSA 框架导出的 PCM 设备文件&#xff0c;如 /dev/snd/pcmC0D0c 和 /dev/snd/pcmC0D0p 等&#xff0c;与 Linux 内核音频设备驱动程序和音频硬件进行数据传递。PCM 设…...

torch.device函数

torch.device 是 PyTorch 中用于表示计算设备&#xff08;如CPU或GPU&#xff09;的类。它允许你在代码中指定你希望在哪个设备上执行张量和模型操作&#xff0c;本文主要介绍了 torch.device 函数的用法和功能。 本文主要包含以下内容&#xff1a; 1.创建设备对象2.将张量和模…...

火车头采集器AI伪原创【php源码】

大家好&#xff0c;本文将围绕python作业提交什么文件展开说明&#xff0c;python123怎么提交作业是一个很多人都想弄明白的事情&#xff0c;想搞清楚python期末作业程序需要先了解以下几个事情。 火车头采集ai伪原创插件截图&#xff1a; I have a python project, whose fold…...

Python中常见的6种数据类型

数字&#xff08;Numbers&#xff09;&#xff1a;数字类型用于表示数值&#xff0c;包括整数&#xff08;int&#xff09;和浮点数&#xff08;float&#xff09;。 字符串&#xff08;Strings&#xff09;&#xff1a;字符串类型用于表示文本&#xff0c;由一系列字符组成。字…...

消息队列项目(2)

我们使用 SQLite 来进行对 Exchange, Queue, Binding 的硬盘保存 对 Message 就保存在硬盘的文本中 SQLite 封装 这里是在 application.yaml 中来引进对 SQLite 的封装 spring:datasource:url: jdbc:sqlite:./data/meta.dbusername:password:driver-class-name: org.sqlite.…...

解决MAC M1处理器运行Android protoc时出现的错误

Protobuf是Google开发的一种新的结构化数据存储格式&#xff0c;一般用于结构化数据的序列化&#xff0c;也就是我们常说的数据序列化。这个序列化协议非常轻量级和高效&#xff0c;并且是跨平台的。目前&#xff0c;它支持多种主流语言&#xff0c;比传统的XML、JSON等方法更具…...

C#使用SnsSharp实现鼠标键盘钩子,实现全局按键响应

gitee下载地址&#xff1a;https://gitee.com/linsns/snssharp 一、键盘事件&#xff0c;使用SnsKeyboardHook 按键事件共有3个&#xff1a; KeyDown(按键按下) KeyUp(按键松开) KeyPress(按键按下并松开) 以KeyDown事件为例&#xff0c;使用代码如下&…...

Zookeeper基础操作

搭建Zookeeper服务器 windows下部署 下载地址: https://mirrors.cloud.tencent.com/apache/zookeeper/zookeeper-3.7.1/ 修改配置文件 打开conf目录&#xff0c;将 zoo_sample.cfg复制一份&#xff0c;命名为 zoo.cfg打开 zoo.cfg&#xff0c;修改 dataDir路径&#xff0c…...

【CSS】说说响应式布局

目录 一、是什么 二、怎么实现 1、媒体查询 2、百分比 3、vw/vh 4、小结 三、总结 一、是什么 响应式设计简而言之&#xff0c;就是一个网站能够兼容多个终端——而不是为每个终端做一个特定的版本。 响应式网站常见特点&#xff1a; 同时适配PC 平板 手机等…...

数据结构 | 利用二叉堆实现优先级队列

目录 一、二叉堆的操作 二、二叉堆的实现 2.1 结构属性 2.2 堆的有序性 2.3 堆操作 队列有一个重要的变体&#xff0c;叫作优先级队列。和队列一样&#xff0c;优先级队列从头部移除元素&#xff0c;不过元素的逻辑顺序是由优先级决定的。优先级最高的元素在最前&#xff…...

Javascript怎样阻止事件传播?

在 JavaScript 中&#xff0c;可以使用事件对象的方法来阻止事件传播。事件传播指的是当一个元素上触发了一个事件&#xff0c;该事件会在事件流中传播到父元素或祖先元素&#xff0c;从而影响到它们。 事件传播有三个阶段&#xff1a;捕获阶段、目标阶段和冒泡阶段。阻止事件…...

web-csrf

目录 CSRF与XSS的区别&#xff1a; get请求 原理&#xff1a; pikachu为例 post请求 pikachu为例 CSRF与XSS的区别&#xff1a; CSRF是借用户的权限完成攻击&#xff0c;攻击者并没有拿到用户的权限&#xff0c;而XSS是直接盗取到了用户的权限 get请求 原理&#xff1a;…...

数据结构—图的存储结构

6.图 回顾&#xff1a;数据的逻辑结构 集合——数据元素间除 “同属于一个集合” 外&#xff0c;无其他关系。 线性结构——一个对一个&#xff0c;如线性表、栈、队列 树形结构——一个对多个&#xff0c;如树 图形结构——多个对多个&#xff0c;如图 6.1图的定义和术语 图:…...

Vue3 中 setup,ref 和 reactive 的理解

setup Vue3中使用了Composition API这种写法&#xff0c;使得所有的组合API函数都在此使用, 只在初始化时执行一次。 函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用 ref 作用&#xff1a;定义一个数据的响应式 语法&#xff1a;const xxx ref(initValue) 一般用来…...

BL302嵌入式ARM控制器进行SQLite3数据库操作的实例演示

本文主要讲述了在钡铼技术BL302嵌入式arm控制器上运行 SQLite3 数据库的命令示例。SQLite3 是一个轻型的嵌入式数据库&#xff0c;不需要安装数据库服务器进程&#xff0c;占用资源低且处理速度快。 首先&#xff0c;需要将对应版本的 SQLite3 文件复制到设备的 /usr/ 目录下&…...

C++ 多线程:std::future

std::future std::future 简介示例1博客引用来源 std::future 简介 我们前面介绍的std::thread 是C11中提供异步创建多线程的工具&#xff0c;只能是异步运行任务&#xff0c;却无法获取任务执行的结果&#xff0c;一般都是依靠全局对象&#xff0c;全局对象在多线程下是及其不…...

断路器回路电阻试验

试验目的 断路器回路电阻主要取决于断路器动、 静触头的接触电阻, 其大小直接影响正常 运行时的发热情况及切断短路电流的性能, 是反应安装检修质量的重要数据。 试验设备 回路电阻测试仪 厂家&#xff1a; 湖北众拓高试代销 试验接线 对于单断口的断路器, 通过断口两端的接线…...

Python中的CALL_FUNCTION指令

在Python字节码中&#xff0c;CALL_FUNCTION指令后跟的数字代表这次函数调用需要从栈上取出的参数的数量。具体来说&#xff0c;这个数字包括位置参数和关键字参数的数量。 这个数字的低两位表示位置参数的数量&#xff0c;然后每两位表示一个关键字参数的数量。因此&#xff…...

微服务——es数据聚合+RestClient实现聚合

数据聚合 聚合的种类 DSL实现Bucket聚合 如图所示&#xff0c;设置了10个桶&#xff0c;那么就显示了数量最多的前10个桶&#xff0c;品牌含有7天酒店的有30家&#xff0c; 品牌含有如家的也有30家。 修改排序规则 限定聚合范围 DSL实现Metrics聚合 如下案例要求对不同的品…...

代码分析Java中的BIO与NIO

开发环境 OS&#xff1a;Win10&#xff08;需要开启telnet服务&#xff0c;或使用第三方远程工具&#xff09; Java版本&#xff1a;8 BIO 概念 BIO(Block IO)&#xff0c;即同步阻塞IO&#xff0c;特点为当客户端发起请求后&#xff0c;在服务端未处理完该请求之前&#xff…...

网络安全(黑客)工作篇

一、网络安全行业的就业前景如何&#xff1f; 网络安全行业的就业前景非常广阔和有吸引力。随着数字化、云计算、物联网和人工智能等技术的迅速发展&#xff0c;网络安全的需求持续增长。以下是网络安全行业就业前景的一些关键因素&#xff1a; 高需求&#xff1a;随着互联网的…...

zookeeper入门学习

zookeeper入门学习 zookeeper应用场景 分布式协调组件 客户端第一次请求发给服务器2&#xff0c;将flag值修改为false&#xff0c;第二次请求被负载均衡到服务器1&#xff0c;访问到的flag也会是false 一旦有节点发生改变&#xff0c;就会通知所有监听方改变自己的值&#…...

VirtualEnv 20.24.0 发布

导读VirtualEnv 20.24.0 现已发布&#xff0c;VirtualEnv 用于在一台机器上创建多个独立的 Python 运行环境&#xff0c;可隔离项目之间的第三方包依赖&#xff0c;为部署应用提供方便&#xff0c;把开发环境的虚拟环境打包到生产环境即可&#xff0c;不需要在服务器上再折腾一…...

LabVIEW开发高压航空航天动力系统爬电距离的测试

LabVIEW开发高压航空航天动力系统爬电距离的测试 更多电动飞机MEA技术将发电&#xff0c;配电和用电集成到一个统一的系统中&#xff0c;提高了飞机的可靠性和可维护性。更多的电动飞机使用更多的电能来用电动替代品取代液压和气动系统。对车载电力的需求不断增加&#xff0c;…...

【论文阅读】基于深度学习的时序异常检测——Anomaly Transformer

系列文章链接 数据基础&#xff1a;多维时序数据集简介 论文一&#xff1a;2022 Anomaly Transformer&#xff1a;异常分数预测 论文二&#xff1a;2022 TransAD&#xff1a;异常分数预测 论文链接&#xff1a;Anomaly Transformer.pdf 代码链接&#xff1a;https://github.co…...

Java并发总结

1.创建线程三种方式 Runnable.Callable接口使用继承Thread类的方式创建多线程Runnable 和Callable区别 Callable规定&#xff08;重写&#xff09;的方法是call()&#xff0c;Runnable规定&#xff08;重写&#xff09;的方法是run()。Callable的任务执行后可返回值&#xff0…...

视频汇聚平台EasyCVR视频广场侧边栏支持拖拽

为了提升用户体验以及让平台的操作更加符合用户使用习惯&#xff0c;我们在EasyCVR v3.3版本中&#xff0c;支持面包屑侧边栏的广场视频、分组列表、收藏这三个模块拖拽排序&#xff0c;并且该操作在视频广场、视频调阅、电子地图、录像回放等页面均能支持。 TSINGSEE青犀视频…...

MyCat分片规则——范围分片、取模分片、一致性hash、枚举分片

1.范围分片 2.取模分片 范围分片和取模分片针对数字类型的字段可以&#xff0c;但是针对于字符串类型的字段时。这两种就不适用了。 3.一致性hash 4.枚举分片 默认节点指的是&#xff0c;如果我们向数据库表插入数据的时候&#xff0c;超出了这个枚举值&#xff0c;那么默认向…...

设计模式行为型——备忘录模式

目录 什么是备忘录模式 备忘录模式的实现 备忘录模式角色 备忘录模式类图 备忘录模式举例 备忘录模式代码实现 备忘录模式的特点 优点 缺点 使用场景 注意事项 实际应用 什么是备忘录模式 备忘录模式&#xff08;Memento Pattern&#xff09;又叫做快照模式&#x…...

Parquet存储的数据模型以及文件格式

文章目录 数据模型Parquet 的原子类型Parquet 的逻辑类型嵌套编码 Parquet文件格式 本文主要参考文献&#xff1a;Tom White. Hadoop权威指南. 第4版. 清华大学出版社, 2017.pages 363. Aapche Parquet是一种能有效存储嵌套数据的列式存储格式&#xff0c;在Spark中应用较多。 …...