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