1 // audio_volume.c
2 // weed plugin
3 // (c) G. Finch (salsaman) 2005 - 2008
4 //
5 // released under the GNU GPL 3 or later
6 // see file COPYING or www.gnu.org for details
7 
8 ///////////////////////////////////////////////////////////////////
9 
10 static int package_version = 1; // version of this package
11 
12 //////////////////////////////////////////////////////////////////
13 
14 #define NEED_AUDIO
15 
16 #ifndef NEED_LOCAL_WEED_PLUGIN
17 #include <weed/weed-plugin.h>
18 #include <weed/weed-utils.h> // optional
19 #include <weed/weed-plugin-utils.h> // optional
20 #else
21 #include "../../libweed/weed-plugin.h"
22 #include "../../libweed/weed-utils.h" // optional
23 #include "../../libweed/weed-plugin-utils.h" // optional
24 #endif
25 
26 #include "weed-plugin-utils.c" // optional
27 
28 /////////////////////////////////////////////////////////////
29 
30 #include <stdio.h>
31 
32 /**
33    Provides a multi stream mixer with volume per stream, pan,
34    and the option to swap left/righ channels (with or without also swapping the pan).
35    The first in channel can be inplaced with the out channel.
36 
37    The plugin utilizes a few special features of Weed. First it sets the filter flag bit:
38    WEED_FILTER_IS_CONVERTER. This allows it to mix multipl input streams into one output,
39    and also permits remapping of channels within the layout (i.e. swap left / right).
40 
41    It also sets the filter flag WEED_FILTER_HINT_PROCESS_LAST, so the host should place it as near to the end of
42    the audio filter chain as possible.
43 
44    Second, only one input channel is created, however the "max_repeats" is set to 0, which means the host may create an
45    unlimited number of copies of the in channel.
46 
47    Finally, one of the parameters in marked "is_volume_master". This, combined with being a converter,
48    allows the plugin to adjust the volume levels between in and out. Additionally the parameter is marked with
49    WEED_PARAMETER_VALUE_PER_CHANNEL | WEED_PARAMETER_VARIABLE_SIZE, which hints the host that the values
50    are mapped one-one to in channels. The other parameters also set these flags, so the host knows the values are also per channel.
51 
52    As an added nicety, the plugin will hint the host to hide the panning and swap controls if the input is mono.
53 */
avol_init(weed_plant_t * inst)54 static weed_error_t  avol_init(weed_plant_t *inst) {
55   weed_plant_t *in_chan = weed_get_in_channel(inst, 0);
56   weed_plant_t **in_params = weed_get_in_params(inst, NULL);
57   weed_plant_t *gui = weed_param_get_gui(in_params[1]);
58   weed_plant_t *gui2 = weed_param_get_gui(in_params[2]);
59   weed_plant_t *gui3 = weed_param_get_gui(in_params[3]);
60   int achans = weed_channel_get_naudchans(in_chan);
61   weed_free(in_params);
62 
63   // hide the "pan" and "swap" controls if we are using mono audio
64   if (achans != 2) {
65     weed_set_boolean_value(gui, WEED_LEAF_HIDDEN, WEED_TRUE);
66     weed_set_boolean_value(gui2, WEED_LEAF_HIDDEN, WEED_TRUE);
67     weed_set_boolean_value(gui3, WEED_LEAF_HIDDEN, WEED_TRUE);
68   } else {
69     weed_set_boolean_value(gui, WEED_LEAF_HIDDEN, WEED_FALSE);
70     weed_set_boolean_value(gui2, WEED_LEAF_HIDDEN, WEED_FALSE);
71     weed_set_boolean_value(gui3, WEED_LEAF_HIDDEN, WEED_FALSE);
72   }
73 
74   return WEED_SUCCESS;
75 }
76 
77 
avol_process(weed_plant_t * inst,weed_timecode_t timestamp)78 static weed_error_t  avol_process(weed_plant_t *inst, weed_timecode_t timestamp) {
79   int ntracks = 0, chans = 0;
80   weed_plant_t **in_channels = weed_get_in_channels(inst, &ntracks);
81   weed_plant_t *out_channel = weed_get_out_channel(inst, 0);
82   float **odst = weed_channel_get_audio_data(out_channel, &chans);
83   float **dst = weed_channel_get_audio_data(out_channel, NULL);
84   float **src = weed_channel_get_audio_data(in_channels[0], NULL);
85   int nsamps = weed_channel_get_audio_length(out_channel), orig_nsamps = nsamps;
86   weed_plant_t **in_params = weed_get_in_params(inst, NULL);
87 
88   // this is marked as WEED_LEAF_IS_VOLUME_MASTER in setup
89   // therefore it must linearly adjust volume between 0.0 and 1.0 for all audio streams
90   double *vol = weed_param_get_array_double(in_params[0],  NULL);
91   double *pan = weed_param_get_array_double(in_params[1], NULL);
92   int *swapchans = weed_param_get_array_boolean(in_params[2], NULL);
93   int *swappan = weed_param_get_array_boolean(in_params[3], NULL);
94 
95   double voll, volr;
96   float tmp;
97   int i, j;
98 
99   weed_free(in_params);
100   voll = volr = vol[0];
101 
102   if (chans == 2) {
103     if (pan[0] < 0.) volr *= (1. + pan[0]);
104     else voll *= (1. - pan[0]);
105     if (swapchans[0] == WEED_FALSE) {
106       for (j = 0; j < nsamps; j++) {
107         dst[0][j] = voll * src[0][j];
108         dst[1][j] = volr * src[1][j];
109       }
110     } else {
111       if (swappan[0]) {
112         tmp = voll; voll = volr; volr = tmp;
113       }
114       for (j = 0; j < nsamps; j++) {
115         tmp = volr * src[0][j]; // in case inplace, src[0] will become dst[0]
116         dst[0][j] = voll * src[1][j];
117         dst[1][j] = tmp;
118       }
119     }
120   } else if (chans == 1) {
121     for (j = 0; j < nsamps; j++) {
122       dst[0][j] = vol[0] * src[0][j];
123     }
124   }
125 
126   for (i = 1; i < ntracks; i++) {
127     if (!in_channels[i]) continue;
128     if (weed_channel_is_disabled(in_channels[i])) continue;
129     if (vol[i] == 0.) continue;
130     dst[0] = odst[0];
131     chans = weed_get_int_value(in_channels[i], WEED_LEAF_AUDIO_CHANNELS, NULL);
132     if (chans == 2) dst[1] = odst[1];
133 
134     nsamps = orig_nsamps = weed_get_int_value(in_channels[i], WEED_LEAF_AUDIO_DATA_LENGTH, NULL);
135     if (src) weed_free(src);
136     src = (float **)weed_get_voidptr_array(in_channels[i], WEED_LEAF_AUDIO_DATA, NULL);
137 
138     voll = volr = vol[i];
139 
140     if (chans == 2) {
141       if (pan[i] < 0.) volr *= (1. + pan[i]);
142       else voll *= (1. - pan[i]);
143       if (swapchans[i] == WEED_FALSE) {
144         for (j = 0; j < nsamps; j++) {
145           dst[0][j] += voll * src[0][j];
146           dst[1][j] += volr * src[1][j];
147         }
148       } else {
149         if (swappan[i]) {
150           tmp = voll; voll = volr; volr = tmp;
151         }
152         for (j = 0; j < nsamps; j++) {
153           tmp = volr * src[0][j]; // in case inplace, src[0] will become dst[0]
154           dst[0][j] += voll * src[1][j];
155           dst[1][j] += tmp;
156         }
157       }
158     } else if (chans == 1) {
159       for (j = 0; j < nsamps; j++) {
160         dst[0][j] += vol[0] * src[0][j];
161       }
162     }
163   }
164 
165   if (odst) weed_free(odst);
166   if (swapchans) weed_free(swapchans);
167   if (swappan) weed_free(swappan);
168   if (dst) weed_free(dst);
169   if (src) weed_free(src);
170   if (vol) weed_free(vol);
171   if (pan) weed_free(pan);
172   if (in_channels) weed_free(in_channels);
173 
174   return WEED_SUCCESS;
175 }
176 
177 
178 WEED_SETUP_START(200, 200) {
179   weed_plant_t *in_chantmpls[] = {weed_audio_channel_template_init("in channel 0",
180                                   WEED_CHANNEL_REINIT_ON_LAYOUT_CHANGE), NULL
181                                  };
182 
183   weed_plant_t *out_chantmpls[] = {weed_audio_channel_template_init("out channel 0", WEED_CHANNEL_CAN_DO_INPLACE), NULL};
184 
185   weed_plant_t *in_params[] = {weed_float_init("volume", "_Volume", 1.0, 0.0, 1.0), weed_float_init("pan", "_Pan", 0., -1., 1.),
186                                weed_switch_init("swap", "_Swap left and right channels", WEED_FALSE),
187                                weed_switch_init("swappan", "_Swap panning when channels swap", WEED_TRUE), NULL
188                               };
189 
190   weed_plant_t *filter_class = weed_filter_class_init("audio volume and pan", "salsaman", 2,
191                                WEED_FILTER_IS_CONVERTER | WEED_FILTER_HINT_PROCESS_LAST, NULL,
192                                avol_init, avol_process, NULL, in_chantmpls, out_chantmpls, in_params, NULL);
193 
194   weed_set_int_value(in_chantmpls[0], WEED_LEAF_MAX_REPEATS, 0); // set optional repeats of this channel
195 
196   weed_set_int_value(in_params[0], WEED_LEAF_FLAGS, WEED_PARAMETER_VARIABLE_SIZE | WEED_PARAMETER_VALUE_PER_CHANNEL);
197   weed_set_double_value(in_params[0], WEED_LEAF_NEW_DEFAULT, 1.0);
198   weed_set_int_value(in_params[1], WEED_LEAF_FLAGS, WEED_PARAMETER_VARIABLE_SIZE | WEED_PARAMETER_VALUE_PER_CHANNEL);
199   weed_set_double_value(in_params[1], WEED_LEAF_NEW_DEFAULT, 0.0);
200   weed_set_int_value(in_params[2], WEED_LEAF_FLAGS, WEED_PARAMETER_VARIABLE_SIZE | WEED_PARAMETER_VALUE_PER_CHANNEL);
201   weed_set_boolean_value(in_params[2], WEED_LEAF_NEW_DEFAULT, WEED_FALSE);
202   weed_set_int_value(in_params[3], WEED_LEAF_FLAGS, WEED_PARAMETER_VARIABLE_SIZE | WEED_PARAMETER_VALUE_PER_CHANNEL);
203   weed_set_boolean_value(in_params[3], WEED_LEAF_NEW_DEFAULT, WEED_TRUE);
204 
205   weed_set_boolean_value(in_params[0], WEED_LEAF_IS_VOLUME_MASTER, WEED_TRUE);
206 
207   weed_plugin_info_add_filter_class(plugin_info, filter_class);
208   weed_set_int_value(plugin_info, WEED_LEAF_VERSION, package_version);
209 }
210 WEED_SETUP_END;
211 
212