"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 }