From patchwork Thu Aug 17 11:59:49 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Takashi Sakamoto X-Patchwork-Id: 9905979 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1EEA26038C for ; Thu, 17 Aug 2017 12:04:20 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2764728841 for ; Thu, 17 Aug 2017 12:04:20 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1C4D7288BD; Thu, 17 Aug 2017 12:04:20 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EC76F28841 for ; Thu, 17 Aug 2017 12:04:18 +0000 (UTC) Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 1546626772E; Thu, 17 Aug 2017 14:00:35 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 961B4267729; Thu, 17 Aug 2017 14:00:31 +0200 (CEST) Received: from smtp-proxy003.phy.lolipop.jp (smtp-proxy003.phy.lolipop.jp [157.7.104.44]) by alsa0.perex.cz (Postfix) with ESMTP id 173A8266EE0 for ; Thu, 17 Aug 2017 14:00:13 +0200 (CEST) Received: from smtp-proxy003.phy.lolipop.lan (HELO smtp-proxy003.phy.lolipop.jp) (172.19.44.44) (smtp-auth username m12129643-o-takashi, mechanism plain) by smtp-proxy003.phy.lolipop.jp (qpsmtpd/0.82) with ESMTPA; Thu, 17 Aug 2017 21:00:10 +0900 Received: from 127.0.0.1 (127.0.0.1) by smtp-proxy003.phy.lolipop.jp (LOLIPOP-Fsecure); Thu, 17 Aug 2017 21:00:04 +0900 (JST) X-Virus-Status: clean(LOLIPOP-Fsecure) From: Takashi Sakamoto To: tiwai@suse.de, perex@perex.cz Date: Thu, 17 Aug 2017 20:59:49 +0900 Message-Id: <20170817120004.15326-9-o-takashi@sakamocchi.jp> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170817120004.15326-1-o-takashi@sakamocchi.jp> References: <20170817120004.15326-1-o-takashi@sakamocchi.jp> Cc: alsa-devel@alsa-project.org, clemens@ladisch.de Subject: [alsa-devel] [RFC][PATCH 08/23] aplay: add an implementation of aligner for multiple target X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP This commit adds support for aligner with 'multiple' target. This handles several files via 'container' functions. --- aplay/Makefile.am | 3 +- aplay/aligner-multiple.c | 337 +++++++++++++++++++++++++++++++++++++++++++++++ aplay/aligner.c | 17 ++- aplay/aligner.h | 4 + 4 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 aplay/aligner-multiple.c diff --git a/aplay/Makefile.am b/aplay/Makefile.am index 68f6411..79f1535 100644 --- a/aplay/Makefile.am +++ b/aplay/Makefile.am @@ -20,7 +20,8 @@ aplay_SOURCES = \ container-voc.c \ aligner.h \ aligner.c \ - aligner-single.c + aligner-single.c \ + aligner-multiple.c EXTRA_DIST = aplay.1 arecord.1 EXTRA_CLEAN = arecord diff --git a/aplay/aligner-multiple.c b/aplay/aligner-multiple.c new file mode 100644 index 0000000..437e3cc --- /dev/null +++ b/aplay/aligner-multiple.c @@ -0,0 +1,337 @@ +/* + * aligner-multiple.c - a muxer/demuxer for multiple containers. + * + * Copyright (c) 2017 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "aligner.h" + +struct multiple_state { + void (*align_frames)(void *frame_buf, unsigned int frame_count, + char **buf, unsigned int bytes_per_sample, + struct container_context *cntrs, + unsigned int cntr_count); + char **bufs; +}; + +static void align_to_i(void *frame_buf, unsigned int frame_count, + char **src_bufs, unsigned int bytes_per_sample, + struct container_context *cntrs, unsigned int cntr_count) +{ + char *dst = frame_buf; + char *src; + unsigned int dst_pos; + unsigned int src_pos; + struct container_context *cntr; + int i, j; + + /* + * src: first channel in each of interleaved buffers in containers => + * dst:interleaved. + */ + for (i = 0; i < cntr_count; ++i) { + src = src_bufs[i]; + cntr = cntrs + i; + + for (j = 0; j < frame_count; ++j) { + /* Use first src channel for each of dst channel. */ + src_pos = bytes_per_sample * cntr->samples_per_frame * j; + dst_pos = i + bytes_per_sample * cntr_count * j; + + memcpy(dst + dst_pos, src + src_pos, bytes_per_sample); + } + } +} + +static void align_to_n(void *frame_buf, unsigned int frame_count, + char **src_bufs, unsigned int bytes_per_sample, + struct container_context *cntrs, unsigned int cntr_count) +{ + char *dst = frame_buf; + char *src; + unsigned int dst_pos; + unsigned int src_pos; + struct container_context *cntr; + int i, j; + + /* + * src: first channel in each of interleaved buffers in containers => + * dst:noninterleaved. + */ + for (i = 0; i < cntr_count; ++i) { + src = src_bufs[i]; + cntr = cntrs + i; + + for (j = 0; j < frame_count; ++j) { + /* Use first src channel for each of dst channel. */ + src_pos = bytes_per_sample * cntr->samples_per_frame * j; + dst_pos = j + bytes_per_sample * cntr_count * i; + + memcpy(dst + dst_pos, src + src_pos, bytes_per_sample); + } + } +} + +static void align_from_i(void *frame_buf, unsigned int frame_count, + char **dst_bufs, unsigned int bytes_per_sample, + struct container_context *cntrs, + unsigned int cntr_count) +{ + char *src = frame_buf; + char *dst; + unsigned int src_pos; + unsigned int dst_pos; + struct container_context *cntr; + int i, j; + + for (i = 0; i < cntr_count; ++i) { + dst = dst_bufs[i]; + cntr = cntrs + i; + + for (j = 0; j < frame_count; ++j) { + /* Use first src channel for each of dst channel. */ + src_pos = bytes_per_sample * cntr_count * j + i; + dst_pos = bytes_per_sample * cntr->samples_per_frame * j; + + memcpy(dst + dst_pos, src + src_pos, bytes_per_sample); + } + } +} + +static void align_from_n(void *frame_buf, unsigned int frame_count, + char **dst_bufs, unsigned int bytes_per_sample, + struct container_context *cntrs, + unsigned int cntr_count) +{ + char *src = frame_buf; + char *dst; + unsigned int src_pos; + unsigned int dst_pos; + struct container_context *cntr; + int i, j; + + for (i = 0; i < cntr_count; ++i) { + dst = dst_bufs[i]; + cntr = cntrs + i; + + for (j = 0; j < frame_count; ++j) { + /* Use first src channel for each of dst channel. */ + src_pos = bytes_per_sample * cntr_count * i + j; + dst_pos = bytes_per_sample * cntr->samples_per_frame * j; + + memcpy(dst + dst_pos, src + src_pos, bytes_per_sample); + } + } +} + +static int multiple_pre_process(struct aligner_context *aligner, + struct container_context *cntrs, + unsigned int cntr_count) +{ + static const struct { + enum aligner_type type; + int access; + void (*align_frames)(void *frame_buf, unsigned int frame_count, + char **buf, unsigned int bytes_per_sample, + struct container_context *cntrs, + unsigned int cntr_count); + } entries[] = { + { + ALIGNER_TYPE_MUXER, + SND_PCM_ACCESS_RW_INTERLEAVED, + align_to_i, + }, + { + ALIGNER_TYPE_MUXER, + SND_PCM_ACCESS_RW_NONINTERLEAVED, + align_to_n, + }, + { + ALIGNER_TYPE_DEMUXER, + SND_PCM_ACCESS_MMAP_INTERLEAVED, + align_from_i, + }, + { + ALIGNER_TYPE_DEMUXER, + SND_PCM_ACCESS_MMAP_NONINTERLEAVED, + align_from_n, + }, + }; + struct multiple_state *state = aligner->private_data; + struct container_context *cntr; + int i; + + /* + * Additionally, format of samples in the containers should be the same + * as the format in PCM substream. + */ + for (i = 0; i < cntr_count; ++i) { + cntr = cntrs + i; + if (aligner->bytes_per_sample != cntr->bytes_per_sample) + return -EINVAL; + } + + state->align_frames = NULL; + for (i = 0; i < ARRAY_SIZE(entries); ++i) { + if (entries[i].type == aligner->type && + (entries[i].access == aligner->access)) { + state->align_frames = entries[i].align_frames; + break; + } + } + + if (state->align_frames) { + /* + * Furthermore, in demuxer case, each container should be + * configured to store one sample per frame. + */ + if (aligner->type == ALIGNER_TYPE_DEMUXER) { + for (i = 0; i < cntr_count; ++i) { + cntr = cntrs + i; + if (cntrs->samples_per_frame != 1) + return -EINVAL; + } + } + + state->bufs = calloc(cntr_count, sizeof(char *)); + if (state->bufs == NULL) + return -ENOMEM; + + for (i = 0; i < cntr_count; ++i) { + unsigned int bytes_per_buffer; + + /* + * Allocate intermediate buffer as the same size as a + * period for each of containers. + */ + cntr = cntrs + i; + + bytes_per_buffer = aligner->bytes_per_sample * + cntr->samples_per_frame * + aligner->frames_per_buffer; + + state->bufs[i] = malloc(bytes_per_buffer); + if (state->bufs[i] == NULL) + return -ENOMEM; + memset(state->bufs[i], 0, bytes_per_buffer); + } + } + + return 0; +} + +static int process_containers(char **src_bufs, unsigned int *frame_count, + struct container_context *cntrs, + unsigned int cntr_count) +{ + struct container_context *cntr; + char *src; + int i; + int err = 0; + + /* TODO: arrangement for *frame_count. */ + for (i = 0; i < cntr_count; ++i) { + cntr = &cntrs[i]; + src = src_bufs[i]; + + err = container_context_process_frames(cntr, src, frame_count); + if (err < 0) + break; + } + + return err; +} + +static int multiple_muxer_process_frames(struct aligner_context *aligner, + void *frame_buf, + unsigned int *frame_count, + struct container_context *cntrs, + unsigned int cntr_count) +{ + struct multiple_state *state = aligner->private_data; + char **src_bufs; + int err; + + /* + * If need to align PCM frames, process PCM frames to the intermediate + * buffer once. + */ + if (!state->align_frames) { + /* The most likely. */ + src_bufs = frame_buf; + } else { + src_bufs = state->bufs; + } + err = process_containers(src_bufs, frame_count, cntrs, cntr_count); + if (err < 0) + return err; + + /* Unlikely. */ + if (src_bufs != frame_buf && *frame_count > 0) { + state->align_frames(frame_buf, *frame_count, src_bufs, + aligner->bytes_per_sample, cntrs, + cntr_count); + } + + return 0; +} + +static int multiple_demuxer_process_frames(struct aligner_context *aligner, + void *frame_buf, + unsigned int *frame_count, + struct container_context *cntrs, + unsigned int cntr_count) +{ + struct multiple_state *state = aligner->private_data; + char **dst_bufs; + + /* + * If need to align PCM frames, process PCM frames to the intermediate + * buffer once. + */ + if (!state->align_frames) { + /* The most likely. */ + dst_bufs = frame_buf; + } else { + dst_bufs = state->bufs; + state->align_frames(frame_buf, *frame_count, dst_bufs, + aligner->bytes_per_sample, cntrs, + cntr_count); + } + + return process_containers(dst_bufs, frame_count, cntrs, cntr_count); +} + +static void multiple_post_process(struct aligner_context *aligner) +{ + struct multiple_state *state = aligner->private_data; + + if (state->bufs) { + if (state->bufs[0]) + free(state->bufs[0]); + free(state->bufs); + } + + state->bufs = NULL; + state->align_frames = NULL; +} + +const struct aligner_data aligner_muxer_multiple = { + .ops = { + .pre_process = multiple_pre_process, + .process_frames = multiple_muxer_process_frames, + .post_process = multiple_post_process, + }, + .private_size = sizeof(struct multiple_state), +}; + +const struct aligner_data aligner_demuxer_multiple = { + .ops = { + .pre_process = multiple_pre_process, + .process_frames = multiple_demuxer_process_frames, + .post_process = multiple_post_process, + }, + .private_size = sizeof(struct multiple_state), +}; diff --git a/aplay/aligner.c b/aplay/aligner.c index e578031..6d260a3 100644 --- a/aplay/aligner.c +++ b/aplay/aligner.c @@ -17,23 +17,30 @@ static const char *const aligner_type_labels[] = { static const char *const aligner_target_labels[] = { [ALIGNER_TARGET_SINGLE] = "single", + [ALIGNER_TARGET_MULTIPLE] = "multiple", }; int aligner_context_init(struct aligner_context *aligner, enum aligner_type type, unsigned int cntr_count, unsigned int verbose) { - const struct aligner_data *data = NULL; + const struct aligner_data *data; if (type == ALIGNER_TYPE_MUXER) { if (cntr_count == 1) { data = &aligner_muxer_single; aligner->target = ALIGNER_TARGET_SINGLE; + } else { + data = &aligner_muxer_multiple; + aligner->target = ALIGNER_TARGET_MULTIPLE; } } else { if (cntr_count == 1) { data = &aligner_demuxer_single; aligner->target = ALIGNER_TARGET_SINGLE; + } else { + data = &aligner_demuxer_multiple; + aligner->target = ALIGNER_TARGET_MULTIPLE; } } aligner->ops = &data->ops; @@ -59,6 +66,14 @@ int aligner_context_pre_process(struct aligner_context *aligner, { int err; + /* + * The purpose of multiple target is to mux/demux each channels to/from + * containers. + */ + if (aligner->target == ALIGNER_TARGET_MULTIPLE && + samples_per_frame != aligner->cntr_count) + return -EINVAL; + aligner->access = access; aligner->bytes_per_sample = snd_pcm_format_physical_width(format) / 8; aligner->samples_per_frame = samples_per_frame; diff --git a/aplay/aligner.h b/aplay/aligner.h index 333b584..bf8b689 100644 --- a/aplay/aligner.h +++ b/aplay/aligner.h @@ -21,6 +21,7 @@ enum aligner_type { enum aligner_target { ALIGNER_TARGET_SINGLE = 0, + ALIGNER_TARGET_MULTIPLE, ALIGNER_TARGET_COUNT, }; @@ -81,4 +82,7 @@ struct aligner_data { extern const struct aligner_data aligner_muxer_single; extern const struct aligner_data aligner_demuxer_single; +extern const struct aligner_data aligner_muxer_multiple; +extern const struct aligner_data aligner_demuxer_multiple; + #endif