Skip to content

Commit

Permalink
Add and test ChannelOffset node
Browse files Browse the repository at this point in the history
  • Loading branch information
ideoforms committed Sep 8, 2024
1 parent 576cc70 commit 08d30d0
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 1 deletion.
29 changes: 29 additions & 0 deletions source/include/signalflow/node/operators/channel-offset.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include "signalflow/core/constants.h"
#include "signalflow/node/node.h"

#include <list>

namespace signalflow
{

/**--------------------------------------------------------------------------------*
* Offsets the input by a specified number of channels. With an N-channel input
* and an offset of M, the output will have M+N channels.
*---------------------------------------------------------------------------------*/
class ChannelOffset : public UnaryOpNode
{

public:
ChannelOffset(int offset = 0, NodeRef input = nullptr);

virtual void process(Buffer &out, int num_frames);

private:
PropertyRef offset;
};

REGISTER(ChannelOffset, "channel-offset")

}
1 change: 1 addition & 0 deletions source/include/signalflow/signalflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <signalflow/node/operators/channel-array.h>
#include <signalflow/node/operators/channel-crossfade.h>
#include <signalflow/node/operators/channel-mixer.h>
#include <signalflow/node/operators/channel-offset.h>
#include <signalflow/node/operators/channel-select.h>
#include <signalflow/node/operators/comparison.h>
#include <signalflow/node/operators/divide.h>
Expand Down
1 change: 1 addition & 0 deletions source/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ set(SRC ${SRC}
${CMAKE_CURRENT_SOURCE_DIR}/node/operators/divide.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/operators/channel-mixer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/operators/channel-array.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/operators/channel-offset.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/operators/channel-select.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/operators/channel-crossfade.cpp
${CMAKE_CURRENT_SOURCE_DIR}/node/operators/frequency-to-midi-note.cpp
Expand Down
37 changes: 37 additions & 0 deletions source/src/node/operators/channel-offset.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "signalflow/node/operators/channel-offset.h"

namespace signalflow
{

ChannelOffset::ChannelOffset(int offset, NodeRef input)
: UnaryOpNode(input), offset(offset)
{
if (!input)
{
throw std::runtime_error("ChannelOffset: No input specified");
}

this->name = "channel-offset";

this->create_property("offset", this->offset);

this->set_channels(this->input->get_num_output_channels(),
this->input->get_num_output_channels() + this->offset->int_value());
}

void ChannelOffset::process(Buffer &out, int num_frames)
{
int output_channel = 0;
for (int channel = 0; channel < this->offset->int_value(); channel++)
{
memset(out[output_channel], 0, num_frames * sizeof(sample));
output_channel++;
}
for (int channel = 0; channel < this->input->get_num_output_channels(); channel++)
{
memcpy(out[output_channel], this->input->out[channel], num_frames * sizeof(sample));
output_channel++;
}
}

}
3 changes: 3 additions & 0 deletions source/src/python/nodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ void init_python_nodes(py::module &m)
py::class_<ChannelMixer, Node, NodeRefTemplate<ChannelMixer>>(m, "ChannelMixer", "Downmix a multichannel input to a lower-channel output. If num_channels is greater than one, spreads the input channels across the field. If amplitude_compensation is enabled, scale down the amplitude based on the ratio of input to output channels.")
.def(py::init<int, NodeRef, bool>(), "num_channels"_a = 1, "input"_a = 0, "amplitude_compensation"_a = true);

py::class_<ChannelOffset, Node, NodeRefTemplate<ChannelOffset>>(m, "ChannelOffset", "Offsets the input by a specified number of channels. With an N-channel input and an offset of M, the output will have M+N channels.")
.def(py::init<int, NodeRef>(), "offset"_a = 0, "input"_a = nullptr);

py::class_<ChannelSelect, Node, NodeRefTemplate<ChannelSelect>>(m, "ChannelSelect", "Select a subset of channels from a multichannel input, starting at offset, up to a maximum of maximum, with the given step.")
.def(py::init<NodeRef, int, int, int>(), "input"_a = nullptr, "offset"_a = 0, "maximum"_a = 0, "step"_a = 1);

Expand Down
19 changes: 18 additions & 1 deletion tests/test_node_operators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from signalflow import Constant, ChannelArray, Sum, SelectInput, Counter, Impulse
from signalflow import Constant, ChannelArray, ChannelOffset, Sum, SelectInput, Counter, Impulse
import numpy as np

from . import graph
Expand Down Expand Up @@ -204,6 +204,23 @@ def test_sum(graph):
assert np.all(c.output_buffer[1] == 6)
assert np.all(c.output_buffer[2] == 6)

def test_channel_offset(graph):
a = ChannelArray([1, 2])
b = ChannelOffset(0, a)
graph.render_subgraph(b, reset=True)
assert b.num_output_channels == 2
assert np.all(b.output_buffer[0] == 1)
assert np.all(b.output_buffer[1] == 2)

b = ChannelOffset(2, a)
graph.render_subgraph(b, reset=True)
assert b.num_output_channels == 4
assert np.all(b.output_buffer[0] == 0)
assert np.all(b.output_buffer[1] == 0)
assert np.all(b.output_buffer[2] == 1)
assert np.all(b.output_buffer[3] == 2)


def test_select_input(graph):
a = SelectInput([1, 2, 3], 0)

Expand Down

0 comments on commit 08d30d0

Please sign in to comment.