Compare commits

...

15 Commits

Author SHA1 Message Date
Ben Niemann 02392ef186 Fix tests and remove lint. 3 years ago
Ben Niemann 8f050f8f98 Add a stack to the VM. Not used yet. 3 years ago
Ben Niemann 7a6b1586fe Osciallator: ports are a-rate control or audio. 3 years ago
Ben Niemann 0b73148139 Connections track their type. 3 years ago
Ben Niemann 0e827495a3 Really make the property getter/setter methods overrideable. 3 years ago
Ben Niemann b64121b1b6 Simplify and remove some cruft from noisicaa.audioproc.engine.graph. 3 years ago
Ben Niemann 6d304c9644 Some preparations to support multiple port types in the UI. 3 years ago
Ben Niemann 766e2e43b5 Nodes keep track of their connections. 3 years ago
Ben Niemann 437369de10 PortDescription.type changed to repeated field, so ports can have different types. 3 years ago
Ben Niemann d58071654b Buffers track the PortDescription::Type for which they were created. 3 years ago
Ben Niemann 03773d38e8 Fix flaky test. 3 years ago
Ben Niemann 7c87fa182b Add missing build dependency. 3 years ago
Ben Niemann 25cae461bc Processor actually stores the Buffer pointer. 3 years ago
Ben Niemann 7f032e453d Pass a Buffer pointer instead of BufferPtr to Processor::connect_port() 3 years ago
Ben Niemann ba7da1e08d Move connect_port() implementation completely into the Processor base class. 3 years ago
  1. 1
      noisicaa/audioproc/audioproc.proto
  2. 12
      noisicaa/audioproc/audioproc_client.py
  3. 20
      noisicaa/audioproc/audioproc_client_test.py
  4. 7
      noisicaa/audioproc/audioproc_process.py
  5. 3
      noisicaa/audioproc/engine/backend_test.py
  6. 17
      noisicaa/audioproc/engine/buffers.cpp
  7. 20
      noisicaa/audioproc/engine/buffers.h
  8. 17
      noisicaa/audioproc/engine/buffers.pxd
  9. 8
      noisicaa/audioproc/engine/buffers.pyi
  10. 19
      noisicaa/audioproc/engine/buffers.pyx
  11. 12
      noisicaa/audioproc/engine/csound_util.cpp
  12. 2
      noisicaa/audioproc/engine/csound_util.h
  13. 8
      noisicaa/audioproc/engine/fluidsynth_util.cpp
  14. 2
      noisicaa/audioproc/engine/fluidsynth_util.h
  15. 193
      noisicaa/audioproc/engine/graph.py
  16. 2
      noisicaa/audioproc/engine/opcodes.cpp
  17. 3
      noisicaa/audioproc/engine/plugin_host_lv2.cpp
  18. 12
      noisicaa/audioproc/engine/plugin_host_process_test.py
  19. 8
      noisicaa/audioproc/engine/plugin_host_test.py
  20. 63
      noisicaa/audioproc/engine/processor.cpp
  21. 6
      noisicaa/audioproc/engine/processor.h
  22. 4
      noisicaa/audioproc/engine/processor.pxd
  23. 6
      noisicaa/audioproc/engine/processor.pyx
  24. 26
      noisicaa/audioproc/engine/processor_csound_base.cpp
  25. 3
      noisicaa/audioproc/engine/processor_csound_base.h
  26. 96
      noisicaa/audioproc/engine/processor_csound_test.py
  27. 55
      noisicaa/audioproc/engine/processor_faust.cpp
  28. 2
      noisicaa/audioproc/engine/processor_faust.h
  29. 5
      noisicaa/audioproc/engine/processor_null.cpp
  30. 1
      noisicaa/audioproc/engine/processor_null.h
  31. 39
      noisicaa/audioproc/engine/processor_plugin.cpp
  32. 4
      noisicaa/audioproc/engine/processor_plugin.h
  33. 122
      noisicaa/audioproc/engine/processor_plugin_test.py
  34. 14
      noisicaa/audioproc/engine/processor_sound_file.cpp
  35. 2
      noisicaa/audioproc/engine/processor_sound_file.h
  36. 49
      noisicaa/audioproc/engine/processor_sound_file_test.py
  37. 31
      noisicaa/audioproc/engine/realm.cpp
  38. 24
      noisicaa/audioproc/engine/realm.h
  39. 128
      noisicaa/audioproc/engine/realm_test.py
  40. 9
      noisicaa/audioproc/engine/spec_test.pyx
  41. 2
      noisicaa/builtin_nodes/beat_track/node_description.py
  42. 2
      noisicaa/builtin_nodes/control_track/node_description.py
  43. 10
      noisicaa/builtin_nodes/control_track/processor.cpp
  44. 3
      noisicaa/builtin_nodes/control_track/processor.h
  45. 8
      noisicaa/builtin_nodes/custom_csound/model.py
  46. 8
      noisicaa/builtin_nodes/custom_csound/processor_test.py
  47. 4
      noisicaa/builtin_nodes/cv_mapper/node_description.py
  48. 23
      noisicaa/builtin_nodes/cv_mapper/processor.cpp
  49. 3
      noisicaa/builtin_nodes/cv_mapper/processor.h
  50. 6
      noisicaa/builtin_nodes/instrument/node_description.py
  51. 12
      noisicaa/builtin_nodes/instrument/processor.cpp
  52. 3
      noisicaa/builtin_nodes/instrument/processor.h
  53. 1
      noisicaa/builtin_nodes/metronome/CMakeLists.txt
  54. 4
      noisicaa/builtin_nodes/metronome/node_description.py
  55. 18
      noisicaa/builtin_nodes/metronome/processor.cpp
  56. 2
      noisicaa/builtin_nodes/metronome/processor.h
  57. 2
      noisicaa/builtin_nodes/midi_cc_to_cv/model.py
  58. 2
      noisicaa/builtin_nodes/midi_cc_to_cv/node_description.py
  59. 16
      noisicaa/builtin_nodes/midi_cc_to_cv/processor.cpp
  60. 2
      noisicaa/builtin_nodes/midi_cc_to_cv/processor.h
  61. 2
      noisicaa/builtin_nodes/midi_cc_to_cv/processor_test.py
  62. 4
      noisicaa/builtin_nodes/midi_looper/node_description.py
  63. 16
      noisicaa/builtin_nodes/midi_looper/processor.cpp
  64. 3
      noisicaa/builtin_nodes/midi_looper/processor.h
  65. 2
      noisicaa/builtin_nodes/midi_monitor/node_description.py
  66. 19
      noisicaa/builtin_nodes/midi_monitor/processor.cpp
  67. 3
      noisicaa/builtin_nodes/midi_monitor/processor.h
  68. 2
      noisicaa/builtin_nodes/midi_source/node_description.py
  69. 11
      noisicaa/builtin_nodes/midi_source/processor.cpp
  70. 3
      noisicaa/builtin_nodes/midi_source/processor.h
  71. 4
      noisicaa/builtin_nodes/midi_velocity_mapper/node_description.py
  72. 23
      noisicaa/builtin_nodes/midi_velocity_mapper/processor.cpp
  73. 3
      noisicaa/builtin_nodes/midi_velocity_mapper/processor.h
  74. 16
      noisicaa/builtin_nodes/mixer/node_description.py
  75. 5
      noisicaa/builtin_nodes/mixer/processor.cpp
  76. 4
      noisicaa/builtin_nodes/oscillator/processor.dsp
  77. 6
      noisicaa/builtin_nodes/oscilloscope/node_description.py
  78. 88
      noisicaa/builtin_nodes/oscilloscope/node_ui.py
  79. 45
      noisicaa/builtin_nodes/oscilloscope/processor.cpp
  80. 3
      noisicaa/builtin_nodes/oscilloscope/processor.h
  81. 12
      noisicaa/builtin_nodes/pianoroll/processor.cpp
  82. 3
      noisicaa/builtin_nodes/pianoroll/processor.h
  83. 4
      noisicaa/builtin_nodes/sample_track/node_description.py
  84. 13
      noisicaa/builtin_nodes/sample_track/processor.cpp
  85. 3
      noisicaa/builtin_nodes/sample_track/processor.h
  86. 2
      noisicaa/builtin_nodes/score_track/node_description.py
  87. 2
      noisicaa/builtin_nodes/step_sequencer/model.py
  88. 2
      noisicaa/builtin_nodes/step_sequencer/node_description.py
  89. 18
      noisicaa/builtin_nodes/step_sequencer/processor.cpp
  90. 3
      noisicaa/builtin_nodes/step_sequencer/processor.h
  91. 2
      noisicaa/builtin_nodes/step_sequencer/processor_test.py
  92. 2
      noisicaa/music/__init__.py
  93. 146
      noisicaa/music/graph.py
  94. 6
      noisicaa/music/model.desc.pb
  95. 4
      noisicaa/music/model.py
  96. 38
      noisicaa/music/project.py
  97. 8
      noisicaa/node_db/faust_parser.py
  98. 7
      noisicaa/node_db/faust_parser_test.py
  99. 13
      noisicaa/node_db/node_description.proto
  100. 14
      noisicaa/node_db/private/builtin_scanner.py
  101. Some files were not shown because too many files have changed in this diff Show More

