130 lines
3.0 KiB
C++
130 lines
3.0 KiB
C++
#include <cassert>
|
|
#include "AudioMerger.h"
|
|
#include "../logger.h"
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
using namespace tc::audio;
|
|
|
|
/* technique based on http://www.vttoth.com/CMS/index.php/technical-notes/68 */
|
|
inline constexpr float merge_ab(float a, float b) {
|
|
/*
|
|
* Form: A,B := [0;n]
|
|
* Z = 2(A + B) - (2/n) * A * B - n
|
|
*
|
|
* For a range from 0 to 2: Z = 2(A + B) - AB - 2
|
|
*/
|
|
|
|
a += 1;
|
|
b += 1;
|
|
|
|
auto result = (2 * (a + b) - a * b - 2) - 1;
|
|
if(result > 1) {
|
|
result = 1;
|
|
} else if(result < -1) {
|
|
result = -1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static_assert(merge_ab(1, 0) == 1);
|
|
static_assert(merge_ab(0, 1) == 1);
|
|
static_assert(merge_ab(1, 1) == 1);
|
|
|
|
bool merge::merge_sources(void *_dest, void *_src_a, void *_src_b, size_t channels, size_t samples) {
|
|
auto dest = (float*) _dest;
|
|
auto src_a = (float*) _src_a;
|
|
auto src_b = (float*) _src_b;
|
|
|
|
for(size_t index = 0; index < channels * samples; index++)
|
|
dest[index] = merge_ab(src_a[index], src_b[index]);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool merge::merge_n_sources(void *dest, void **srcs, size_t src_length, size_t channels, size_t samples) {
|
|
assert(src_length > 0);
|
|
|
|
/* find the first non empty source */
|
|
while(!srcs[0]) {
|
|
srcs++;
|
|
src_length--;
|
|
|
|
if(src_length == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
if(srcs[0] != dest) {
|
|
memcpy(dest, srcs[0], channels * samples * 4);
|
|
}
|
|
|
|
srcs++;
|
|
src_length--;
|
|
|
|
while(src_length > 0) {
|
|
/* only invoke is srcs is not null! */
|
|
if(srcs[0] && !merge::merge_sources(dest, srcs[0], dest, channels, samples)) {
|
|
return false;
|
|
}
|
|
|
|
srcs++;
|
|
src_length--;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#define stack_buffer_length (1024 * 8)
|
|
bool merge::merge_channels_interleaved(void *target, size_t target_channels, const void *src, size_t src_channels, size_t samples) {
|
|
assert(src_channels == 1 || src_channels == 2);
|
|
assert(target_channels == 1 || target_channels == 2);
|
|
|
|
if(src_channels == target_channels) {
|
|
if(src == target) {
|
|
return true;
|
|
}
|
|
|
|
memcpy(target, src, samples * src_channels * 4);
|
|
return true;
|
|
}
|
|
|
|
if(src_channels == 1 && target_channels == 2) {
|
|
auto source_array = (float*) src;
|
|
auto target_array = (float*) target;
|
|
|
|
if(source_array == target_array) {
|
|
if(stack_buffer_length < samples * src_channels) {
|
|
log_error(category::audio, tr("channel merge failed due to large inputs"));
|
|
return false;
|
|
}
|
|
float stack_buffer[stack_buffer_length];
|
|
memcpy(stack_buffer, src, src_channels * samples * 4);
|
|
source_array = stack_buffer;
|
|
|
|
while(samples-- > 0) {
|
|
*(target_array++) = *source_array;
|
|
*(target_array++) = *source_array;
|
|
source_array++;
|
|
}
|
|
} else {
|
|
while(samples-- > 0) {
|
|
*(target_array++) = *source_array;
|
|
*(target_array++) = *source_array;
|
|
source_array++;
|
|
}
|
|
}
|
|
} else if(src_channels == 2 && target_channels == 1) {
|
|
auto source_array = (float*) src;
|
|
auto target_array = (float*) target;
|
|
|
|
while(samples-- > 0) {
|
|
*(target_array++) = merge_ab(*(source_array), *(source_array + 1));
|
|
source_array += 2;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} |