@@ -207,6 +207,7 @@ External library support:
--disable-bzlib disable bzlib [autodetect]
--disable-coreimage disable Apple CoreImage framework [autodetect]
--enable-chromaprint enable audio fingerprinting with chromaprint [no]
+ --enable-csound enable Csound audio filtering [no]
--enable-frei0r enable frei0r video filtering [no]
--enable-gcrypt enable gcrypt, needed for rtmp(t)e support
if openssl, librtmp or gmp is not used [no]
@@ -1752,6 +1753,7 @@ EXTERNAL_LIBRARY_LIST="
$EXTERNAL_LIBRARY_VERSION3_LIST
$EXTERNAL_LIBRARY_GPLV3_LIST
chromaprint
+ csound
gcrypt
gnutls
jni
@@ -3474,6 +3476,7 @@ coreimagesrc_filter_deps="coreimage appkit"
coreimagesrc_filter_extralibs="-framework OpenGL"
cover_rect_filter_deps="avcodec avformat gpl"
cropdetect_filter_deps="gpl"
+csound_filter_deps="csound"
deconvolve_filter_deps="avcodec"
deconvolve_filter_select="fft"
deinterlace_qsv_filter_deps="libmfx"
@@ -6222,6 +6225,7 @@ done
# these are off by default, so fail if requested and not available
enabled cuda_nvcc && { check_nvcc cuda_nvcc || die "ERROR: failed checking for nvcc."; }
enabled chromaprint && require chromaprint chromaprint.h chromaprint_get_version -lchromaprint
+enabled csound && require csound csound/csound.h csoundCreate -lcsound64
enabled decklink && { require_headers DeckLinkAPI.h &&
{ test_cpp_condition DeckLinkAPIVersion.h "BLACKMAGIC_DECKLINK_API_VERSION >= 0x0a090500" || die "ERROR: Decklink API version must be >= 10.9.5."; } }
enabled frei0r && require_headers "frei0r.h dlfcn.h"
@@ -3230,6 +3230,22 @@ Enable clipping. By default is enabled.
This filter supports the all above options as @ref{commands}.
+@section csound
+
+Load a Csound plugin.
+
+Csound is a unit generator-based, user-programmable computer music system.
+
+To enable compilation of this filter you need to configure FFmpeg with
+@code{--enable-csound}.
+
+@table @option
+@item csd
+Give name or full path to CSD script file.
+CSD file holds unified orchestra and score file.
+This option must always be set.
+@end table
+
@section dcshift
Apply a DC shift to the audio.
@@ -102,6 +102,7 @@ OBJS-$(CONFIG_COMPAND_FILTER) += af_compand.o
OBJS-$(CONFIG_COMPENSATIONDELAY_FILTER) += af_compensationdelay.o
OBJS-$(CONFIG_CROSSFEED_FILTER) += af_crossfeed.o
OBJS-$(CONFIG_CRYSTALIZER_FILTER) += af_crystalizer.o
+OBJS-$(CONFIG_CSOUND_FILTER) += af_csound.o
OBJS-$(CONFIG_DCSHIFT_FILTER) += af_dcshift.o
OBJS-$(CONFIG_DEESSER_FILTER) += af_deesser.o
OBJS-$(CONFIG_DRMETER_FILTER) += af_drmeter.o
new file mode 100644
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2020 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Csound wrapper
+ */
+
+#include <csound/csound.h>
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/channel_layout.h"
+#include "libavutil/opt.h"
+#include "audio.h"
+#include "avfilter.h"
+#include "filters.h"
+#include "internal.h"
+
+typedef struct CsoundContext {
+ const AVClass *class;
+ int sample_rate;
+ int nchnls, nchnls_input;
+ char *csd_filename;
+
+ uint32_t ksmps;
+ int format;
+ int64_t pts;
+ CSOUND *csound;
+ controlChannelInfo_t *devs;
+} CsoundContext;
+
+#define OFFSET(x) offsetof(CsoundContext, x)
+#define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
+
+static const AVOption csound_options[] = {
+ { "csd", "set CSD name or full path", OFFSET(csd_filename), AV_OPT_TYPE_STRING, .flags = FLAGS },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(csound);
+
+static int activate(AVFilterContext *ctx)
+{
+ CsoundContext *s = ctx->priv;
+ AVFilterLink *inlink = ctx->inputs[0];
+ AVFilterLink *outlink = ctx->outputs[0];
+ int ret, nb_samples, nb_in_samples;
+ MYFLT *buffer;
+ AVFrame *out, *in;
+
+ FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx);
+
+ nb_in_samples = csoundGetInputBufferSize(s->csound) / s->nchnls;
+
+ if (nb_in_samples > 0) {
+ ret = ff_inlink_consume_samples(inlink, nb_in_samples, nb_in_samples, &in);
+ if (ret > 0) {
+ buffer = csoundGetInputBuffer(s->csound);
+ if (buffer) {
+ const int nch = s->nchnls;
+ MYFLT *dst = buffer;
+ MYFLT *src = (MYFLT *)in->data[0];
+
+ for (int f = 0; f < nb_in_samples / s->ksmps; f++) {
+ for (int ch = 0; ch < nch; ch++) {
+ for (int n = 0; n < s->ksmps; n++) {
+ dst[n] = *src++;
+ }
+ dst += s->ksmps;
+ }
+ }
+ }
+ av_frame_free(&in);
+ } else if (!ret) {
+ FF_FILTER_FORWARD_WANTED(outlink, inlink);
+ return 0;
+ } else {
+ return ret;
+ }
+ }
+ ret = csoundPerformBuffer(s->csound);
+ nb_samples = csoundGetOutputBufferSize(s->csound) / s->nchnls;
+ if (ret || !nb_samples) {
+ ff_outlink_set_status(outlink, AVERROR_EOF, 0);
+ return 0;
+ }
+
+ buffer = csoundGetOutputBuffer(s->csound);
+ if (buffer) {
+ const int nch = s->nchnls;
+ MYFLT *src = buffer;
+ MYFLT *dst;
+
+ out = ff_get_audio_buffer(outlink, nb_samples);
+ if (!out)
+ return AVERROR(ENOMEM);
+ dst = (MYFLT *)out->data[0];
+ out->pts = s->pts;
+ s->pts += nb_samples;
+ for (int f = 0; f < nb_samples / s->ksmps; f++) {
+ for (int ch = 0; ch < nch; ch++) {
+ for (int n = 0; n < s->ksmps; n++) {
+ *dst++ = src[n];
+ }
+ src += s->ksmps;
+ }
+ }
+
+ return ff_filter_frame(outlink, out);
+ }
+
+ FF_FILTER_FORWARD_STATUS(inlink, outlink);
+
+ return 0;
+}
+
+static void callback(CSOUND *csound, int attr, const char *format, va_list valist)
+{
+ int level;
+
+ switch (attr & CSOUNDMSG_TYPE_MASK) {
+ case CSOUNDMSG_ERROR: level = AV_LOG_ERROR; break;
+ case CSOUNDMSG_WARNING: level = AV_LOG_WARNING; break;
+ default: level = AV_LOG_VERBOSE; break;
+ }
+
+ av_vlog(csoundGetHostData(csound), level, format, valist);
+}
+
+static av_cold int init(AVFilterContext *ctx)
+{
+ CsoundContext *s = ctx->priv;
+ int ret, size;
+
+ csoundSetDefaultMessageCallback(callback);
+ s->csound = csoundCreate(NULL);
+ if (!s->csound)
+ return AVERROR(ENOMEM);
+
+ if (!s->csd_filename)
+ return AVERROR(EINVAL);
+
+ csoundSetHostData(s->csound, s);
+ csoundSetHostImplementedAudioIO(s->csound, 1, 1024);
+ csoundSetMessageCallback(s->csound, callback);
+ ret = csoundCompileCsd(s->csound, s->csd_filename);
+ if (ret != 0)
+ return AVERROR_EXTERNAL;
+ csoundStart(s->csound);
+ s->sample_rate = csoundGetSr(s->csound);
+ s->ksmps = csoundGetKsmps(s->csound);
+ s->nchnls = csoundGetNchnls(s->csound);
+ s->nchnls_input = csoundGetNchnlsInput(s->csound);
+
+ size = csoundGetSizeOfMYFLT();
+ switch (size) {
+ case 4: s->format = AV_SAMPLE_FMT_FLT; break;
+ case 8: s->format = AV_SAMPLE_FMT_DBL; break;
+ default: return AVERROR_BUG;
+ }
+
+ return 0;
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+ CsoundContext *s = ctx->priv;
+ AVFilterFormats *formats;
+ AVFilterChannelLayouts *layouts;
+ int sample_rates[] = { s->sample_rate, -1 };
+ enum AVSampleFormat sample_fmts[] = {
+ s->format, AV_SAMPLE_FMT_NONE };
+ uint64_t inlayout = FF_COUNT2LAYOUT(s->nchnls_input);
+ uint64_t outlayout = FF_COUNT2LAYOUT(s->nchnls);
+ AVFilterLink *outlink = ctx->outputs[0];
+ AVFilterLink *inlink = ctx->inputs[0];
+ int ret;
+
+ formats = ff_make_format_list(sample_fmts);
+ if (!formats)
+ return AVERROR(ENOMEM);
+
+ ret = ff_set_common_formats(ctx, formats);
+ if (ret < 0)
+ return ret;
+
+ formats = ff_make_format_list(sample_rates);
+ if (!formats)
+ return AVERROR(ENOMEM);
+
+ ret = ff_set_common_samplerates(ctx, formats);
+ if (ret < 0)
+ return ret;
+
+
+ layouts = NULL;
+ ret = ff_add_channel_layout(&layouts, inlayout);
+ if (ret < 0)
+ return ret;
+ ret = ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
+ if (ret < 0)
+ return ret;
+
+ layouts = NULL;
+ ret = ff_add_channel_layout(&layouts, outlayout);
+ if (ret < 0)
+ return ret;
+ ret = ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+ CsoundContext *s = ctx->priv;
+
+ csoundReset(s->csound);
+ csoundDestroy(s->csound);
+}
+
+static const AVFilterPad inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_AUDIO,
+ },
+ { NULL }
+};
+
+static const AVFilterPad outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_AUDIO,
+ },
+ { NULL }
+};
+
+AVFilter ff_af_csound = {
+ .name = "csound",
+ .description = NULL_IF_CONFIG_SMALL("Apply Csound script."),
+ .priv_size = sizeof(CsoundContext),
+ .priv_class = &csound_class,
+ .init = init,
+ .activate = activate,
+ .uninit = uninit,
+ .query_formats = query_formats,
+ .inputs = inputs,
+ .outputs = outputs,
+};
@@ -95,6 +95,7 @@ extern AVFilter ff_af_compand;
extern AVFilter ff_af_compensationdelay;
extern AVFilter ff_af_crossfeed;
extern AVFilter ff_af_crystalizer;
+extern AVFilter ff_af_csound;
extern AVFilter ff_af_dcshift;
extern AVFilter ff_af_deesser;
extern AVFilter ff_af_drmeter;
Signed-off-by: Paul B Mahol <onemda@gmail.com> --- configure | 4 + doc/filters.texi | 16 +++ libavfilter/Makefile | 1 + libavfilter/af_csound.c | 267 +++++++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + 5 files changed, 289 insertions(+) create mode 100644 libavfilter/af_csound.c