1
noisicaa/audioproc/audioproc.proto

@ -53,6 +53,7 @@ message ConnectPorts {
required string src_port = 2;
required string dest_node_id = 3;
required string dest_port = 4;
required noisicaa.pb.PortDescription.Type type = 5;
}
message DisconnectPorts {

12
noisicaa/audioproc/audioproc_client.py

@ -84,7 +84,8 @@ class AbstractAudioProcClient(object):
raise NotImplementedError
async def connect_ports(
self, realm: str, node1_id: str, port1_name: str, node2_id: str, port2_name: str
self, realm: str, node1_id: str, port1_name: str, node2_id: str, port2_name: str,
type: node_db.PortDescription.Type, # pylint: disable=redefined-builtin
) -> None:
raise NotImplementedError
@ -262,7 +263,11 @@ class AudioProcClient(AbstractAudioProcClient):
remove_node=audioproc_pb2.RemoveNode(id=node_id)))
async def connect_ports(
self, realm: str, node1_id: str, port1_name: str, node2_id: str, port2_name: str
self,
realm: str,
node1_id: str, port1_name: str,
node2_id: str, port2_name: str,
type: node_db.PortDescription.Type, # pylint: disable=redefined-builtin
) -> None:
await self.pipeline_mutation(
realm,
@ -271,7 +276,8 @@ class AudioProcClient(AbstractAudioProcClient):
src_node_id=node1_id,
src_port=port1_name,
dest_node_id=node2_id,
dest_port=port2_name)))
dest_port=port2_name,
type=type)))
async def disconnect_ports(
self, realm: str, node1_id: str, port1_name: str, node2_id: str, port2_name: str

20
noisicaa/audioproc/audioproc_client_test.py

@ -50,22 +50,22 @@ class AudioProcClientTest(
node_db.PortDescription(
name='in:left',
direction=node_db.PortDescription.INPUT,
type=node_db.PortDescription.AUDIO,
types=[node_db.PortDescription.AUDIO],
),
node_db.PortDescription(
name='in:right',
direction=node_db.PortDescription.INPUT,
type=node_db.PortDescription.AUDIO,
types=[node_db.PortDescription.AUDIO],
),
node_db.PortDescription(
name='out:left',
direction=node_db.PortDescription.OUTPUT,
type=node_db.PortDescription.AUDIO,
types=[node_db.PortDescription.AUDIO],
),
node_db.PortDescription(
name='out:right',
direction=node_db.PortDescription.OUTPUT,
type=node_db.PortDescription.AUDIO,
types=[node_db.PortDescription.AUDIO],
),
],
processor=node_db.ProcessorDescription(
@ -107,8 +107,10 @@ class AudioProcClientTest(
await client.add_node(
'root',
id='child', child_realm='test', description=node_db.Builtins.ChildRealmDescription)
await client.connect_ports('root', 'child', 'out:left', 'sink', 'in:left')
await client.connect_ports('root', 'child', 'out:right', 'sink', 'in:right')
await client.connect_ports(
'root', 'child', 'out:left', 'sink', 'in:left', node_db.PortDescription.AUDIO)
await client.connect_ports(
'root', 'child', 'out:right', 'sink', 'in:right', node_db.PortDescription.AUDIO)
await client.disconnect_ports('root', 'child', 'out:left', 'sink', 'in:left')
await client.disconnect_ports('root', 'child', 'out:right', 'sink', 'in:right')
@ -125,13 +127,15 @@ class AudioProcClientTest(
async with self.create_process() as client:
await client.add_node('root', id='node1', description=self.passthru_description)
await client.add_node('root', id='node2', description=self.passthru_description)
await client.connect_ports('root', 'node1', 'out:left', 'node2', 'in:left')
await client.connect_ports(
'root', 'node1', 'out:left', 'node2', 'in:left', node_db.PortDescription.AUDIO)
async def test_disconnect_ports(self):
async with self.create_process() as client:
await client.add_node('root', id='node1', description=self.passthru_description)
await client.add_node('root', id='node2', description=self.passthru_description)
await client.connect_ports('root', 'node1', 'out:left', 'node2', 'in:left')
await client.connect_ports(
'root', 'node1', 'out:left', 'node2', 'in:left', node_db.PortDescription.AUDIO)
await client.disconnect_ports('root', 'node1', 'out:left', 'node2', 'in:left')
async def test_plugin_node(self):

7
noisicaa/audioproc/audioproc_process.py

@ -322,7 +322,8 @@ class AudioProcProcess(core.ProcessBase):
"Node %s (%s) has no port %s"
% (node2.id, type(node2).__name__, connect_ports.dest_port)
).with_traceback(sys.exc_info()[2]) from None
port2.connect(port1)
port2.connect(port1, connect_ports.type)
realm.update_spec()
elif mutation_type == 'disconnect_ports':
@ -439,8 +440,8 @@ class AudioProcProcess(core.ProcessBase):
await realm.setup_node(node)
sink = realm.graph.find_node('sink')
sink.inputs['in:left'].connect(node.outputs['out:left'])
sink.inputs['in:right'].connect(node.outputs['out:right'])
sink.inputs['in:left'].connect(node.outputs['out:left'], node_db.PortDescription.AUDIO)
sink.inputs['in:right'].connect(node.outputs['out:right'], node_db.PortDescription.AUDIO)
realm.update_spec()
sound_file_complete_urid = self.__urid_mapper.map(

3
noisicaa/audioproc/engine/backend_test.py

@ -21,6 +21,7 @@
from noisidev import unittest
from noisidev import unittest_engine_mixins
from noisidev import unittest_engine_utils
from noisicaa import node_db
from noisicaa.audioproc.public import backend_settings_pb2
from . import buffers
from .realm import PyRealm
@ -39,7 +40,7 @@ class BackendTest(unittest_engine_mixins.HostSystemMixin, unittest.TestCase):
backend.setup(realm)
bufmgr = unittest_engine_utils.BufferManager(self.host_system)
bufmgr.allocate('samples', buffers.PyFloatAudioBlockBuffer())
bufmgr.allocate('samples', buffers.PyFloatAudioBlockBuffer(node_db.PortDescription.AUDIO))
samples = bufmgr['samples']
ctxt = PyBlockContext()

17
noisicaa/audioproc/engine/buffers.cpp

@ -31,6 +31,11 @@
namespace noisicaa {
BufferType::BufferType(pb::PortDescription::Type type)
: _type(type) {}
BufferType::~BufferType() {}
Status BufferType::setup(HostSystem* host_system, BufferPtr buf) const {
return Status::Ok();
}
@ -38,6 +43,9 @@ Status BufferType::setup(HostSystem* host_system, BufferPtr buf) const {
void BufferType::cleanup(HostSystem* host_system, BufferPtr buf) const {
}
FloatControlValueBuffer::FloatControlValueBuffer()
: BufferType(pb::PortDescription::KRATE_CONTROL) {}
uint32_t FloatControlValueBuffer::size(HostSystem* host_system) const {
return sizeof(ControlValue);
}
@ -66,6 +74,9 @@ Status FloatControlValueBuffer::mul_buffer(
return Status::Ok();
}
FloatAudioBlockBuffer::FloatAudioBlockBuffer(pb::PortDescription::Type type)
: BufferType(type) {}
uint32_t FloatAudioBlockBuffer::size(HostSystem* host_system) const {
return host_system->block_size() * sizeof(float);
}
@ -96,6 +107,9 @@ Status FloatAudioBlockBuffer::mul_buffer(HostSystem* host_system, BufferPtr buf,
return Status::Ok();
}
AtomDataBuffer::AtomDataBuffer()
: BufferType(pb::PortDescription::EVENTS) {}
uint32_t AtomDataBuffer::size(HostSystem* host_system) const {
return 10240;
}
@ -177,6 +191,9 @@ Status AtomDataBuffer::mul_buffer(HostSystem* host_system, BufferPtr buf, float
return ERROR_STATUS("Operation not supported for AtomDataBuffer");
}
PluginCondBuffer::PluginCondBuffer()
: BufferType(pb::PortDescription::INTERNAL_TYPE) {}
uint32_t PluginCondBuffer::size(HostSystem* host_system) const {
return sizeof(PluginCond);
}

20
noisicaa/audioproc/engine/buffers.h

@ -30,6 +30,7 @@
#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
#include "noisicaa/core/status.h"
#include "noisicaa/node_db/node_description.pb.h"
namespace noisicaa {
@ -42,9 +43,12 @@ typedef BufferData* BufferPtr;
class BufferType {
public:
virtual ~BufferType() {}
virtual ~BufferType();
virtual uint32_t size(HostSystem* host_system) const = 0;
pb::PortDescription::Type type() const {
return _type;
}
virtual Status setup(HostSystem* host_system, BufferPtr buf) const;
virtual void cleanup(HostSystem* host_system, BufferPtr buf) const;
@ -52,6 +56,12 @@ public:
virtual Status clear_buffer(HostSystem* host_system, BufferPtr buf) const = 0;
virtual Status mix_buffers(HostSystem* host_system, const BufferPtr buf1, BufferPtr buf2) const = 0;
virtual Status mul_buffer(HostSystem* host_system, BufferPtr buf, float factor) const = 0;
protected:
BufferType(pb::PortDescription::Type type);
private:
pb::PortDescription::Type _type;
};
class FloatControlValueBuffer : public BufferType {
@ -61,6 +71,8 @@ public:
uint32_t generation;
};
FloatControlValueBuffer();
uint32_t size(HostSystem* host_system) const override;
Status clear_buffer(HostSystem* host_system, BufferPtr buf) const override;
@ -70,6 +82,8 @@ public:
class FloatAudioBlockBuffer : public BufferType {
public:
FloatAudioBlockBuffer(pb::PortDescription::Type type);
uint32_t size(HostSystem* host_system) const override;
Status clear_buffer(HostSystem* host_system, BufferPtr buf) const override;
@ -79,6 +93,8 @@ public:
class AtomDataBuffer : public BufferType {
public:
AtomDataBuffer();
uint32_t size(HostSystem* host_system) const override;
Status clear_buffer(HostSystem* host_system, BufferPtr buf) const override;
@ -88,6 +104,8 @@ public:
class PluginCondBuffer : public BufferType {
public:
PluginCondBuffer();
uint32_t size(HostSystem* host_system) const override;
Status setup(HostSystem* host_system, BufferPtr buf) const override;

17
noisicaa/audioproc/engine/buffers.pxd

@ -25,6 +25,12 @@ from noisicaa.core.status cimport Status
from noisicaa.host_system.host_system cimport HostSystem
cdef extern from "noisicaa/node_db/node_description.pb.h" namespace "noisicaa::pb" nogil:
enum PortDescription_Type: pass
cppclass PortDescription:
ctypedef PortDescription_Type Type
cdef extern from "noisicaa/audioproc/engine/buffers.h" namespace "noisicaa" nogil:
ctypedef uint8_t* BufferPtr
@ -41,7 +47,7 @@ cdef extern from "noisicaa/audioproc/engine/buffers.h" namespace "noisicaa" nogi
pass
cppclass FloatAudioBlockBuffer(BufferType):
pass
FloatAudioBlockBuffer(PortDescription.Type type)
cppclass AtomDataBuffer(BufferType):
pass
@ -72,3 +78,12 @@ cdef class PyBufferType(object):
cdef BufferType* get(self) nogil
cdef BufferType* release(self) nogil
cdef class PyBuffer(object):
cdef PyBufferType __type
cdef Buffer* __buffer
cdef unique_ptr[Buffer] __buffer_ref
cdef Buffer* get(self) nogil
cdef Buffer* release(self) nogil

8
noisicaa/audioproc/engine/buffers.pyi

@ -20,6 +20,8 @@
from typing import List
from noisicaa import node_db
from noisicaa import host_system
class PyBufferType(object):
@property
@ -34,7 +36,7 @@ class PyFloatControlValueBuffer(PyBufferType):
class PyFloatAudioBlockBuffer(PyBufferType):
def __init__(self) -> None: ...
def __init__(self, type: node_db.PortDescription.Type) -> None: ...
def __str__(self) -> str: ...
@property
def view_type(self) -> str: ...
@ -55,3 +57,7 @@ class PyPluginCondBuffer(PyBufferType):
def set_cond(self, buf: List) -> None: ...
def clear_cond(self, buf: List) -> None: ...
def wait_cond(self, buf: List) -> None: ...
class PyBuffer(object):
def __init__(self, host_system: host_system.HostSystem, buf_type: PyBufferType, buf: bytes) -> None: ...

19
noisicaa/audioproc/engine/buffers.pyx

@ -19,6 +19,7 @@
# @end:license
from noisicaa.core.status cimport check
from noisicaa.host_system.host_system cimport PyHostSystem
cdef class PyBufferType(object):
@ -47,8 +48,8 @@ cdef class PyFloatControlValueBuffer(PyBufferType):
cdef class PyFloatAudioBlockBuffer(PyBufferType):
def __init__(self):
self.__type_ref.reset(new FloatAudioBlockBuffer())
def __init__(self, type):
self.__type_ref.reset(new FloatAudioBlockBuffer(type))
self.__type = self.__type_ref.get()
def __str__(self):
@ -101,3 +102,17 @@ cdef class PyPluginCondBuffer(PyBufferType):
cdef BufferPtr c_buf = <BufferPtr>&buf[0]
with nogil:
check(c_type.wait_cond(c_buf))
cdef class PyBuffer(object):
def __init__(self, PyHostSystem host_system, PyBufferType buf_type, uint8_t[:] buf):
self.__type = buf_type
self.__buffer_ref.reset(new Buffer(host_system.get(), self.__type.get(), <BufferPtr>&buf[0]))
self.__buffer = self.__buffer_ref.get()
cdef Buffer* get(self) nogil:
return self.__buffer
cdef Buffer* release(self) nogil:
return self.__buffer_ref.release()

12
noisicaa/audioproc/engine/csound_util.cpp

@ -177,14 +177,14 @@ Status CSoundUtil::setup(
}
Status CSoundUtil::process_block(
BlockContext* ctxt, TimeMapper* time_mapper, vector<BufferPtr>& buffers) {
BlockContext* ctxt, TimeMapper* time_mapper, vector<Buffer*>& buffers) {
assert(buffers.size() == (size_t)_ports.size());
for (size_t port_idx = 0 ; port_idx < _ports.size() ; ++port_idx) {
const auto& port = _ports[port_idx];
if (port.direction == pb::PortDescription::INPUT
&& port.type == pb::PortDescription::EVENTS) {
LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)buffers[port_idx];
LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)buffers[port_idx]->data();
if (seq->atom.type != _host_system->lv2->urid.atom_sequence) {
return ERROR_STATUS(
"Excepted sequence in port '%s', got %d.", port.name.c_str(), seq->atom.type);
@ -213,7 +213,7 @@ Status CSoundUtil::process_block(
switch (port.type) {
case pb::PortDescription::AUDIO:
case pb::PortDescription::ARATE_CONTROL: {
float* buf = (float*)buffers[port_idx];
float* buf = (float*)buffers[port_idx]->data();
buf += pos;
MYFLT* channel_ptr = _channel_ptr[port_idx];
@ -227,7 +227,7 @@ Status CSoundUtil::process_block(
}
case pb::PortDescription::KRATE_CONTROL: {
float* buf = (float*)buffers[port_idx];
float* buf = (float*)buffers[port_idx]->data();
MYFLT* channel_ptr = _channel_ptr[port_idx];
int *lock = _channel_lock[port_idx];
@ -334,7 +334,7 @@ Status CSoundUtil::process_block(
switch (port.type) {
case pb::PortDescription::AUDIO:
case pb::PortDescription::ARATE_CONTROL: {
float* buf = (float*)buffers[port_idx];
float* buf = (float*)buffers[port_idx]->data();
buf += pos;
MYFLT* channel_ptr = _channel_ptr[port_idx];
@ -348,7 +348,7 @@ Status CSoundUtil::process_block(
}
case pb::PortDescription::KRATE_CONTROL: {
float* buf = (float*)buffers[port_idx];
float* buf = (float*)buffers[port_idx]->data();
MYFLT* channel_ptr = _channel_ptr[port_idx];
int *lock = _channel_lock[port_idx];

2
noisicaa/audioproc/engine/csound_util.h

@ -59,7 +59,7 @@ public:
};
Status setup(const string& orchestra, const string& score, const vector<PortSpec>& ports);
Status process_block(BlockContext* ctxt, TimeMapper* time_mapper, vector<BufferPtr>& buffers);
Status process_block(BlockContext* ctxt, TimeMapper* time_mapper, vector<Buffer*>& buffers);
private:
Logger* _logger;

8
noisicaa/audioproc/engine/fluidsynth_util.cpp

@ -115,18 +115,18 @@ Status FluidSynthUtil::setup(const string& path, uint32_t bank, uint32_t preset)
}
Status FluidSynthUtil::process_block(
BlockContext* ctxt, TimeMapper* time_mapper, vector<BufferPtr>& buffers) {
BlockContext* ctxt, TimeMapper* time_mapper, vector<Buffer*>& buffers) {
assert(buffers.size() == 3);
LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)buffers[0];
LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)buffers[0]->data();
if (seq->atom.type != _host_system->lv2->urid.atom_sequence) {
return ERROR_STATUS("Excepted sequence in port 'in', got %d.", seq->atom.type);
}
LV2_Atom_Event* event = lv2_atom_sequence_begin(&seq->body);
uint32_t segment_start = 0;
float* out_left = (float*)buffers[1];
float* out_right = (float*)buffers[2];
float* out_left = (float*)buffers[1]->data();
float* out_right = (float*)buffers[2]->data();
while (!lv2_atom_sequence_is_end(&seq->body, seq->atom.size, event)) {
if (event->body.type == _host_system->lv2->urid.midi_event) {
uint32_t esample_pos;

2
noisicaa/audioproc/engine/fluidsynth_util.h

@ -47,7 +47,7 @@ public:
~FluidSynthUtil();
Status setup(const string& path, uint32_t bank, uint32_t preset);
Status process_block(BlockContext* ctxt, TimeMapper* time_mapper, vector<BufferPtr>& buffers);
Status process_block(BlockContext* ctxt, TimeMapper* time_mapper, vector<Buffer*>& buffers);
private:
Logger* _logger;

193
noisicaa/audioproc/engine/graph.py

@ -51,7 +51,9 @@ class GraphError(Exception):
class Port(object):
def __init__(self, *, description: node_db.PortDescription) -> None:
self.__description = description
self.__current_type = None # type: node_db.PortDescription.Type
self.owner = None # type: Node
self.connections = [] # type: List[Port]
def __str__(self) -> str:
return '<%s %s:%s>' % (
@ -71,36 +73,59 @@ class Port(object):
def buf_name(self) -> str:
return '%s:%s' % (self.owner.id, self.__description.name)
@property
def current_type(self) -> node_db.PortDescription.Type:
if self.__current_type is None:
return self.__description.types[0]
return self.__current_type
def connect(self, port: 'Port', conn_type: node_db.PortDescription.Type) -> None:
if self.__current_type is None:
self.__current_type = conn_type
else:
assert conn_type == self.__current_type
self.connections.append(port)
def disconnect(self, port: 'Port') -> None:
assert port in self.connections
self.connections.remove(port)
if not self.connections:
self.__current_type = None
def get_buf_type(self) -> buffers.PyBufferType:
raise NotImplementedError(type(self).__name__)
port_type = self.current_type
if port_type == node_db.PortDescription.AUDIO:
return buffers.PyFloatAudioBlockBuffer(node_db.PortDescription.AUDIO)
elif port_type == node_db.PortDescription.ARATE_CONTROL:
return buffers.PyFloatAudioBlockBuffer(node_db.PortDescription.ARATE_CONTROL)
elif port_type == node_db.PortDescription.KRATE_CONTROL:
return buffers.PyFloatControlValueBuffer()
elif port_type == node_db.PortDescription.EVENTS:
return buffers.PyAtomDataBuffer()
else:
raise ValueError(port_type)
def set_prop(self, **kwargs: Any) -> None:
assert not kwargs
class InputPortMixin(Port):
# pylint: disable=abstract-method
class InputPort(Port):
def connect(self, port: Port, conn_type: node_db.PortDescription.Type) -> None:
if not isinstance(port, OutputPort):
raise GraphError("Can only connect to output ports")
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self.inputs = [] # type: List[Port]
if not set(self.description.types) & set(port.description.types):
raise GraphError("Incompatible port types")
def connect(self, port: Port) -> None:
self.check_port(port)
self.inputs.append(port)
super().connect(port, conn_type)
port.connect(self, conn_type)
def disconnect(self, port: Port) -> None:
assert port in self.inputs, port
self.inputs.remove(port)
def check_port(self, port: Port) -> None:
if not isinstance(port, OutputPortMixin):
raise GraphError("Can only connect to output ports")
super().disconnect(port)
port.disconnect(self)
class OutputPortMixin(Port):
# pylint: disable=abstract-method
class OutputPort(Port):
def __init__(self, *, bypass_port: Optional[str] = None, **kwargs: Any) -> None:
super().__init__(**kwargs)
self._bypass = False
@ -125,111 +150,9 @@ class OutputPortMixin(Port):
self.bypass = bypass
class AudioPortMixin(Port):
def get_buf_type(self) -> buffers.PyBufferType:
return buffers.PyFloatAudioBlockBuffer()
class ARateControlPortMixin(Port):
def get_buf_type(self) -> buffers.PyBufferType:
return buffers.PyFloatAudioBlockBuffer()
class KRateControlPortMixin(Port):
def get_buf_type(self) -> buffers.PyBufferType:
return buffers.PyFloatControlValueBuffer()
class EventPortMixin(Port):
def get_buf_type(self) -> buffers.PyBufferType:
return buffers.PyAtomDataBuffer()
class AudioInputPort(AudioPortMixin, InputPortMixin, Port):
def check_port(self, port: Port) -> None:
super().check_port(port)
if not isinstance(port, AudioOutputPort):
raise GraphError("Can only connect to AudioOutputPort")
class AudioOutputPort(AudioPortMixin, OutputPortMixin, Port):
def __init__(self, *, drywet_port: Optional[str] = None, **kwargs: Any) -> None:
super().__init__(**kwargs)
self._drywet = 0.0
self._drywet_port = drywet_port
@property
def drywet_port(self) -> str:
return self._drywet_port
@property
def drywet(self) -> float:
return self._drywet
@drywet.setter
def drywet(self, value: float) -> None:
value = float(value)
if value < -100.0 or value > 100.0:
raise ValueError("Invalid dry/wet value.")
self._drywet = float(value)
def set_prop(self, *, drywet: Optional[float] = None, **kwargs: Any) -> None:
super().set_prop(**kwargs)
if drywet is not None:
self.drywet = drywet
class ARateControlInputPort(ARateControlPortMixin, InputPortMixin, Port):
def check_port(self, port: Port) -> None:
super().check_port(port)
if not isinstance(port, ARateControlOutputPort):
raise GraphError("Can only connect to ARateControlOutputPort")
class ARateControlOutputPort(ARateControlPortMixin, OutputPortMixin, Port):
pass
class KRateControlInputPort(KRateControlPortMixin, InputPortMixin, Port):
def check_port(self, port: Port) -> None:
super().check_port(port)
if not isinstance(port, KRateControlOutputPort):
raise GraphError("Can only connect to KRateControlOutputPort")
class KRateControlOutputPort(KRateControlPortMixin, OutputPortMixin, Port):
pass
class EventInputPort(EventPortMixin, InputPortMixin, Port):
def check_port(self, port: Port) -> None:
super().check_port(port)
if not isinstance(port, EventOutputPort):
raise GraphError("Can only connect to EventOutputPort")
class EventOutputPort(EventPortMixin, OutputPortMixin, Port):
pass
port_cls_map = {
(node_db.PortDescription.AUDIO,
node_db.PortDescription.INPUT): AudioInputPort,
(node_db.PortDescription.AUDIO,
node_db.PortDescription.OUTPUT): AudioOutputPort,
(node_db.PortDescription.ARATE_CONTROL,
node_db.PortDescription.INPUT): ARateControlInputPort,
(node_db.PortDescription.ARATE_CONTROL,
node_db.PortDescription.OUTPUT): ARateControlOutputPort,
(node_db.PortDescription.KRATE_CONTROL,
node_db.PortDescription.INPUT): KRateControlInputPort,
(node_db.PortDescription.KRATE_CONTROL,
node_db.PortDescription.OUTPUT): KRateControlOutputPort,
(node_db.PortDescription.EVENTS,
node_db.PortDescription.INPUT): EventInputPort,
(node_db.PortDescription.EVENTS,
node_db.PortDescription.OUTPUT): EventOutputPort,
node_db.PortDescription.INPUT: InputPort,
node_db.PortDescription.OUTPUT: OutputPort,
}
@ -252,8 +175,8 @@ class Node(object):
self.__realm = None # type: realm_lib.PyRealm
self.broken = False
self.ports = [] # type: List[Port]
self.inputs = {} # type: Dict[str, InputPortMixin]
self.outputs = {} # type: Dict[str, OutputPortMixin]
self.inputs = {} # type: Dict[str, InputPort]
self.outputs = {} # type: Dict[str, OutputPort]
self.__control_values = {} # type: Dict[str, control_value.PyControlValue]
self.__port_properties = {} # type: Dict[str, node_port_properties_pb2.NodePortProperties]
@ -294,8 +217,7 @@ class Node(object):
return self.__realm is realm
def __create_port(self, port_desc: node_db.PortDescription) -> Port:
port_cls = port_cls_map[
(port_desc.type, port_desc.direction)]
port_cls = port_cls_map[port_desc.direction]
kwargs = {}
if port_desc.HasField('bypass_port'):
@ -310,17 +232,17 @@ class Node(object):
def __add_port(self, port: Port) -> None:
self.ports.append(port)
if isinstance(port, InputPortMixin):
if isinstance(port, InputPort):
self.inputs[port.name] = port
else:
assert isinstance(port, OutputPortMixin)
assert isinstance(port, OutputPort)
self.outputs[port.name] = port
@property
def parent_nodes(self) -> List['Node']:
parents = [] # type: List[Node]
for port in self.inputs.values():
for upstream_port in port.inputs:
for upstream_port in port.connections:
parents.append(upstream_port.owner)
return parents
@ -332,7 +254,8 @@ class Node(object):
logger.info("%s: setup()", self.name)
for port in self.ports:
if isinstance(port, (KRateControlInputPort, ARateControlInputPort)):
if set(port.description.types) & {node_db.PortDescription.KRATE_CONTROL,
node_db.PortDescription.ARATE_CONTROL}:
if port.buf_name not in self.__control_values:
logger.info("New float control value '%s'", port.buf_name)
cv = control_value.PyFloatControlValue(
@ -377,7 +300,7 @@ class Node(object):
changed = {
name for name, port_desc in new.items()
if (name in old
and (port_desc.type != old[name].type
and (port_desc.types != old[name].types
or port_desc.direction != old[name].direction))
}
@ -435,19 +358,19 @@ class Node(object):
spec.append_buffer(port.buf_name, port.get_buf_type())
if port.buf_name in self.__control_values and not port_properties.exposed:
if isinstance(port, KRateControlPortMixin):
if port.current_type == node_db.PortDescription.KRATE_CONTROL:
spec.append_opcode(
'FETCH_CONTROL_VALUE',
self.__control_values[port.buf_name], port.buf_name)
else:
assert isinstance(port, ARateControlPortMixin)
assert port.current_type == node_db.PortDescription.ARATE_CONTROL
spec.append_opcode(
'FETCH_CONTROL_VALUE_TO_AUDIO',
self.__control_values[port.buf_name], port.buf_name)
elif isinstance(port, InputPortMixin):
elif isinstance(port, InputPort):
spec.append_opcode('CLEAR', port.buf_name)
for upstream_port in port.inputs:
for upstream_port in port.connections:
spec.append_opcode('MIX', upstream_port.buf_name, port.buf_name)
def add_to_spec_post(self, spec: spec_lib.PySpec) -> None:

2
noisicaa/audioproc/engine/opcodes.cpp

@ -220,7 +220,7 @@ Status init_CONNECT_PORT(BlockContext* ctxt, ProgramState* state, const vector<O
int buf_idx = args[2].int_value();
Processor* processor = state->program->spec->get_processor(processor_idx);
Buffer* buf = state->program->buffers[buf_idx].get();
processor->connect_port(ctxt, port_idx, buf->data());
processor->connect_port(ctxt, port_idx, buf);
return Status::Ok();
}

3
noisicaa/audioproc/engine/plugin_host_lv2.cpp

@ -94,9 +94,10 @@ Status PluginHostLV2::setup() {
_portmap.resize(_spec.node_description().ports_size());
for (int idx = 0 ; idx < _spec.node_description().ports_size() ; ++idx ) {
const auto& port = _spec.node_description().ports(idx);
assert(port.types_size() == 1);
if (port.direction() == pb::PortDescription::INPUT
and port.type() == pb::PortDescription::KRATE_CONTROL) {
and port.types(0) == pb::PortDescription::KRATE_CONTROL) {
_rt_control_values[idx] = ControlValue{0.0, 0};
_control_values[idx] = ControlValue{0.0, 1};
}

12
noisicaa/audioproc/engine/plugin_host_process_test.py

@ -138,24 +138,24 @@ class PluginHostProcessTest(
bufp = {}
buffers = []
for port_index, port_spec in enumerate(spec.node_description.ports):
if port_spec.type == node_db.PortDescription.AUDIO:
if port_spec.types[0] == node_db.PortDescription.AUDIO:
bufsize = block_size * 4
elif port_spec.type == node_db.PortDescription.KRATE_CONTROL:
elif port_spec.types[0] == node_db.PortDescription.KRATE_CONTROL:
bufsize = 4
else:
raise ValueError(port_spec.type)
raise ValueError(port_spec.types[0])
buffers.append((port_index, offset))
p = shm_data[offset:offset+bufsize]
if port_spec.type == node_db.PortDescription.AUDIO:
if port_spec.types[0] == node_db.PortDescription.AUDIO:
# TODO: mypy doesn't know memoryview.cast
bufp[port_spec.name] = p.cast('f') # type: ignore
elif port_spec.type == node_db.PortDescription.KRATE_CONTROL:
elif port_spec.types[0] == node_db.PortDescription.KRATE_CONTROL:
# TODO: mypy doesn't know memoryview.cast
bufp[port_spec.name] = p.cast('f') # type: ignore
else:
raise ValueError(port_spec.type)
raise ValueError(port_spec.types[0])
offset += bufsize

8
noisicaa/audioproc/engine/plugin_host_test.py

@ -41,17 +41,17 @@ class PluginHostMixin(unittest_mixins.NodeDBMixin, unittest_engine_mixins.HostSy
bufp = {}
for idx, port in enumerate(spec.node_description.ports):
if port.type == node_db.PortDescription.AUDIO:
if port.types[0] == node_db.PortDescription.AUDIO:
bufsize = block_size * 4
valuetype = 'f'
elif port.type == node_db.PortDescription.KRATE_CONTROL:
elif port.types[0] == node_db.PortDescription.KRATE_CONTROL:
bufsize = 4
valuetype = 'f'
elif port.type == node_db.PortDescription.EVENTS:
elif port.types[0] == node_db.PortDescription.EVENTS:
bufsize = 10240
valuetype = 'B'
else:
raise ValueError(port.type)
raise ValueError(port.types[0])
buf = bytearray(bufsize)
plugin.connect_port(idx, buf)

63
noisicaa/audioproc/engine/processor.cpp

@ -138,6 +138,7 @@ Status Processor::setup_internal() {
for (int port_idx = 0 ; port_idx < _desc.ports_size() ; ++port_idx) {
_buffers[port_idx] = nullptr;
}
_buffers_changed = true;
return Status::Ok();
}
@ -151,6 +152,7 @@ void Processor::cleanup() {
}
void Processor::cleanup_internal() {
_buffers.clear();
}
Status Processor::handle_message(const string& msg_serialized) {
@ -194,24 +196,24 @@ Status Processor::set_description_internal(const pb::NodeDescription& desc) {
return Status::Ok();
}
void Processor::connect_port(BlockContext* ctxt, uint32_t port_idx, BufferPtr buf) {
// Some processors might have additional ports, which are not in the NodeDescription.
if (port_idx < _buffers.size()) {
_buffers[port_idx] = buf;
void Processor::connect_port(BlockContext* ctxt, uint32_t port_idx, Buffer* buf) {
if (port_idx >= _buffers.size()) {
_logger->error(
"Processor %llx: connect_port(%u) failed: Invalid index %u", id(), port_idx, port_idx);
RTUnsafe rtu; // We just crashed... doesn't matter we're now calling unsafe callbacks.
set_state(ProcessorState::BROKEN);
return;
}
if (state() == ProcessorState::RUNNING) {
Status status = connect_port_internal(ctxt, port_idx, buf);
if (status.is_error()) {
_logger->error(
"Processor %llx: connect_port(%u) failed: %s", id(), port_idx, status.message());
RTUnsafe rtu; // We just crashed... doesn't matter we're now calling unsafe callbacks.
set_state(ProcessorState::BROKEN);
}
}
_buffers[port_idx] = buf;
_buffers_changed = true;
}
void Processor::process_block(BlockContext* ctxt, TimeMapper* time_mapper) {
for (const auto* buf : _buffers) {
assert(buf != nullptr);
}
if (state() == ProcessorState::RUNNING) {
Status status = process_block_internal(ctxt, time_mapper);
if (status.is_error()) {
@ -221,6 +223,8 @@ void Processor::process_block(BlockContext* ctxt, TimeMapper* time_mapper) {
}
}
_buffers_changed = false;
if (state() != ProcessorState::RUNNING || _muted.load()) {
// Processor is muted or broken, just clear all outputs.
clear_all_outputs();
@ -247,38 +251,7 @@ void Processor::clear_all_outputs() {
continue;
}
switch (port.type()) {
case pb::PortDescription::AUDIO:
case pb::PortDescription::ARATE_CONTROL: {
float* buf = (float*)_buffers[port_idx];
for (uint32_t i = 0 ; i < _host_system->block_size() ; ++i) {
*buf++ = 0.0;
}
break;
}
case pb::PortDescription::KRATE_CONTROL: {
float* buf = (float*)_buffers[port_idx];
*buf = 0.0;
break;
}
case pb::PortDescription::EVENTS: {
LV2_Atom_Forge forge;
lv2_atom_forge_init(&forge, &_host_system->lv2->urid_map);
LV2_Atom_Forge_Frame frame;
lv2_atom_forge_set_buffer(&forge, _buffers[port_idx], 10240);
lv2_atom_forge_sequence_head(&forge, &frame, _host_system->lv2->urid.atom_frame_time);
lv2_atom_forge_pop(&forge, &frame);
break;
}
default:
_logger->error("Unsupported port type %d", port.type());
abort();
}
_buffers[port_idx]->clear();
}
}

6
noisicaa/audioproc/engine/processor.h

@ -87,7 +87,7 @@ public:
Status set_parameters(const string& parameters_serialized);
Status set_description(const string& description_serialized);
void connect_port(BlockContext* ctxt, uint32_t port_idx, BufferPtr buf);
void connect_port(BlockContext* ctxt, uint32_t port_idx, Buffer* buf);
void process_block(BlockContext* ctxt, TimeMapper* time_mapper);
Slot<pb::EngineNotification> notifications;
@ -101,7 +101,6 @@ protected:
virtual Status handle_message_internal(pb::ProcessorMessage* msg);
virtual Status set_parameters_internal(const pb::NodeParameters& parameters);
virtual Status set_description_internal(const pb::NodeDescription& description);
virtual Status connect_port_internal(BlockContext* ctxt, uint32_t port_idx, BufferPtr buf) = 0;
virtual Status process_block_internal(BlockContext* ctxt, TimeMapper* time_mapper) = 0;
virtual Status post_process_block_internal(BlockContext* ctxt, TimeMapper* time_mapper);
@ -115,12 +114,13 @@ protected:
pb::NodeDescription _desc;
pb::NodeParameters _params;
atomic<bool> _muted;
vector<Buffer*> _buffers;
bool _buffers_changed;
private:
static uint64_t new_id();
ProcessorState _state;
vector<BufferPtr> _buffers;
};
} // namespace noisicaa

4
noisicaa/audioproc/engine/processor.pxd

@ -27,7 +27,7 @@ from noisicaa.core.refcount cimport RefCounted
from noisicaa.host_system.host_system cimport HostSystem
from noisicaa.audioproc.public.time_mapper cimport TimeMapper
from noisicaa.audioproc.public.musical_time cimport MusicalTime
from .buffers cimport BufferPtr
from .buffers cimport Buffer
from .block_context cimport BlockContext
@ -55,7 +55,7 @@ cdef extern from "noisicaa/audioproc/engine/processor.h" namespace "noisicaa" no
Status set_parameters(const string& msg)
Status set_description(const string& msg)
void connect_port(BlockContext* ctxt, uint32_t port_idx, BufferPtr buf)
void connect_port(BlockContext* ctxt, uint32_t port_idx, Buffer* buf)
void process_block(BlockContext* ctxt, TimeMapper* time_mapper)

6
noisicaa/audioproc/engine/processor.pyx

@ -28,6 +28,7 @@ from noisicaa.core.status cimport check
from noisicaa.audioproc.public.time_mapper cimport PyTimeMapper
from noisicaa.host_system.host_system cimport PyHostSystem
from .block_context cimport PyBlockContext
from .buffers cimport PyBuffer
from . cimport rtcheck
@ -83,14 +84,13 @@ cdef class PyProcessor(object):
with nogil:
self.__processor.cleanup()
def connect_port(self, PyBlockContext ctxt, port_index, unsigned char[:] data):
def connect_port(self, PyBlockContext ctxt, port_index, PyBuffer buf):
cdef uint32_t c_port_index = port_index
cdef BufferPtr c_data = &data[0]
with nogil:
rtcheck.reset_rt_checker_violations()
rtcheck.enable_rt_checker(1)
try:
self.__processor.connect_port(ctxt.get(), c_port_index, c_data)
self.__processor.connect_port(ctxt.get(), c_port_index, buf.get())
finally:
rtcheck.enable_rt_checker(0)
assert rtcheck.rt_checker_violations() == 0

26
noisicaa/audioproc/engine/processor_csound_base.cpp

@ -62,10 +62,11 @@ Status ProcessorCSoundBase::set_code(const string& orchestra, const string& scor
vector<CSoundUtil::PortSpec> ports;
for (const auto& port : _desc.ports()) {
assert(port.types_size() == 1);
ports.emplace_back(
CSoundUtil::PortSpec {
port.name(),
port.type(),
port.types(0),
port.direction(),
port.csound_name()
});
@ -80,11 +81,7 @@ Status ProcessorCSoundBase::set_code(const string& orchestra, const string& scor
}
Status ProcessorCSoundBase::setup_internal() {
RETURN_IF_ERROR(Processor::setup_internal());
_buffers.resize(_desc.ports_size());
return Status::Ok();
return Processor::setup_internal();
}
void ProcessorCSoundBase::c