summaryrefslogtreecommitdiffstats
path: root/src/server/mix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/mix.cpp')
-rw-r--r--src/server/mix.cpp112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/server/mix.cpp b/src/server/mix.cpp
new file mode 100644
index 00000000..065e3efc
--- /dev/null
+++ b/src/server/mix.cpp
@@ -0,0 +1,112 @@
+/*
+ This file is part of Ingen.
+ Copyright 2007-2015 David Robillard <http://drobilla.net/>
+
+ Ingen is free software: you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or any later version.
+
+ Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with Ingen. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "lv2/atom/util.h"
+
+#include "Buffer.hpp"
+#include "RunContext.hpp"
+#include "mix.hpp"
+
+namespace ingen {
+namespace server {
+
+static inline bool
+is_end(const Buffer* buf, const LV2_Atom_Event* ev)
+{
+ const LV2_Atom* atom = buf->get<const LV2_Atom>();
+ return lv2_atom_sequence_is_end(
+ (const LV2_Atom_Sequence_Body*)LV2_ATOM_BODY_CONST(atom),
+ atom->size,
+ ev);
+}
+
+void
+mix(const RunContext& context,
+ Buffer* dst,
+ const Buffer*const* srcs,
+ uint32_t num_srcs)
+{
+ if (num_srcs == 1) {
+ dst->copy(context, srcs[0]);
+ } else if (dst->is_control()) {
+ Sample* const out = dst->samples();
+ out[0] = srcs[0]->value_at(0);
+ for (uint32_t i = 1; i < num_srcs; ++i) {
+ out[0] += srcs[i]->value_at(0);
+ }
+ } else if (dst->is_audio()) {
+ // Copy the first source
+ dst->copy(context, srcs[0]);
+
+ // Mix in the rest
+ Sample* __restrict const out = dst->samples();
+ const SampleCount end = context.nframes();
+ for (uint32_t i = 1; i < num_srcs; ++i) {
+ const Sample* __restrict const in = srcs[i]->samples();
+ if (srcs[i]->is_control()) { // control => audio
+ for (SampleCount i = 0; i < end; ++i) {
+ out[i] += in[0];
+ }
+ } else if (srcs[i]->is_audio()) { // audio => audio
+ for (SampleCount i = 0; i < end; ++i) {
+ out[i] += in[i];
+ }
+ } else if (srcs[i]->is_sequence()) { // sequence => audio
+ dst->render_sequence(context, srcs[i], true);
+ }
+ }
+ } else if (dst->is_sequence()) {
+ const LV2_Atom_Event* iters[num_srcs];
+ for (uint32_t i = 0; i < num_srcs; ++i) {
+ iters[i] = nullptr;
+ if (srcs[i]->is_sequence()) {
+ const LV2_Atom_Sequence* seq = srcs[i]->get<const LV2_Atom_Sequence>();
+ iters[i] = lv2_atom_sequence_begin(&seq->body);
+ if (is_end(srcs[i], iters[i])) {
+ iters[i] = nullptr;
+ }
+ }
+ }
+
+ while (true) {
+ const LV2_Atom_Event* first = nullptr;
+ uint32_t first_i = 0;
+ for (uint32_t i = 0; i < num_srcs; ++i) {
+ const LV2_Atom_Event* const ev = iters[i];
+ if (!first || (ev && ev->time.frames < first->time.frames)) {
+ first = ev;
+ first_i = i;
+ }
+ }
+
+ if (first) {
+ dst->append_event(
+ first->time.frames, first->body.size, first->body.type,
+ (const uint8_t*)LV2_ATOM_BODY_CONST(&first->body));
+
+ iters[first_i] = lv2_atom_sequence_next(first);
+ if (is_end(srcs[first_i], iters[first_i])) {
+ iters[first_i] = nullptr;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+}
+
+} // namespace server
+} // namespace ingen