-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathrematrix-filter.cpp
324 lines (262 loc) · 9.76 KB
/
rematrix-filter.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#pragma once
#include <obs-module.h>
#include <stdio.h>
#include <media-io/audio-math.h>
#include <math.h>
#include <atomic>
OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("rematrix-filter", "en-US")
#define MT_ obs_module_text
#ifndef MAX_AUDIO_SIZE
#ifndef AUDIO_OUTPUT_FRAMES
#define AUDIO_OUTPUT_FRAMES 1024
#endif
#define MAX_AUDIO_SIZE (AUDIO_OUTPUT_FRAMES * sizeof(float))
#endif // !MAX_AUDIO_SIZE
/*****************************************************************************/
long long get_obs_output_channels() {
// get channel number from output speaker layout set by obs
struct obs_audio_info aoi;
obs_get_audio_info(&aoi);
long long recorded_channels = get_audio_channels(aoi.speakers);
return recorded_channels;
}
/*****************************************************************************/
struct rematrix_data {
obs_source_t *context;
size_t channels;
//store the routing information
std::atomic<long> route[MAX_AV_PLANES];
std::atomic<float> gain[MAX_AV_PLANES];
//store a temporary buffer
uint8_t *tmpbuffer[MAX_AV_PLANES];
};
/*****************************************************************************/
static const char *rematrix_name(void *unused) {
UNUSED_PARAMETER(unused);
return MT_("Rematrix");
}
/*****************************************************************************/
static void rematrix_destroy(void *data) {
struct rematrix_data *rematrix = (struct rematrix_data *)data;
for (size_t i = 0; i < rematrix->channels; i++) {
if (rematrix->tmpbuffer[i])
bfree(rematrix->tmpbuffer[i]);
}
bfree(rematrix);
}
/*****************************************************************************/
static void rematrix_update(void *data, obs_data_t *settings) {
struct rematrix_data *rematrix = (struct rematrix_data *)data;
rematrix->channels = audio_output_get_channels(obs_get_audio());
bool route_changed = false;
bool gain_changed = false;
long route[MAX_AV_PLANES];
float gain[MAX_AV_PLANES];
//make enough space for c strings
int pad_digits = (int)floor(log10(abs(MAX_AV_PLANES))) + 1;
//template out the route format
const char* route_name_format = "route %i";
size_t route_len = strlen(route_name_format) + pad_digits;
char* route_name = (char *)calloc(route_len, sizeof(char));
//template out the gain format
const char* gain_name_format = "gain %i";
size_t gain_len = strlen(gain_name_format) + pad_digits;
char* gain_name = (char *)calloc(gain_len, sizeof(char));
//copy the routing over from the settings
for (long long i = 0; i < MAX_AV_PLANES; i++) {
sprintf(route_name, route_name_format, i);
sprintf(gain_name, gain_name_format, i);
route[i] = (int)obs_data_get_int(settings, route_name);
gain[i] = (float)obs_data_get_double(settings, gain_name);
gain[i] = db_to_mul(gain[i]);
long other_route = rematrix->route[i].load();
float other_gain = rematrix->gain[i].load();
if (!rematrix->route[i].compare_exchange_strong(other_route, route[i]))
route_changed = true;
if (!rematrix->gain[i].compare_exchange_strong(other_gain, gain[i]))
gain_changed = true;
}
//don't memory leak
free(route_name);
free(gain_name);
}
/*****************************************************************************/
static void *rematrix_create(obs_data_t *settings, obs_source_t *filter) {
//struct rematrix_data *rematrix = bzalloc(sizeof(*rematrix));
struct rematrix_data *rematrix = (struct rematrix_data *) bzalloc(sizeof(*rematrix));
rematrix->context = filter;
rematrix_update(rematrix, settings);
for (size_t i = 0; i < rematrix->channels; i++) {
rematrix->tmpbuffer[i] = (uint8_t *)bzalloc(MAX_AUDIO_SIZE);
}
return rematrix;
}
/*****************************************************************************/
static struct obs_audio_data *rematrix_filter_audio(void *data,
struct obs_audio_data *audio) {
struct rematrix_data *rematrix = (struct rematrix_data *)data;
const size_t channels = rematrix->channels;
float **fmatrixed_data = (float**)rematrix->tmpbuffer;
float **fdata = (float**)audio->data;
size_t ch_buffer = (audio->frames * sizeof(float));
float route[MAX_AV_PLANES];
float gain[MAX_AV_PLANES];
//prevent race condition
for (size_t c = 0; c < channels; c++) {
route[c] = rematrix->route[c].load();
gain[c] = rematrix->gain[c].load();
}
uint32_t frames = audio->frames;
size_t copy_size = 0;
size_t unprocessed_samples = 0;
//consume AUDIO_OUTPUT_FRAMES or less # of frames
for (size_t chunk = 0; chunk < frames; chunk += AUDIO_OUTPUT_FRAMES) {
//calculate the size of the data we're about to try to copy
if (frames - chunk < AUDIO_OUTPUT_FRAMES)
unprocessed_samples = frames - chunk;
else
unprocessed_samples = AUDIO_OUTPUT_FRAMES;
copy_size = unprocessed_samples * sizeof(float);
//copy data to temporary buffer
for (size_t c = 0; c < channels; c++) {
//valid route copy data to temporary buffer
if (fdata[c] && route[c] >= 0 && route[c] < channels)
memcpy(fmatrixed_data[c],
&fdata[(int)route[c]][chunk],
copy_size);
//not a valid route, mute
else
memset(fmatrixed_data[c], 0, MAX_AUDIO_SIZE);
}
//move data into place and process gain
for (size_t c = 0; c < channels; c++) {
if (!fdata[c])
continue;
for (size_t s = 0; s < unprocessed_samples; s++)
fdata[c][chunk + s] = fmatrixed_data[c][s] * gain[c];
}
//move to next chunk of unprocessed data
}
return audio;
}
/*****************************************************************************/
static void rematrix_defaults(obs_data_t *settings)
{
//make enough space for c strings
int pad_digits = (int)floor(log10(abs(MAX_AV_PLANES))) + 1;
//template out the route format
const char* route_name_format = "route %i";
size_t route_len = strlen(route_name_format) + pad_digits;
char* route_name = (char *)calloc(route_len, sizeof(char));
//template out the gain format
const char* gain_name_format = "gain %i";
size_t gain_len = strlen(gain_name_format) + pad_digits;
char* gain_name = (char *)calloc(gain_len, sizeof(char));
//default is no routing (ordered) -1 or any out of bounds is mute*
for (long long i = 0; i < MAX_AV_PLANES; i++) {
sprintf(route_name, route_name_format, i);
sprintf(gain_name, gain_name_format, i);
obs_data_set_default_int(settings, route_name, i);
obs_data_set_default_double(settings, gain_name, 0.0);
}
obs_data_set_default_string(settings, "profile_name", MT_("Default"));
//don't memory leak
free(gain_name);
free(route_name);
}
/*****************************************************************************/
static bool fill_out_channels(obs_properties_t *props, obs_property_t *list,
obs_data_t *settings) {
obs_property_list_clear(list);
obs_property_list_add_int(list, MT_("mute"), -1);
long long channels = get_obs_output_channels();
//make enough space for c strings
int pad_digits = (int)floor(log10(abs(MAX_AV_PLANES))) + 1;
//template out the format for the json
const char* route_obs_format = "in.ch.%i";
size_t route_obs_len = strlen(route_obs_format) + pad_digits;
char* route_obs = (char *)calloc(route_obs_len, sizeof(char));
for (long long c = 0; c < channels; c++) {
sprintf(route_obs, route_obs_format, c);
obs_property_list_add_int(list, MT_(route_obs), c);
}
//don't memory leak
free(route_obs);
return true;
}
/*****************************************************************************/
static obs_properties_t *rematrix_properties(void *data)
{
UNUSED_PARAMETER(data);
obs_properties_t *props = obs_properties_create();
//make a list long enough for the maximum # of chs
obs_property_t *route[MAX_AV_PLANES];
//pseduo-pan w/ gain (thanks Matt)
obs_property_t *gain[MAX_AV_PLANES];
size_t channels = audio_output_get_channels(obs_get_audio());
//make enough space for c strings
int pad_digits = (int)floor(log10(abs(MAX_AV_PLANES))) + 1;
//template out the route format
const char* route_name_format = "route %i";
size_t route_len = strlen(route_name_format) + pad_digits;
char* route_name = (char *)calloc(route_len, sizeof(char));
//template out the format for the json
const char* route_obs_format = "out.ch.%i";
size_t route_obs_len = strlen(route_obs_format) + pad_digits;
char* route_obs = (char *)calloc(route_obs_len, sizeof(char));
//template out the gain format
const char* gain_name_format = "gain %i";
size_t gain_len = strlen(gain_name_format) + pad_digits;
char* gain_name = (char *)calloc(gain_len, sizeof(char));
//add an appropriate # of options to mix from
for (size_t i = 0; i < channels; i++) {
sprintf(route_name, route_name_format, i);
sprintf(gain_name, gain_name_format, i);
sprintf(route_obs, route_obs_format, i);
route[i] = obs_properties_add_list(props, route_name,
MT_(route_obs), OBS_COMBO_TYPE_LIST,
OBS_COMBO_FORMAT_INT);
obs_property_set_long_description(route[i],
MT_("tooltip"));
obs_property_set_modified_callback(route[i],
fill_out_channels);
gain[i] = obs_properties_add_float_slider(props, gain_name,
MT_("Gain.GainDB"), -30.0, 30.0, 0.1);
}
//don't memory leak
free(gain_name);
free(route_name);
free(route_obs);
return props;
}
/*****************************************************************************/
bool obs_module_load(void)
{
struct obs_source_info rematrixer_filter = {
.id = "rematrix_filter",
.type = OBS_SOURCE_TYPE_FILTER,
.output_flags = OBS_SOURCE_AUDIO,
.get_name = rematrix_name,
.create = rematrix_create,
.destroy = rematrix_destroy,
// .get_width = null,
// .get_height = null,
.get_defaults = rematrix_defaults,
.get_properties = rematrix_properties,
.update = rematrix_update,
// .deactivate = null,
// .show = null,
// .hide = null,
// .video_tick = null,
// .video_render = null,
// .filter_video = null,
.filter_audio = rematrix_filter_audio,
// .enum_active_sources = null,
// .save = null,
// .load = null,
// ... many more
};
obs_register_source(&rematrixer_filter);
return true;
}