"Fossies" - the Fresh Open Source Software Archive

Member "gst-plugins-good-1.20.3/gst/audiofx/audiopanorama.c" (15 Jun 2022, 17005 Bytes) of package /linux/misc/gst-plugins-good-1.20.3.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "audiopanorama.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.18.6_vs_1.20.0.

    1 /*
    2  * GStreamer
    3  * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
    4  * Copyright (C) 2006 Sebastian Dröge <slomo@circular-chaos.org>
    5  *
    6  * This library is free software; you can redistribute it and/or
    7  * modify it under the terms of the GNU Library General Public
    8  * License as published by the Free Software Foundation; either
    9  * version 2 of the License, or (at your option) any later version.
   10  *
   11  * This library is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14  * Library General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU Library General Public
   17  * License along with this library; if not, write to the
   18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
   19  * Boston, MA 02110-1301, USA.
   20  */
   21 
   22 /**
   23  * SECTION:element-audiopanorama
   24  * @title: audiopanorama
   25  *
   26  * Stereo panorama effect with controllable pan position. One can choose between the default psychoacoustic panning method,
   27  * which keeps the same perceived loudness, and a simple panning method that just controls the volume on one channel.
   28  *
   29  * ## Example launch line
   30  * |[
   31  * gst-launch-1.0 audiotestsrc wave=saw ! audiopanorama panorama=-1.00 ! alsasink
   32  * gst-launch-1.0 filesrc location="melo1.ogg" ! oggdemux ! vorbisdec ! audioconvert ! audiopanorama panorama=-1.00 ! alsasink
   33  * gst-launch-1.0 audiotestsrc wave=saw ! audioconvert ! audiopanorama panorama=-1.00 ! audioconvert ! alsasink
   34  * gst-launch-1.0 audiotestsrc wave=saw ! audioconvert ! audiopanorama method=simple panorama=-0.50 ! audioconvert ! alsasink
   35  * ]|
   36  *
   37  */
   38 
   39 #ifdef HAVE_CONFIG_H
   40 #include "config.h"
   41 #endif
   42 
   43 #include <string.h>
   44 
   45 #include <gst/gst.h>
   46 #include <gst/base/gstbasetransform.h>
   47 
   48 #ifdef HAVE_ORC
   49 #include <orc/orcfunctions.h>
   50 #else
   51 #define orc_memset memset
   52 #endif
   53 
   54 #include "audiopanorama.h"
   55 #include "audiopanoramaorc.h"
   56 
   57 #define GST_CAT_DEFAULT gst_audio_panorama_debug
   58 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
   59 
   60 /* Filter signals and args */
   61 enum
   62 {
   63   PROP_0,
   64   PROP_PANORAMA,
   65   PROP_METHOD
   66 };
   67 
   68 #define GST_TYPE_AUDIO_PANORAMA_METHOD (gst_audio_panorama_method_get_type ())
   69 static GType
   70 gst_audio_panorama_method_get_type (void)
   71 {
   72   static GType gtype = 0;
   73 
   74   if (gtype == 0) {
   75     static const GEnumValue values[] = {
   76       {METHOD_PSYCHOACOUSTIC, "Psychoacoustic Panning (default)",
   77           "psychoacoustic"},
   78       {METHOD_SIMPLE, "Simple Panning", "simple"},
   79       {0, NULL, NULL}
   80     };
   81 
   82     gtype = g_enum_register_static ("GstAudioPanoramaMethod", values);
   83   }
   84   return gtype;
   85 }
   86 
   87 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
   88     GST_PAD_SINK,
   89     GST_PAD_ALWAYS,
   90     GST_STATIC_CAPS ("audio/x-raw, "
   91         "format = (string) { " GST_AUDIO_NE (F32) ", " GST_AUDIO_NE (S16) "}, "
   92         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ], "
   93         "layout = (string) interleaved")
   94     );
   95 
   96 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
   97     GST_PAD_SRC,
   98     GST_PAD_ALWAYS,
   99     GST_STATIC_CAPS ("audio/x-raw, "
  100         "format = (string) { " GST_AUDIO_NE (F32) ", " GST_AUDIO_NE (S16) "}, "
  101         "rate = (int) [ 1, MAX ], " "channels = (int) 2, "
  102         "layout = (string) interleaved")
  103     );
  104 
  105 G_DEFINE_TYPE (GstAudioPanorama, gst_audio_panorama, GST_TYPE_BASE_TRANSFORM);
  106 GST_ELEMENT_REGISTER_DEFINE (audiopanorama, "audiopanorama",
  107     GST_RANK_NONE, GST_TYPE_AUDIO_PANORAMA);
  108 
  109 static void gst_audio_panorama_set_property (GObject * object, guint prop_id,
  110     const GValue * value, GParamSpec * pspec);
  111 static void gst_audio_panorama_get_property (GObject * object, guint prop_id,
  112     GValue * value, GParamSpec * pspec);
  113 
  114 static gboolean gst_audio_panorama_get_unit_size (GstBaseTransform * base,
  115     GstCaps * caps, gsize * size);
  116 static GstCaps *gst_audio_panorama_transform_caps (GstBaseTransform * base,
  117     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
  118 static gboolean gst_audio_panorama_set_caps (GstBaseTransform * base,
  119     GstCaps * incaps, GstCaps * outcaps);
  120 
  121 static void gst_audio_panorama_m2s_int (gfloat pan,
  122     gint16 * idata, gint16 * odata, guint num_samples);
  123 static void gst_audio_panorama_s2s_int (gfloat pan,
  124     gint16 * idata, gint16 * odata, guint num_samples);
  125 static void gst_audio_panorama_m2s_float (gfloat pan,
  126     gfloat * idata, gfloat * odata, guint num_samples);
  127 static void gst_audio_panorama_s2s_float (gfloat pan,
  128     gfloat * idata, gfloat * odata, guint num_samples);
  129 
  130 static void gst_audio_panorama_m2s_int_simple (gfloat pan,
  131     gint16 * idata, gint16 * odata, guint num_samples);
  132 static void gst_audio_panorama_s2s_int_simple (gfloat pan,
  133     gint16 * idata, gint16 * odata, guint num_samples);
  134 static void gst_audio_panorama_m2s_float_simple (gfloat pan,
  135     gfloat * idata, gfloat * odata, guint num_samples);
  136 static void gst_audio_panorama_s2s_float_simple (gfloat pan,
  137     gfloat * idata, gfloat * odata, guint num_samples);
  138 
  139 static GstFlowReturn gst_audio_panorama_transform (GstBaseTransform * base,
  140     GstBuffer * inbuf, GstBuffer * outbuf);
  141 
  142 
  143 /* Table with processing functions: [channels][format][method] */
  144 static const GstAudioPanoramaProcessFunc panorama_process_functions[2][2][2] = {
  145   {
  146         {
  147               (GstAudioPanoramaProcessFunc) gst_audio_panorama_m2s_int,
  148             (GstAudioPanoramaProcessFunc) gst_audio_panorama_m2s_int_simple},
  149         {
  150               (GstAudioPanoramaProcessFunc) gst_audio_panorama_m2s_float,
  151             (GstAudioPanoramaProcessFunc) gst_audio_panorama_m2s_float_simple}
  152       },
  153   {
  154         {
  155               (GstAudioPanoramaProcessFunc) gst_audio_panorama_s2s_int,
  156             (GstAudioPanoramaProcessFunc) gst_audio_panorama_s2s_int_simple},
  157         {
  158               (GstAudioPanoramaProcessFunc) gst_audio_panorama_s2s_float,
  159             (GstAudioPanoramaProcessFunc) gst_audio_panorama_s2s_float_simple}
  160       }
  161 };
  162 
  163 /* GObject vmethod implementations */
  164 
  165 static void
  166 gst_audio_panorama_class_init (GstAudioPanoramaClass * klass)
  167 {
  168   GObjectClass *gobject_class;
  169   GstElementClass *gstelement_class;
  170 
  171   GST_DEBUG_CATEGORY_INIT (gst_audio_panorama_debug, "audiopanorama", 0,
  172       "audiopanorama element");
  173 
  174   gobject_class = (GObjectClass *) klass;
  175   gstelement_class = (GstElementClass *) klass;
  176 
  177   gobject_class->set_property = gst_audio_panorama_set_property;
  178   gobject_class->get_property = gst_audio_panorama_get_property;
  179 
  180   g_object_class_install_property (gobject_class, PROP_PANORAMA,
  181       g_param_spec_float ("panorama", "Panorama",
  182           "Position in stereo panorama (-1.0 left -> 1.0 right)", -1.0, 1.0,
  183           0.0,
  184           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
  185   /**
  186    * GstAudioPanorama:method:
  187    *
  188    * Panning method: psychoacoustic mode keeps the same perceived loudness,
  189    * while simple mode just controls the volume of one channel. It's merely
  190    * a matter of taste which method should be chosen.
  191    */
  192   g_object_class_install_property (gobject_class, PROP_METHOD,
  193       g_param_spec_enum ("method", "Panning method",
  194           "Psychoacoustic mode keeps same perceived loudness, "
  195           "simple mode just controls volume of one channel.",
  196           GST_TYPE_AUDIO_PANORAMA_METHOD, METHOD_PSYCHOACOUSTIC,
  197           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  198 
  199   gst_element_class_set_static_metadata (gstelement_class, "Stereo positioning",
  200       "Filter/Effect/Audio",
  201       "Positions audio streams in the stereo panorama",
  202       "Stefan Kost <ensonic@users.sf.net>");
  203 
  204   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
  205   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
  206 
  207   GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size =
  208       GST_DEBUG_FUNCPTR (gst_audio_panorama_get_unit_size);
  209   GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
  210       GST_DEBUG_FUNCPTR (gst_audio_panorama_transform_caps);
  211   GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
  212       GST_DEBUG_FUNCPTR (gst_audio_panorama_set_caps);
  213   GST_BASE_TRANSFORM_CLASS (klass)->transform =
  214       GST_DEBUG_FUNCPTR (gst_audio_panorama_transform);
  215 
  216   gst_type_mark_as_plugin_api (GST_TYPE_AUDIO_PANORAMA_METHOD, 0);
  217 }
  218 
  219 static void
  220 gst_audio_panorama_init (GstAudioPanorama * filter)
  221 {
  222 
  223   filter->panorama = 0;
  224   filter->method = METHOD_PSYCHOACOUSTIC;
  225   gst_audio_info_init (&filter->info);
  226   filter->process = NULL;
  227 
  228   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
  229 }
  230 
  231 static gboolean
  232 gst_audio_panorama_set_process_function (GstAudioPanorama * filter,
  233     GstAudioInfo * info)
  234 {
  235   gint channel_index, format_index, method_index;
  236   const GstAudioFormatInfo *finfo = info->finfo;
  237 
  238   /* set processing function */
  239   channel_index = GST_AUDIO_INFO_CHANNELS (info) - 1;
  240   if (channel_index > 1 || channel_index < 0) {
  241     filter->process = NULL;
  242     return FALSE;
  243   }
  244 
  245   format_index = GST_AUDIO_FORMAT_INFO_IS_FLOAT (finfo) ? 1 : 0;
  246   method_index = filter->method;
  247 
  248   filter->process =
  249       panorama_process_functions[channel_index][format_index][method_index];
  250   return TRUE;
  251 }
  252 
  253 static void
  254 gst_audio_panorama_set_property (GObject * object, guint prop_id,
  255     const GValue * value, GParamSpec * pspec)
  256 {
  257   GstAudioPanorama *filter = GST_AUDIO_PANORAMA (object);
  258 
  259   switch (prop_id) {
  260     case PROP_PANORAMA:
  261       filter->panorama = g_value_get_float (value);
  262       break;
  263     case PROP_METHOD:
  264       filter->method = g_value_get_enum (value);
  265       gst_audio_panorama_set_process_function (filter, &filter->info);
  266       break;
  267     default:
  268       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  269       break;
  270   }
  271 }
  272 
  273 static void
  274 gst_audio_panorama_get_property (GObject * object, guint prop_id,
  275     GValue * value, GParamSpec * pspec)
  276 {
  277   GstAudioPanorama *filter = GST_AUDIO_PANORAMA (object);
  278 
  279   switch (prop_id) {
  280     case PROP_PANORAMA:
  281       g_value_set_float (value, filter->panorama);
  282       break;
  283     case PROP_METHOD:
  284       g_value_set_enum (value, filter->method);
  285       break;
  286     default:
  287       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  288       break;
  289   }
  290 }
  291 
  292 /* GstBaseTransform vmethod implementations */
  293 
  294 static gboolean
  295 gst_audio_panorama_get_unit_size (GstBaseTransform * base, GstCaps * caps,
  296     gsize * size)
  297 {
  298   GstAudioInfo info;
  299 
  300   g_assert (size);
  301 
  302   if (!gst_audio_info_from_caps (&info, caps))
  303     return FALSE;
  304 
  305   *size = GST_AUDIO_INFO_BPF (&info);
  306 
  307   return TRUE;
  308 }
  309 
  310 static GstCaps *
  311 gst_audio_panorama_transform_caps (GstBaseTransform * base,
  312     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
  313 {
  314   GstCaps *res;
  315   GstStructure *structure;
  316   gint i;
  317 
  318   /* replace the channel property with our range. */
  319   res = gst_caps_copy (caps);
  320   for (i = 0; i < gst_caps_get_size (res); i++) {
  321     structure = gst_caps_get_structure (res, i);
  322     if (direction == GST_PAD_SRC) {
  323       GST_INFO_OBJECT (base, "[%d] allow 1-2 channels", i);
  324       gst_structure_set (structure, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
  325     } else {
  326       GST_INFO_OBJECT (base, "[%d] allow 2 channels", i);
  327       gst_structure_set (structure, "channels", G_TYPE_INT, 2, NULL);
  328     }
  329     gst_structure_remove_field (structure, "channel-mask");
  330   }
  331   GST_DEBUG_OBJECT (base, "transformed %" GST_PTR_FORMAT, res);
  332 
  333   if (filter) {
  334     GstCaps *intersection;
  335 
  336     GST_DEBUG_OBJECT (base, "Using filter caps %" GST_PTR_FORMAT, filter);
  337     intersection =
  338         gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
  339     gst_caps_unref (res);
  340     res = intersection;
  341     GST_DEBUG_OBJECT (base, "Intersection %" GST_PTR_FORMAT, res);
  342   }
  343 
  344   return res;
  345 }
  346 
  347 static gboolean
  348 gst_audio_panorama_set_caps (GstBaseTransform * base, GstCaps * incaps,
  349     GstCaps * outcaps)
  350 {
  351   GstAudioPanorama *filter = GST_AUDIO_PANORAMA (base);
  352   GstAudioInfo info;
  353 
  354   /*GST_INFO ("incaps are %" GST_PTR_FORMAT, incaps); */
  355   if (!gst_audio_info_from_caps (&info, incaps))
  356     goto no_format;
  357 
  358   GST_DEBUG ("try to process %d input with %d channels",
  359       GST_AUDIO_INFO_FORMAT (&info), GST_AUDIO_INFO_CHANNELS (&info));
  360 
  361   if (!gst_audio_panorama_set_process_function (filter, &info))
  362     goto no_format;
  363 
  364   filter->info = info;
  365 
  366   return TRUE;
  367 
  368 no_format:
  369   {
  370     GST_DEBUG ("invalid caps");
  371     return FALSE;
  372   }
  373 }
  374 
  375 /* psychoacoustic processing functions */
  376 
  377 /* mono to stereo panning
  378  * pan: -1.0  0.0  1.0
  379  * l:    1.0  0.5  0.0  
  380  * r:    0.0  0.5  1.0
  381  *
  382  * FIXME: we should use -3db (1/sqtr(2)) for 50:50
  383  */
  384 static void
  385 gst_audio_panorama_m2s_int (gfloat pan, gint16 * idata, gint16 * odata, guint n)
  386 {
  387   gfloat r = (pan + 1.0) / 2.0;
  388   audiopanoramam_orc_process_s16_ch1_psy (odata, idata, 1.0 - r, r, n);
  389 }
  390 
  391 static void
  392 gst_audio_panorama_m2s_float (gfloat pan, gfloat * idata,
  393     gfloat * odata, guint n)
  394 {
  395   gfloat r = (pan + 1.0) / 2.0;
  396   audiopanoramam_orc_process_f32_ch1_psy (odata, idata, 1.0 - r, r, n);
  397 }
  398 
  399 /* stereo balance
  400  * pan: -1.0  0.0  1.0
  401  * ll:   1.0  1.0  0.0
  402  * lr:   1.0  0.0  0.0
  403  * rr:   0.0  1.0  1.0
  404  * rl:   0.0  0.0  1.0
  405  */
  406 static void
  407 gst_audio_panorama_s2s_int (gfloat pan, gint16 * idata, gint16 * odata, guint n)
  408 {
  409   if (pan == 0.0) {
  410     audiopanoramam_orc_process_s16_ch2_none (odata, idata, n);
  411   } else if (pan > 0.0) {
  412     gfloat rl = pan;
  413     gfloat ll = 1.0 - rl;
  414     audiopanoramam_orc_process_s16_ch2_psy_right (odata, idata, ll, rl, n);
  415   } else {
  416     gfloat rr = 1.0 + pan;
  417     gfloat lr = 1.0 - rr;
  418     audiopanoramam_orc_process_s16_ch2_psy_left (odata, idata, lr, rr, n);
  419   }
  420 }
  421 
  422 static void
  423 gst_audio_panorama_s2s_float (gfloat pan, gfloat * idata,
  424     gfloat * odata, guint n)
  425 {
  426   if (pan == 0.0) {
  427     audiopanoramam_orc_process_f32_ch2_none (odata, idata, n);
  428   } else if (pan > 0.0) {
  429     gfloat rl = pan;
  430     gfloat ll = 1.0 - rl;
  431     audiopanoramam_orc_process_f32_ch2_psy_right (odata, idata, ll, rl, n);
  432   } else {
  433     gfloat rr = 1.0 + pan;
  434     gfloat lr = 1.0 - rr;
  435     audiopanoramam_orc_process_f32_ch2_psy_left (odata, idata, lr, rr, n);
  436   }
  437 }
  438 
  439 /* simple processing functions */
  440 
  441 static void
  442 gst_audio_panorama_m2s_int_simple (gfloat pan, gint16 * idata,
  443     gint16 * odata, guint n)
  444 {
  445   if (pan == 0.0) {
  446     audiopanoramam_orc_process_s16_ch1_none (odata, idata, n);
  447   } else if (pan > 0.0) {
  448     gfloat lpan = 1.0 - pan;
  449     audiopanoramam_orc_process_s16_ch1_sim_left (odata, idata, lpan, n);
  450   } else {
  451     gfloat rpan = 1.0 + pan;
  452     audiopanoramam_orc_process_s16_ch1_sim_right (odata, idata, rpan, n);
  453   }
  454 }
  455 
  456 static void
  457 gst_audio_panorama_s2s_int_simple (gfloat pan, gint16 * idata,
  458     gint16 * odata, guint n)
  459 {
  460   if (pan == 0.0) {
  461     audiopanoramam_orc_process_s16_ch2_none (odata, idata, n);
  462   } else if (pan > 0.0) {
  463     gfloat lpan = 1.0 - pan;
  464     audiopanoramam_orc_process_s16_ch2_sim_left (odata, idata, lpan, n);
  465   } else {
  466     gfloat rpan = 1.0 + pan;
  467     audiopanoramam_orc_process_s16_ch2_sim_right (odata, idata, rpan, n);
  468   }
  469 }
  470 
  471 static void
  472 gst_audio_panorama_m2s_float_simple (gfloat pan, gfloat * idata,
  473     gfloat * odata, guint n)
  474 {
  475   if (pan == 0.0) {
  476     audiopanoramam_orc_process_f32_ch1_none (odata, idata, n);
  477   } else if (pan > 0.0) {
  478     gfloat lpan = 1.0 - pan;
  479     audiopanoramam_orc_process_f32_ch1_sim_left (odata, idata, lpan, n);
  480   } else {
  481     gfloat rpan = 1.0 + pan;
  482     audiopanoramam_orc_process_f32_ch1_sim_right (odata, idata, rpan, n);
  483   }
  484 }
  485 
  486 static void
  487 gst_audio_panorama_s2s_float_simple (gfloat pan, gfloat * idata,
  488     gfloat * odata, guint n)
  489 {
  490   if (pan == 0.0) {
  491     audiopanoramam_orc_process_f32_ch2_none (odata, idata, n);
  492   } else if (pan > 0.0) {
  493     gfloat lpan = 1.0 - pan;
  494     audiopanoramam_orc_process_f32_ch2_sim_left (odata, idata, lpan, n);
  495   } else {
  496     gfloat rpan = 1.0 + pan;
  497     audiopanoramam_orc_process_f32_ch2_sim_right (odata, idata, rpan, n);
  498   }
  499 }
  500 
  501 /* this function does the actual processing
  502  */
  503 static GstFlowReturn
  504 gst_audio_panorama_transform (GstBaseTransform * base, GstBuffer * inbuf,
  505     GstBuffer * outbuf)
  506 {
  507   GstAudioPanorama *filter = GST_AUDIO_PANORAMA (base);
  508   GstClockTime ts;
  509   GstMapInfo inmap, outmap;
  510 
  511   ts = gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME,
  512       GST_BUFFER_TIMESTAMP (inbuf));
  513 
  514   if (GST_CLOCK_TIME_IS_VALID (ts)) {
  515     GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT, GST_TIME_ARGS (ts));
  516     gst_object_sync_values (GST_OBJECT (filter), ts);
  517   }
  518 
  519   gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
  520 
  521   if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP))) {
  522     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
  523     orc_memset (outmap.data, 0, outmap.size);
  524   } else {
  525     /* output is always stereo, input is mono or stereo,
  526      * and info describes input format */
  527     guint num_samples = outmap.size / (2 * GST_AUDIO_INFO_BPS (&filter->info));
  528 
  529     gst_buffer_map (inbuf, &inmap, GST_MAP_READ);
  530     filter->process (filter->panorama, inmap.data, outmap.data, num_samples);
  531     gst_buffer_unmap (inbuf, &inmap);
  532   }
  533 
  534   gst_buffer_unmap (outbuf, &outmap);
  535 
  536   return GST_FLOW_OK;
  537 }