From patchwork Sun Jul 14 10:26:09 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 13941 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 1CC2C449CF5 for ; Sun, 14 Jul 2019 13:26:27 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id EA55468AAD0; Sun, 14 Jul 2019 13:26:26 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ed1-f42.google.com (mail-ed1-f42.google.com [209.85.208.42]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A53DB68A110 for ; Sun, 14 Jul 2019 13:26:19 +0300 (EEST) Received: by mail-ed1-f42.google.com with SMTP id d4so12763762edr.13 for ; Sun, 14 Jul 2019 03:26:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id; bh=/HxlroaduIgk4MrMs6s+3y8enNh/e8jwtSSCnwaWh1o=; b=hLSZATPhkPrVFiEBS2Zy3f95jkydaMih6V2dawG1WTRFAjvPgh2P0AV9xzdXq73jNM Mr4no5Cadp0RCRPDroQJg/RqtTA5j1VwvvxKnH8UU9v/pB0VUqbFxFRFYSOueEqIwJGB grd82/rTqb/82XjG9RRxML0+OyTPqrpcPlc+LWNXMjtPDC9zWKUC1RMpzCY1pC4mq+O2 mWzDrwRcqO0TAYOcq0pcr6awOLi3AK//xtIuLLdJFHu8Myi9RSbtV9iKNblqbGs4HNyn YeWJKKKIeu/2pVTcMxLny2xQllNFJZs+SsQHjLyjHhBhIyFyq7fmk8Tr9VdS1Jh6hRnY ovSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id; bh=/HxlroaduIgk4MrMs6s+3y8enNh/e8jwtSSCnwaWh1o=; b=psKCMrqnjwZU9ykp9ISLojzUSlpCu3dLi+b1cYPRTUra7LJkoo0ZAm57AGlGUt2ja5 eOf9kJHSSLhd4X4N5Eh9CrFy/Z/GRIuFAeL3I7fsfWH8UWTSlpLQgwnAtfjLzi5WHVWt lf5ciFkQtjG8elfPxm45ehgErKpB300rcmdXpA6sJDK5sShH+emBlWILpEEYVyT3mbMg bIk17OB3CbNHSd2GDoaEsoe1vXl9yyn0y2JuPQSZAsRAJcxxcd9MIYx25sNrSXJjpljC nhZUOREmhfeVE1X9ssPHdKf6Fy7QmkUcv4eVuYGevvYp4niUBNthbKwHHX1i8eTf53JP ZATg== X-Gm-Message-State: APjAAAXRmefjKaT+esw1QTlCLfm1oW6yffNrUrwBbgrpa/PsEnSKKWsY HB8HhWJwM6hpLUo6IF1pTVhmDb9i X-Google-Smtp-Source: APXvYqxHFbApCb/QERIpH04mqZqVSpv/+KtZxVL78sYHLZeKNQn46/hFXz+kFWCsVwn0QICuv838Ug== X-Received: by 2002:aa7:d985:: with SMTP id u5mr17535899eds.222.1563099978705; Sun, 14 Jul 2019 03:26:18 -0700 (PDT) Received: from localhost.localdomain ([77.237.109.181]) by smtp.gmail.com with ESMTPSA id d44sm4308895eda.75.2019.07.14.03.26.17 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 14 Jul 2019 03:26:18 -0700 (PDT) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Sun, 14 Jul 2019 12:26:09 +0200 Message-Id: <20190714102609.13919-1-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 Subject: [FFmpeg-devel] [PATCH] avfilter: add square audio source filter X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Signed-off-by: Paul B Mahol --- doc/filters.texi | 44 ++++++++++++++++++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/asrc_sine.c | 86 ++++++++++++++++++++++++++++++++++------ 4 files changed, 119 insertions(+), 13 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 3108ad349e..b94eddefe5 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -5895,6 +5895,50 @@ sine=1000:samples_per_frame='st(0,mod(n,5)); 1602-not(not(eq(ld(0),1)+eq(ld(0),3 @end example @end itemize +@section square + +Generate an audio signal made of a square wave with custom amplitude. + +The audio signal is bit-exact. + +The filter accepts the following options: + +@table @option +@item amplitude, a +Set the carrier amplitude, Default is 0.2. + +@item frequency, f +Set the carrier frequency. Default is 440 Hz. + +@item sample_rate, r +Specify the sample rate, default is 44100. + +@item duration, d +Specify the duration of the generated audio stream. + +@item samples_per_frame +Set the number of samples per output frame. + +The expression can contain the following constants: + +@table @option +@item n +The (sequential) number of the output audio frame, starting from 0. + +@item pts +The PTS (Presentation TimeStamp) of the output audio frame, +expressed in @var{TB} units. + +@item t +The PTS of the output audio frame, expressed in seconds. + +@item TB +The timebase of the output audio frames. +@end table + +Default is @code{1024}. +@end table + @c man end AUDIO SOURCES @chapter Audio Sinks diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 455c809b15..b958450a80 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -148,6 +148,7 @@ OBJS-$(CONFIG_FLITE_FILTER) += asrc_flite.o OBJS-$(CONFIG_HILBERT_FILTER) += asrc_hilbert.o OBJS-$(CONFIG_SINC_FILTER) += asrc_sinc.o OBJS-$(CONFIG_SINE_FILTER) += asrc_sine.o +OBJS-$(CONFIG_SQUARE_FILTER) += asrc_sine.o OBJS-$(CONFIG_ANULLSINK_FILTER) += asink_anullsink.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 04a3df7d56..510e0d65dd 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -140,6 +140,7 @@ extern AVFilter ff_asrc_flite; extern AVFilter ff_asrc_hilbert; extern AVFilter ff_asrc_sinc; extern AVFilter ff_asrc_sine; +extern AVFilter ff_asrc_square; extern AVFilter ff_asink_anullsink; diff --git a/libavfilter/asrc_sine.c b/libavfilter/asrc_sine.c index 3a87210b4b..e3153f03e4 100644 --- a/libavfilter/asrc_sine.c +++ b/libavfilter/asrc_sine.c @@ -30,6 +30,7 @@ typedef struct SineContext { const AVClass *class; + double amplitude; double frequency; double beep_factor; char *samples_per_frame; @@ -38,6 +39,7 @@ typedef struct SineContext { int64_t duration; int16_t *sin; int64_t pts; + int square_amplitude; uint32_t phi; ///< current phase of the sine (2pi = 1<<32) uint32_t dphi; ///< phase increment between two samples unsigned beep_period; @@ -45,6 +47,8 @@ typedef struct SineContext { unsigned beep_length; uint32_t phi_beep; ///< current phase of the beep uint32_t dphi_beep; ///< phase increment of the beep + + void (*generate)(struct SineContext *sine, AVFrame *frame); } SineContext; #define CONTEXT SineContext @@ -141,11 +145,51 @@ enum { VAR_VARS_NB }; +static void generate_sine(SineContext *sine, AVFrame *frame) +{ + int16_t *samples = (int16_t *)frame->data[0]; + + for (int i = 0; i < frame->nb_samples; i++) { + samples[i] = sine->sin[sine->phi >> (32 - LOG_PERIOD)]; + sine->phi += sine->dphi; + if (sine->beep_index < sine->beep_length) { + samples[i] += sine->sin[sine->phi_beep >> (32 - LOG_PERIOD)] << 1; + sine->phi_beep += sine->dphi_beep; + } + if (++sine->beep_index == sine->beep_period) + sine->beep_index = 0; + } +} + +static void generate_square(SineContext *sine, AVFrame *frame) +{ + int16_t *samples = (int16_t *)frame->data[0]; + + for (int i = 0; i < frame->nb_samples; i++) { + int16_t sample = sine->sin[sine->phi >> (32 - LOG_PERIOD)]; + + if (sample >= 0) + sample = sine->square_amplitude; + else + sample = -sine->square_amplitude; + + samples[i] = sample; + sine->phi += sine->dphi; + } +} + static av_cold int init(AVFilterContext *ctx) { int ret; SineContext *sine = ctx->priv; + if (!strcmp(ctx->filter->name, "square")) { + sine->square_amplitude = -sine->amplitude * INT16_MIN; + sine->generate = generate_square; + } else { + sine->generate = generate_sine; + } + if (!(sine->sin = av_malloc(sizeof(*sine->sin) << LOG_PERIOD))) return AVERROR(ENOMEM); sine->dphi = ldexp(sine->frequency, 32) / sine->sample_rate + 0.5; @@ -224,8 +268,7 @@ static int request_frame(AVFilterLink *outlink) [VAR_T] = sine->pts * av_q2d(outlink->time_base), [VAR_TB] = av_q2d(outlink->time_base), }; - int i, nb_samples = lrint(av_expr_eval(sine->samples_per_frame_expr, values, sine)); - int16_t *samples; + int nb_samples = lrint(av_expr_eval(sine->samples_per_frame_expr, values, sine)); if (nb_samples <= 0) { av_log(sine, AV_LOG_WARNING, "nb samples expression evaluated to %d, " @@ -241,18 +284,8 @@ static int request_frame(AVFilterLink *outlink) } if (!(frame = ff_get_audio_buffer(outlink, nb_samples))) return AVERROR(ENOMEM); - samples = (int16_t *)frame->data[0]; - for (i = 0; i < nb_samples; i++) { - samples[i] = sine->sin[sine->phi >> (32 - LOG_PERIOD)]; - sine->phi += sine->dphi; - if (sine->beep_index < sine->beep_length) { - samples[i] += sine->sin[sine->phi_beep >> (32 - LOG_PERIOD)] << 1; - sine->phi_beep += sine->dphi_beep; - } - if (++sine->beep_index == sine->beep_period) - sine->beep_index = 0; - } + sine->generate(sine, frame); frame->pts = sine->pts; sine->pts += nb_samples; @@ -280,3 +313,30 @@ AVFilter ff_asrc_sine = { .outputs = sine_outputs, .priv_class = &sine_class, }; + +static const AVOption square_options[] = { + OPT_DBL("amplitude", amplitude, 0.2, 0, 1, "set the square amplitude",), + OPT_DBL("a", amplitude, 0.2, 0, 1, "set the square amplitude",), + OPT_DBL("frequency", frequency, 440, 0, DBL_MAX, "set the square frequency",), + OPT_DBL("f", frequency, 440, 0, DBL_MAX, "set the square frequency",), + OPT_INT("sample_rate", sample_rate, 44100, 1, INT_MAX, "set the sample rate",), + OPT_INT("r", sample_rate, 44100, 1, INT_MAX, "set the sample rate",), + OPT_DUR("duration", duration, 0, 0, INT64_MAX, "set the audio duration",), + OPT_DUR("d", duration, 0, 0, INT64_MAX, "set the audio duration",), + OPT_STR("samples_per_frame", samples_per_frame, "1024", 0, 0, "set the number of samples per frame",), + {NULL} +}; + +AVFILTER_DEFINE_CLASS(square); + +AVFilter ff_asrc_square = { + .name = "square", + .description = NULL_IF_CONFIG_SMALL("Generate square wave audio signal."), + .query_formats = query_formats, + .init = init, + .uninit = uninit, + .priv_size = sizeof(SineContext), + .inputs = NULL, + .outputs = sine_outputs, + .priv_class = &square_class, +};