From b935c3064e712c1f90bc4bbcdf34aa0dd7dc3eaa Mon Sep 17 00:00:00 2001 From: dreamer Date: Wed, 23 Jul 2025 23:42:16 +0200 Subject: [PATCH 1/7] poc of soundfiler for dpf --- hvcc/compiler.py | 9 +- hvcc/generators/c2dpf/templates/HeavyDPF.cpp | 90 +++++++++++++++++++ hvcc/generators/c2dpf/templates/HeavyDPF.hpp | 2 + .../c2dpf/templates/Makefile_plugin | 3 + hvcc/generators/c2owl/c2owl.py | 4 +- hvcc/interpreters/pd2hv/PdSendObject.py | 3 +- hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd | 14 +++ 7 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd diff --git a/hvcc/compiler.py b/hvcc/compiler.py index 1b03834a..947bfc53 100644 --- a/hvcc/compiler.py +++ b/hvcc/compiler.py @@ -113,7 +113,7 @@ def count_midi_objects(hvir: IRGraph) -> Dict[str, List[str]]: } -def filter_midi_from_out_parameters(output_parameter_list: List, midi_out_objects: List) -> List: +def filter_objects_from_out_parameters(output_parameter_list: List, midi_out_objects: List) -> List: new_out_list = [] for item in output_parameter_list: @@ -160,7 +160,12 @@ def generate_extern_info(hvir: IRGraph, results: CompilerResults) -> ExternInfo: midi_objects = count_midi_objects(hvir) # filter midi objects from the output parameters list - out_parameter_list = filter_midi_from_out_parameters(out_parameter_list, midi_objects['out']) + out_parameter_list = filter_objects_from_out_parameters(out_parameter_list, midi_objects['out']) + + # filter snd objects from the output parameters list + out_parameter_list = filter_objects_from_out_parameters( + out_parameter_list, ['__hv_snd_write', '__hv_snd_read', '__hv_snd_read_resize'] + ) return ExternInfo( parameters=ExternParams( diff --git a/hvcc/generators/c2dpf/templates/HeavyDPF.cpp b/hvcc/generators/c2dpf/templates/HeavyDPF.cpp index 39844417..138abc42 100644 --- a/hvcc/generators/c2dpf/templates/HeavyDPF.cpp +++ b/hvcc/generators/c2dpf/templates/HeavyDPF.cpp @@ -37,6 +37,10 @@ #define HV_HASH_DPF_BPM 0xDF8C2721 +#define HV_HASH_SND_WRITE 0x74140F5F +#define HV_HASH_SND_READ 0xEB5BD581 +#define HV_HASH_SND_READ_RES 0x280AFD69 + // midi realtime messages std::set mrtSet { MIDI_RT_CLOCK, @@ -59,10 +63,19 @@ static void hvSendHookFunc(HeavyContextInterface *c, const char *sendName, uint3 if (plugin != nullptr) { plugin->setOutputParameter(sendHash, m); + switch (sendHash) + { + case HV_HASH_SND_WRITE: + case HV_HASH_SND_READ: + case HV_HASH_SND_READ_RES: + plugin->sndFileOperator(sendHash, m); + break; + } {%- if meta.midi_output is sameas true %} #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT plugin->handleMidiSend(sendHash, m); #endif + } {% endif %} } } @@ -167,6 +180,83 @@ void {{class_name}}::setOutputParameter(uint32_t sendHash, const HvMessage *m) {%- endif %} } +void HeavyDPF_soundfilermock::sndFileOperator(uint32_t sendHash, const HvMessage *m) +{ + switch (sendHash) { + case HV_HASH_SND_READ: // read_file_out + { + char buf[64]; + char buf2[64]; + char* dst = buf; + char* dst2 = buf2; + const char *fileName = hv_msg_getSymbol(m, 0); + const char *tableName = hv_msg_getSymbol(m, 1); + + dst = strncpy(dst, fileName, 63); + dst2 = strncpy(dst2, tableName, 63); + printf("> %s - %s \n", buf, buf2); + + hv_uint32_t tableHash = hv_string_to_hash(tableName); + float *table = _context->getBufferForTable(tableHash); + int tableSize = _context->getLengthForTable(tableHash); + + TinyWav tw; + tinywav_open_read(&tw, fileName, TW_INLINE); + + tinywav_read_f(&tw, table, tableSize); + tinywav_close_read(&tw); + + break; + } + case HV_HASH_SND_READ_RES: // read_file_resize_out + { + const char *fileName = hv_msg_getSymbol(m, 0); + const char *tableName = hv_msg_getSymbol(m, 1); + + printf("> resize \n"); + + hv_uint32_t tableHash = hv_string_to_hash(tableName); + + TinyWav tw; + tinywav_open_read(&tw, fileName, TW_INLINE); + int tableSize = tw.numFramesInHeader; + + printf("> %d \n", tableSize); + + _context->setLengthForTable(tableHash, (uint32_t)tableSize); + float *table = _context->getBufferForTable(tableHash); + + tinywav_read_f(&tw, table, tableSize); + tinywav_close_read(&tw); + + break; + } + case HV_HASH_SND_WRITE: // write_file_out + { + const char *fileName = hv_msg_getSymbol(m, 0); + const char *tableName = hv_msg_getSymbol(m, 1); + + printf("> writing file \n"); + + hv_uint32_t tableHash = hv_string_to_hash(tableName); + float *table = _context->getBufferForTable(tableHash); + int tableSize = _context->getLengthForTable(tableHash); + + TinyWav tw; + tinywav_open_write( + &tw, 1, + _context->getSampleRate(), + TW_FLOAT32, TW_INLINE, + fileName + ); + tinywav_write_f(&tw, table, tableSize); + tinywav_close_write(&tw); + + break; + } + default: break; + } +} // ------------------------------------------------------------------- // Process diff --git a/hvcc/generators/c2dpf/templates/HeavyDPF.hpp b/hvcc/generators/c2dpf/templates/HeavyDPF.hpp index eaeb03d6..bd8d3332 100644 --- a/hvcc/generators/c2dpf/templates/HeavyDPF.hpp +++ b/hvcc/generators/c2dpf/templates/HeavyDPF.hpp @@ -6,6 +6,7 @@ #include "DistrhoPlugin.hpp" #include "DistrhoPluginInfo.h" #include "Heavy_{{name}}.hpp" +#include "tinywav.h" START_NAMESPACE_DISTRHO @@ -49,6 +50,7 @@ class {{class_name}} : public Plugin void handleMidiSend(uint32_t sendHash, const HvMessage *m); void hostTransportEvents(uint32_t frames); void setOutputParameter(uint32_t sendHash, const HvMessage *m); + void sndFileOperator(uint32_t sendHash, const HvMessage *m); protected: // ------------------------------------------------------------------- diff --git a/hvcc/generators/c2dpf/templates/Makefile_plugin b/hvcc/generators/c2dpf/templates/Makefile_plugin index 7afffaa5..6ffd6f2c 100644 --- a/hvcc/generators/c2dpf/templates/Makefile_plugin +++ b/hvcc/generators/c2dpf/templates/Makefile_plugin @@ -8,6 +8,7 @@ FILES_DSP = $(filter-out HeavyDPF_{{name}}_UI.cpp, $(wildcard *.cpp)) FILES_DSP = $(wildcard *.cpp) {%- endif %} FILES_DSP += $(wildcard *.c) +FILES_DSP += ../../tinywav/tinywav.c {%- if meta.enable_ui is sameas true %} FILES_UI = HeavyDPF_{{name}}_UI.cpp @@ -30,6 +31,8 @@ BUILD_CXX_FLAGS += -I ../../{{dpf_path}}dpf-widgets/opengl BUILD_CXX_FLAGS += -I ../../{{dpf_path}}{{dependency}} {%- endfor %} {%- endif %} +BUILD_CXX_FLAGS += -I ../../tinywav +BUILD_C_FLAGS += -I ../../tinywav BUILD_C_FLAGS += -Wno-unused-parameter -std=c11 -fno-strict-aliasing -pthread BUILD_CXX_FLAGS += -Wno-unused-parameter -fno-strict-aliasing -pthread diff --git a/hvcc/generators/c2owl/c2owl.py b/hvcc/generators/c2owl/c2owl.py index 7f0d59c7..455fb2d3 100644 --- a/hvcc/generators/c2owl/c2owl.py +++ b/hvcc/generators/c2owl/c2owl.py @@ -36,7 +36,7 @@ def make_jdata(cls, patch_ir: str) -> List: continue # If a name has been specified - if recv.attributes.get('raw'): + if recv.attributes.get('raw') is not None: key = recv.attributes['raw'] jdata.append((key, name, 'RECV', f"0x{heavy_hash(name):X}", recv.attributes['min'], @@ -53,7 +53,7 @@ def make_jdata(cls, patch_ir: str) -> List: try: if obj.type == '__send': name = obj.args['name'] - if obj.args['attributes'].get('raw'): + if obj.args['attributes'].get('raw') is not None: key = obj.args['attributes']['raw'] jdata.append((key, f'{name}>', 'SEND', f"0x{heavy_hash(name):X}", obj.args['attributes']['min'], diff --git a/hvcc/interpreters/pd2hv/PdSendObject.py b/hvcc/interpreters/pd2hv/PdSendObject.py index 8772d2d8..f434750e 100644 --- a/hvcc/interpreters/pd2hv/PdSendObject.py +++ b/hvcc/interpreters/pd2hv/PdSendObject.py @@ -80,7 +80,8 @@ def __init__( self.__attributes["max"])) self.__extern_type = None - if '@raw' in self.obj_args or '@owl' in self.obj_args: # TODO(dromer): deprecate @owl on next stable release + # TODO(dromer): deprecate @owl on next stable release + if '@raw' in self.obj_args[1] or '@owl' in self.obj_args[1]: try: pd_raw_args = parse_pd_raw_args(self.obj_args) self.__attributes.update(pd_raw_args) diff --git a/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd b/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd new file mode 100644 index 00000000..fe62a937 --- /dev/null +++ b/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd @@ -0,0 +1,14 @@ +#N canvas 827 239 734 565 12; +#X obj 191 102 inlet; +#X obj 191 173 route read write; +#X obj 191 257 route -resize; +#X obj 231 210 s __hv_snd_write @hv_param; +#X obj 253 289 s __hv_snd_read @hv_param; +#X obj 191 316 s __hv_snd_read_resize @hv_param; +#X obj 191 472 outlet; +#X obj 366 472 outlet; +#X connect 0 0 1 0; +#X connect 1 0 2 0; +#X connect 1 1 3 0; +#X connect 2 0 5 0; +#X connect 2 1 4 0; From 4c0d0a490cbcff79fc6280b8313d6795c3fe4084 Mon Sep 17 00:00:00 2001 From: dreamer Date: Thu, 24 Jul 2025 00:09:35 +0200 Subject: [PATCH 2/7] cleanup --- hvcc/generators/c2dpf/templates/HeavyDPF.cpp | 1 - hvcc/interpreters/pd2hv/PdSendObject.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/hvcc/generators/c2dpf/templates/HeavyDPF.cpp b/hvcc/generators/c2dpf/templates/HeavyDPF.cpp index 138abc42..a7566483 100644 --- a/hvcc/generators/c2dpf/templates/HeavyDPF.cpp +++ b/hvcc/generators/c2dpf/templates/HeavyDPF.cpp @@ -75,7 +75,6 @@ static void hvSendHookFunc(HeavyContextInterface *c, const char *sendName, uint3 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT plugin->handleMidiSend(sendHash, m); #endif - } {% endif %} } } diff --git a/hvcc/interpreters/pd2hv/PdSendObject.py b/hvcc/interpreters/pd2hv/PdSendObject.py index f434750e..495e6937 100644 --- a/hvcc/interpreters/pd2hv/PdSendObject.py +++ b/hvcc/interpreters/pd2hv/PdSendObject.py @@ -81,7 +81,7 @@ def __init__( self.__extern_type = None # TODO(dromer): deprecate @owl on next stable release - if '@raw' in self.obj_args[1] or '@owl' in self.obj_args[1]: + if '@raw' in self.obj_args or '@owl' in self.obj_args: try: pd_raw_args = parse_pd_raw_args(self.obj_args) self.__attributes.update(pd_raw_args) From 75d7bdaa0c2070bfd72619279202908d58eea103 Mon Sep 17 00:00:00 2001 From: dreamer Date: Sun, 3 Aug 2025 13:15:37 +0200 Subject: [PATCH 3/7] poc of symbol in pack --- hvcc/core/json/heavy.ir.json | 2 +- hvcc/generators/ir2c/static/HvControlPack.c | 4 +- hvcc/interpreters/pd2hv/PdPackObject.py | 2 +- tests/pd/control/test-pack-symbol.golden.txt | 5 +++ tests/pd/control/test-pack-symbol.pd | 39 ++++++++++++++++++++ tests/test_control.py | 3 ++ 6 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 tests/pd/control/test-pack-symbol.golden.txt create mode 100644 tests/pd/control/test-pack-symbol.pd diff --git a/hvcc/core/json/heavy.ir.json b/hvcc/core/json/heavy.ir.json index ff36a44d..980535a1 100644 --- a/hvcc/core/json/heavy.ir.json +++ b/hvcc/core/json/heavy.ir.json @@ -2071,7 +2071,7 @@ ], "args": [{ "name": "values", - "value_type": "floatarray", + "value_type": "mixedarray", "description": "The preset values to this pack object.", "default": [0.0, 0.0], "required": false diff --git a/hvcc/generators/ir2c/static/HvControlPack.c b/hvcc/generators/ir2c/static/HvControlPack.c index 7ae73430..5cb5c041 100644 --- a/hvcc/generators/ir2c/static/HvControlPack.c +++ b/hvcc/generators/ir2c/static/HvControlPack.c @@ -45,7 +45,7 @@ void cPack_onMessage(HeavyContextInterface *_c, ControlPack *o, int letIn, const for (int i = hv_min_i(numElements, msg_getNumElements(m))-1; i >= 0; --i) { switch (msg_getType(m, i)) { case HV_MSG_FLOAT: msg_setFloat(o->msg, i, msg_getFloat(m, i)); break; - case HV_MSG_SYMBOL: + case HV_MSG_SYMBOL: msg_setSymbol(o->msg, i, msg_getSymbol(m, i+1)); break; case HV_MSG_HASH: msg_setHash(o->msg, i, msg_getHash(m, i)); break; default: break; } @@ -57,7 +57,7 @@ void cPack_onMessage(HeavyContextInterface *_c, ControlPack *o, int letIn, const default: { // rest of inlets just store values switch (msg_getType(m, 0)) { case HV_MSG_FLOAT: msg_setFloat(o->msg, letIn, msg_getFloat(m, 0)); break; - case HV_MSG_SYMBOL: + case HV_MSG_SYMBOL: msg_setSymbol(o->msg, letIn, msg_getSymbol(m, 1)); break; case HV_MSG_HASH: msg_setHash(o->msg, letIn, msg_getHash(m, 0)); break; default: break; } diff --git a/hvcc/interpreters/pd2hv/PdPackObject.py b/hvcc/interpreters/pd2hv/PdPackObject.py index 09d252db..7a97d97a 100644 --- a/hvcc/interpreters/pd2hv/PdPackObject.py +++ b/hvcc/interpreters/pd2hv/PdPackObject.py @@ -40,7 +40,7 @@ def __init__( try: self.values.append(float(x)) except Exception: - if x in {"f", "float"}: + if x in {"f", "float", "s", "symbol"}: self.values.append(0.0) else: self.add_error( diff --git a/tests/pd/control/test-pack-symbol.golden.txt b/tests/pd/control/test-pack-symbol.golden.txt new file mode 100644 index 00000000..7e730d72 --- /dev/null +++ b/tests/pd/control/test-pack-symbol.golden.txt @@ -0,0 +1,5 @@ +[@ 0.000] print: 100 bla +[@ 0.000] print: 0 200 +[@ 0.000] print: 10 200 +[@ 0.000] print: 10 0 +[@ 0.000] print: 10 100 diff --git a/tests/pd/control/test-pack-symbol.pd b/tests/pd/control/test-pack-symbol.pd new file mode 100644 index 00000000..715f0619 --- /dev/null +++ b/tests/pd/control/test-pack-symbol.pd @@ -0,0 +1,39 @@ +#N canvas 750 181 363 362 10; +#X obj 24 11 loadbang; +#X obj 24 200 pack; +#X obj 24 127 t b b b; +#X msg 24 167 10; +#X msg 53 167 100; +#X obj 170 126 t b b; +#X obj 98 211 pack f f; +#X obj 117 181 t b f; +#X msg 128 155 200; +#X obj 98 124 t b b; +#X msg 98 154 10; +#X obj 24 326 print; +#X obj 24 75 t b b b; +#X obj 24 39 bng 25 250 50 0 empty empty empty 17 7 0 10 #191919 #ffffff #ffffff; +#X obj 170 211 pack 100 s; +#X msg 219 181 symbol bla; +#X connect 0 0 13 0; +#X connect 1 0 11 0; +#X connect 2 0 3 0; +#X connect 2 1 4 0; +#X connect 2 2 3 0; +#X connect 3 0 1 0; +#X connect 4 0 1 1; +#X connect 5 0 14 0; +#X connect 5 1 15 0; +#X connect 6 0 11 0; +#X connect 7 0 6 0; +#X connect 7 1 6 1; +#X connect 8 0 7 0; +#X connect 9 0 10 0; +#X connect 9 1 8 0; +#X connect 10 0 6 0; +#X connect 12 0 2 0; +#X connect 12 1 9 0; +#X connect 12 2 5 0; +#X connect 13 0 12 0; +#X connect 14 0 11 0; +#X connect 15 0 14 1; diff --git a/tests/test_control.py b/tests/test_control.py index cdbeb263..95825394 100755 --- a/tests/test_control.py +++ b/tests/test_control.py @@ -198,6 +198,9 @@ def test_null_object(self): def test_pack(self): self._test_control_patch("test-pack.pd") + def test_pack_symbol(self): + self._test_control_patch("test-pack-symbol.pd") + def test_pack_wrong_args(self): self._test_control_patch_expect_error("test-pack_wrong_args.pd", NotificationEnum.ERROR_PACK_FLOAT_ARGUMENTS) From bad823a1c6afb5632902dd55758c1e61926bcbcd Mon Sep 17 00:00:00 2001 From: dreamer Date: Sun, 3 Aug 2025 17:42:44 +0200 Subject: [PATCH 4/7] soundfiler outputs --- hvcc/generators/c2dpf/templates/HeavyDPF.cpp | 98 +++++++++++++++---- hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd | 77 ++++++++++++--- 2 files changed, 144 insertions(+), 31 deletions(-) diff --git a/hvcc/generators/c2dpf/templates/HeavyDPF.cpp b/hvcc/generators/c2dpf/templates/HeavyDPF.cpp index a7566483..11e211c2 100644 --- a/hvcc/generators/c2dpf/templates/HeavyDPF.cpp +++ b/hvcc/generators/c2dpf/templates/HeavyDPF.cpp @@ -182,18 +182,19 @@ void {{class_name}}::setOutputParameter(uint32_t sendHash, const HvMessage *m) void HeavyDPF_soundfilermock::sndFileOperator(uint32_t sendHash, const HvMessage *m) { switch (sendHash) { - case HV_HASH_SND_READ: // read_file_out + case HV_HASH_SND_READ: // __hv_snd_read { - char buf[64]; - char buf2[64]; - char* dst = buf; - char* dst2 = buf2; - const char *fileName = hv_msg_getSymbol(m, 0); - const char *tableName = hv_msg_getSymbol(m, 1); + float sndID = hv_msg_getFloat(m, 0); + const char *fileName = hv_msg_getSymbol(m, 1); + const char *tableName = hv_msg_getSymbol(m, 2); - dst = strncpy(dst, fileName, 63); - dst2 = strncpy(dst2, tableName, 63); - printf("> %s - %s \n", buf, buf2); + char sndRecInfo[32]; + char sndRecSamples[32]; + char *bufInfo = sndRecInfo; + char *bufSamples = sndRecSamples; + + snprintf(bufInfo, 31, "%d__hv_snd_info", (int) sndID); + snprintf(bufSamples, 31, "%d__hv_snd_samples", (int) sndID); hv_uint32_t tableHash = hv_string_to_hash(tableName); float *table = _context->getBufferForTable(tableHash); @@ -203,16 +204,38 @@ void HeavyDPF_soundfilermock::sndFileOperator(uint32_t sendHash, const HvMessage tinywav_open_read(&tw, fileName, TW_INLINE); tinywav_read_f(&tw, table, tableSize); + + _context->sendMessageToReceiverV( + hv_string_to_hash(sndRecInfo), 0, "ffffs", + (float) tw.h.SampleRate, // sample rate + 44.0, // header size + 1.0, // channels + (float) tw.sampFmt, // bytes per sample + "l" // endianness + ); + + _context->sendFloatToReceiver( + hv_string_to_hash(sndRecSamples), + float(tableSize) + ); + tinywav_close_read(&tw); break; } - case HV_HASH_SND_READ_RES: // read_file_resize_out + case HV_HASH_SND_READ_RES: // __hv_snd_read_resize { - const char *fileName = hv_msg_getSymbol(m, 0); - const char *tableName = hv_msg_getSymbol(m, 1); + float sndID = hv_msg_getFloat(m, 0); + const char *fileName = hv_msg_getSymbol(m, 1); + const char *tableName = hv_msg_getSymbol(m, 2); + + char sndRecInfo[32]; + char sndRecSamples[32]; + char *bufInfo = sndRecInfo; + char *bufSamples = sndRecSamples; - printf("> resize \n"); + snprintf(bufInfo, 31, "%d__hv_snd_info", (int) sndID); + snprintf(bufSamples, 31, "%d__hv_snd_samples", (int) sndID); hv_uint32_t tableHash = hv_string_to_hash(tableName); @@ -220,22 +243,42 @@ void HeavyDPF_soundfilermock::sndFileOperator(uint32_t sendHash, const HvMessage tinywav_open_read(&tw, fileName, TW_INLINE); int tableSize = tw.numFramesInHeader; - printf("> %d \n", tableSize); - _context->setLengthForTable(tableHash, (uint32_t)tableSize); float *table = _context->getBufferForTable(tableHash); tinywav_read_f(&tw, table, tableSize); + + _context->sendMessageToReceiverV( + hv_string_to_hash(sndRecInfo), 0, "ffffs", + (float) tw.h.SampleRate, // sample rate + 44.0, // header size + 1.0, // channels + (float) tw.sampFmt, // bytes per sample + "l" // endianness + ); + + _context->sendFloatToReceiver( + hv_string_to_hash(sndRecSamples), + float(tableSize) + ); + tinywav_close_read(&tw); break; } - case HV_HASH_SND_WRITE: // write_file_out + case HV_HASH_SND_WRITE: // __hv_snd_write { - const char *fileName = hv_msg_getSymbol(m, 0); - const char *tableName = hv_msg_getSymbol(m, 1); + float sndID = hv_msg_getFloat(m, 0); + const char *fileName = hv_msg_getSymbol(m, 1); + const char *tableName = hv_msg_getSymbol(m, 2); + + char sndRecInfo[32]; + char sndRecSamples[32]; + char *bufInfo = sndRecInfo; + char *bufSamples = sndRecSamples; - printf("> writing file \n"); + snprintf(bufInfo, 31, "%d__hv_snd_info", (int) sndID); + snprintf(bufSamples, 31, "%d__hv_snd_samples", (int) sndID); hv_uint32_t tableHash = hv_string_to_hash(tableName); float *table = _context->getBufferForTable(tableHash); @@ -249,6 +292,21 @@ void HeavyDPF_soundfilermock::sndFileOperator(uint32_t sendHash, const HvMessage fileName ); tinywav_write_f(&tw, table, tableSize); + + _context->sendMessageToReceiverV( + hv_string_to_hash(sndRecInfo), 0, "ffffs", + (float) tw.h.SampleRate, // sample rate + 44.0, // header size + 1.0, // channels + (float) tw.sampFmt, // bytes per sample + "l" // endianness + ); + + _context->sendFloatToReceiver( + hv_string_to_hash(sndRecSamples), + float(tableSize) + ); + tinywav_close_write(&tw); break; diff --git a/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd b/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd index fe62a937..b4b5a325 100644 --- a/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd +++ b/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd @@ -1,14 +1,69 @@ #N canvas 827 239 734 565 12; -#X obj 191 102 inlet; -#X obj 191 173 route read write; -#X obj 191 257 route -resize; -#X obj 231 210 s __hv_snd_write @hv_param; -#X obj 253 289 s __hv_snd_read @hv_param; -#X obj 191 316 s __hv_snd_read_resize @hv_param; -#X obj 191 472 outlet; -#X obj 366 472 outlet; +#X obj 41 35 inlet; +#X obj 41 67 route read write; +#X obj 41 151 route -resize; +#X obj 81 122 s __hv_snd_write @hv_param; +#X obj 103 208 s __hv_snd_read @hv_param; +#X obj 41 264 s __hv_snd_read_resize @hv_param; +#X obj 41 368 outlet; +#X obj 216 368 outlet; +#N canvas 0 50 450 300 pack_id 0; +#X obj 143 254 unpack s s s; +#X obj 131 227 t b a; +#X obj 131 306 pack f s s s; +#X obj 131 280 f \$0; +#X obj 131 199 inlet; +#X obj 131 332 outlet; +#X connect 0 0 2 1; +#X connect 0 1 2 2; +#X connect 0 2 2 3; +#X connect 1 0 3 0; +#X connect 1 1 0 0; +#X connect 2 0 5 0; +#X connect 3 0 2 0; +#X connect 4 0 1 0; +#X restore 41 235 pd pack_id; +#N canvas 0 50 450 300 pack_id 0; +#X obj 143 254 unpack s s s; +#X obj 131 227 t b a; +#X obj 131 306 pack f s s s; +#X obj 131 280 f \$0; +#X obj 131 199 inlet; +#X obj 131 332 outlet; +#X connect 0 0 2 1; +#X connect 0 1 2 2; +#X connect 0 2 2 3; +#X connect 1 0 3 0; +#X connect 1 1 0 0; +#X connect 2 0 5 0; +#X connect 3 0 2 0; +#X connect 4 0 1 0; +#X restore 103 181 pd pack_id; +#N canvas 0 50 450 300 pack_id 0; +#X obj 143 254 unpack s s s; +#X obj 131 227 t b a; +#X obj 131 306 pack f s s s; +#X obj 131 280 f \$0; +#X obj 131 199 inlet; +#X obj 131 332 outlet; +#X connect 0 0 2 1; +#X connect 0 1 2 2; +#X connect 0 2 2 3; +#X connect 1 0 3 0; +#X connect 1 1 0 0; +#X connect 2 0 5 0; +#X connect 3 0 2 0; +#X connect 4 0 1 0; +#X restore 81 95 pd pack_id; +#X obj 216 336 r \$0__hv_snd_info; +#X obj 41 336 r \$0__hv_snd_samples; #X connect 0 0 1 0; #X connect 1 0 2 0; -#X connect 1 1 3 0; -#X connect 2 0 5 0; -#X connect 2 1 4 0; +#X connect 1 1 10 0; +#X connect 2 0 8 0; +#X connect 2 1 9 0; +#X connect 8 0 5 0; +#X connect 9 0 4 0; +#X connect 10 0 3 0; +#X connect 11 0 7 0; +#X connect 12 0 6 0; From 07182f09c250e879c9ef2feb8b1d3f42a429091e Mon Sep 17 00:00:00 2001 From: dreamer Date: Fri, 8 Aug 2025 21:48:33 +0200 Subject: [PATCH 5/7] start writing some docs --- CHANGELOG.md | 1 + docs/03.gen.dpf.md | 7 +++++++ docs/09.supported_vanilla_objects.md | 4 +++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f15fc80..d0e55afa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Features: * Metadata: optional Dict for external generators * JS: audio inputs now work - thanks to @ZXMushroom63 * Objects: support symbol in `[pack]` (not on first inlet!) +* Objects: support `[soundfiler]` (DPF only) Bugfixes: diff --git a/docs/03.gen.dpf.md b/docs/03.gen.dpf.md index 7b1e5f4e..26fa42f9 100644 --- a/docs/03.gen.dpf.md +++ b/docs/03.gen.dpf.md @@ -107,6 +107,13 @@ The full type specification can be found [here](https://github.com/Wasted-Audio/ An example plugin that uses some of these extended metadata is [WSTD 3Q](https://github.com/Wasted-Audio/wstd-3q). +## Soundfiler +> WORK IN PROGRESS + +It is possible to read and write `.wav` audio files to/from tables using `[soundfiler]` object. This currently depends on [TinyWav](https://github.com/mhroth/tinywav) being placed on the same level as `DPF` in your build environment. Paths are relative to the execution directory of the plugin. + +> WORK IN PROGRESS + ## Notes * The `[notein]` object is the only supported means of receiving MIDI note events (i.e. Note On and Note Off). Arguments to the object (e.g. to specify the channel number) will be ignored. Velocity of `0` will be assumed to mean Note Off diff --git a/docs/09.supported_vanilla_objects.md b/docs/09.supported_vanilla_objects.md index 7bec8c72..1918c50c 100644 --- a/docs/09.supported_vanilla_objects.md +++ b/docs/09.supported_vanilla_objects.md @@ -96,6 +96,7 @@ Here is a list of [unsupported Pd objects](10.unsupported_vanilla_objects.md). | select | right inlet not supported | | send | | | sin | | +| soundfiler | only some generators 2 | | spigot | | | sqrt | | | stripnote | | @@ -118,7 +119,8 @@ Here is a list of [unsupported Pd objects](10.unsupported_vanilla_objects.md). | vsl | converted to `[f ]` | | wrap | | -[1] Midi i/o objects are currently only supported for [dpf](03.gen.dpf.md), [daisy](03.gen.daisy.md) and [owl](03.gen.owl.md) +1. Midi i/o objects are currently only supported for [dpf](03.gen.dpf.md), [daisy](03.gen.daisy.md) and [owl](03.gen.owl.md) +2. `[soundfiler]` is currently only supported for [dpf](03.gen.dpf.md) ## Signal Objects From 700986700e37ec4781342a0c0a4f23c03e6506b8 Mon Sep 17 00:00:00 2001 From: dreamer Date: Sun, 21 Sep 2025 17:35:39 +0200 Subject: [PATCH 6/7] fix HvControlPack; use correct number of arguments in soundfiler; use DPF getSpecialDir(); split to separate template include --- hvcc/generators/c2dpf/templates/HeavyDPF.cpp | 138 +---------------- .../c2dpf/templates/sndFileOperator.cpp | 141 ++++++++++++++++++ hvcc/generators/ir2c/static/HvControlPack.c | 4 +- hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd | 65 ++++---- 4 files changed, 177 insertions(+), 171 deletions(-) create mode 100644 hvcc/generators/c2dpf/templates/sndFileOperator.cpp diff --git a/hvcc/generators/c2dpf/templates/HeavyDPF.cpp b/hvcc/generators/c2dpf/templates/HeavyDPF.cpp index 11e211c2..0c1da4a7 100644 --- a/hvcc/generators/c2dpf/templates/HeavyDPF.cpp +++ b/hvcc/generators/c2dpf/templates/HeavyDPF.cpp @@ -2,6 +2,8 @@ #include "Heavy_{{name}}.h" #include "{{class_name}}.hpp" +#include "DistrhoPluginUtils.hpp" +#include #include {% if meta.denormals is sameas false %} #include "extra/ScopedDenormalDisable.hpp" @@ -179,141 +181,7 @@ void {{class_name}}::setOutputParameter(uint32_t sendHash, const HvMessage *m) {%- endif %} } -void HeavyDPF_soundfilermock::sndFileOperator(uint32_t sendHash, const HvMessage *m) -{ - switch (sendHash) { - case HV_HASH_SND_READ: // __hv_snd_read - { - float sndID = hv_msg_getFloat(m, 0); - const char *fileName = hv_msg_getSymbol(m, 1); - const char *tableName = hv_msg_getSymbol(m, 2); - - char sndRecInfo[32]; - char sndRecSamples[32]; - char *bufInfo = sndRecInfo; - char *bufSamples = sndRecSamples; - - snprintf(bufInfo, 31, "%d__hv_snd_info", (int) sndID); - snprintf(bufSamples, 31, "%d__hv_snd_samples", (int) sndID); - - hv_uint32_t tableHash = hv_string_to_hash(tableName); - float *table = _context->getBufferForTable(tableHash); - int tableSize = _context->getLengthForTable(tableHash); - - TinyWav tw; - tinywav_open_read(&tw, fileName, TW_INLINE); - - tinywav_read_f(&tw, table, tableSize); - - _context->sendMessageToReceiverV( - hv_string_to_hash(sndRecInfo), 0, "ffffs", - (float) tw.h.SampleRate, // sample rate - 44.0, // header size - 1.0, // channels - (float) tw.sampFmt, // bytes per sample - "l" // endianness - ); - - _context->sendFloatToReceiver( - hv_string_to_hash(sndRecSamples), - float(tableSize) - ); - - tinywav_close_read(&tw); - - break; - } - case HV_HASH_SND_READ_RES: // __hv_snd_read_resize - { - float sndID = hv_msg_getFloat(m, 0); - const char *fileName = hv_msg_getSymbol(m, 1); - const char *tableName = hv_msg_getSymbol(m, 2); - - char sndRecInfo[32]; - char sndRecSamples[32]; - char *bufInfo = sndRecInfo; - char *bufSamples = sndRecSamples; - - snprintf(bufInfo, 31, "%d__hv_snd_info", (int) sndID); - snprintf(bufSamples, 31, "%d__hv_snd_samples", (int) sndID); - - hv_uint32_t tableHash = hv_string_to_hash(tableName); - - TinyWav tw; - tinywav_open_read(&tw, fileName, TW_INLINE); - int tableSize = tw.numFramesInHeader; - - _context->setLengthForTable(tableHash, (uint32_t)tableSize); - float *table = _context->getBufferForTable(tableHash); - - tinywav_read_f(&tw, table, tableSize); - - _context->sendMessageToReceiverV( - hv_string_to_hash(sndRecInfo), 0, "ffffs", - (float) tw.h.SampleRate, // sample rate - 44.0, // header size - 1.0, // channels - (float) tw.sampFmt, // bytes per sample - "l" // endianness - ); - - _context->sendFloatToReceiver( - hv_string_to_hash(sndRecSamples), - float(tableSize) - ); - - tinywav_close_read(&tw); - - break; - } - case HV_HASH_SND_WRITE: // __hv_snd_write - { - float sndID = hv_msg_getFloat(m, 0); - const char *fileName = hv_msg_getSymbol(m, 1); - const char *tableName = hv_msg_getSymbol(m, 2); - - char sndRecInfo[32]; - char sndRecSamples[32]; - char *bufInfo = sndRecInfo; - char *bufSamples = sndRecSamples; - - snprintf(bufInfo, 31, "%d__hv_snd_info", (int) sndID); - snprintf(bufSamples, 31, "%d__hv_snd_samples", (int) sndID); - - hv_uint32_t tableHash = hv_string_to_hash(tableName); - float *table = _context->getBufferForTable(tableHash); - int tableSize = _context->getLengthForTable(tableHash); - - TinyWav tw; - tinywav_open_write( - &tw, 1, - _context->getSampleRate(), - TW_FLOAT32, TW_INLINE, - fileName - ); - tinywav_write_f(&tw, table, tableSize); - - _context->sendMessageToReceiverV( - hv_string_to_hash(sndRecInfo), 0, "ffffs", - (float) tw.h.SampleRate, // sample rate - 44.0, // header size - 1.0, // channels - (float) tw.sampFmt, // bytes per sample - "l" // endianness - ); - - _context->sendFloatToReceiver( - hv_string_to_hash(sndRecSamples), - float(tableSize) - ); - - tinywav_close_write(&tw); - - break; - } - default: break; - } -} +{% include 'sndFileOperator.cpp' %} // ------------------------------------------------------------------- // Process diff --git a/hvcc/generators/c2dpf/templates/sndFileOperator.cpp b/hvcc/generators/c2dpf/templates/sndFileOperator.cpp new file mode 100644 index 00000000..fa34566d --- /dev/null +++ b/hvcc/generators/c2dpf/templates/sndFileOperator.cpp @@ -0,0 +1,141 @@ +void {{class_name}}::sndFileOperator(uint32_t sendHash, const HvMessage *m) +{ + switch (sendHash) { + case HV_HASH_SND_READ: // __hv_snd_read + { + float sndID = hv_msg_getFloat(m, 0); + const char *fileName = hv_msg_getSymbol(m, 1); + const char *tableName = hv_msg_getSymbol(m, 2); + + auto filePath = getSpecialDir(kSpecialDirDocumentsForPlugin) + std::string("/Samples/") + std::string(fileName); + + char sndRecInfo[32]; + char sndRecSamples[32]; + char *bufInfo = sndRecInfo; + char *bufSamples = sndRecSamples; + + snprintf(bufInfo, 31, "%d__hv_snd_info", (int) sndID); + snprintf(bufSamples, 31, "%d__hv_snd_samples", (int) sndID); + + hv_uint32_t tableHash = hv_string_to_hash(tableName); + float *table = _context->getBufferForTable(tableHash); + int tableSize = _context->getLengthForTable(tableHash); + + TinyWav tw; + tinywav_open_read(&tw, filePath.c_str(), TW_INLINE); + + tinywav_read_f(&tw, table, tableSize); + + _context->sendMessageToReceiverV( + hv_string_to_hash(sndRecInfo), 0, "ffffs", + (float) tw.h.SampleRate, // sample rate + 44.0, // header size + 1.0, // channels + (float) tw.sampFmt, // bytes per sample + "l" // endianness + ); + + _context->sendFloatToReceiver( + hv_string_to_hash(sndRecSamples), + float(tableSize) + ); + + tinywav_close_read(&tw); + + break; + } + case HV_HASH_SND_READ_RES: // __hv_snd_read_resize + { + float sndID = hv_msg_getFloat(m, 0); + const char *fileName = hv_msg_getSymbol(m, 1); + const char *tableName = hv_msg_getSymbol(m, 2); + + auto filePath = getSpecialDir(kSpecialDirDocumentsForPlugin) + std::string("/Samples/") + std::string(fileName); + + char sndRecInfo[32]; + char sndRecSamples[32]; + char *bufInfo = sndRecInfo; + char *bufSamples = sndRecSamples; + + snprintf(bufInfo, 31, "%d__hv_snd_info", (int) sndID); + snprintf(bufSamples, 31, "%d__hv_snd_samples", (int) sndID); + + hv_uint32_t tableHash = hv_string_to_hash(tableName); + + TinyWav tw; + tinywav_open_read(&tw, filePath.c_str(), TW_INLINE); + int tableSize = tw.numFramesInHeader; + + _context->setLengthForTable(tableHash, (uint32_t)tableSize); + float *table = _context->getBufferForTable(tableHash); + + tinywav_read_f(&tw, table, tableSize); + + _context->sendMessageToReceiverV( + hv_string_to_hash(sndRecInfo), 0, "ffffs", + (float) tw.h.SampleRate, // sample rate + 44.0, // header size + 1.0, // channels + (float) tw.sampFmt, // bytes per sample + "l" // endianness + ); + + _context->sendFloatToReceiver( + hv_string_to_hash(sndRecSamples), + float(tableSize) + ); + + tinywav_close_read(&tw); + + break; + } + case HV_HASH_SND_WRITE: // __hv_snd_write + { + float sndID = hv_msg_getFloat(m, 0); + const char *fileName = hv_msg_getSymbol(m, 1); + const char *tableName = hv_msg_getSymbol(m, 2); + + auto filePath = getSpecialDir(kSpecialDirDocumentsForPlugin) + std::string("/Samples/") + std::string(fileName); + + char sndRecInfo[32]; + char sndRecSamples[32]; + char *bufInfo = sndRecInfo; + char *bufSamples = sndRecSamples; + + snprintf(bufInfo, 31, "%d__hv_snd_info", (int) sndID); + snprintf(bufSamples, 31, "%d__hv_snd_samples", (int) sndID); + + hv_uint32_t tableHash = hv_string_to_hash(tableName); + float *table = _context->getBufferForTable(tableHash); + int tableSize = _context->getLengthForTable(tableHash); + + TinyWav tw; + tinywav_open_write( + &tw, 1, + _context->getSampleRate(), + TW_FLOAT32, TW_INLINE, + filePath.c_str() + ); + tinywav_write_f(&tw, table, tableSize); + + _context->sendMessageToReceiverV( + hv_string_to_hash(sndRecInfo), 0, "ffffs", + (float) tw.h.SampleRate, // sample rate + 44.0, // header size + 1.0, // channels + (float) tw.sampFmt, // bytes per sample + "l" // endianness + ); + + _context->sendFloatToReceiver( + hv_string_to_hash(sndRecSamples), + float(tableSize) + ); + + tinywav_close_write(&tw); + + break; + } + default: break; + } +} diff --git a/hvcc/generators/ir2c/static/HvControlPack.c b/hvcc/generators/ir2c/static/HvControlPack.c index 5cb5c041..ade181fb 100644 --- a/hvcc/generators/ir2c/static/HvControlPack.c +++ b/hvcc/generators/ir2c/static/HvControlPack.c @@ -45,7 +45,7 @@ void cPack_onMessage(HeavyContextInterface *_c, ControlPack *o, int letIn, const for (int i = hv_min_i(numElements, msg_getNumElements(m))-1; i >= 0; --i) { switch (msg_getType(m, i)) { case HV_MSG_FLOAT: msg_setFloat(o->msg, i, msg_getFloat(m, i)); break; - case HV_MSG_SYMBOL: msg_setSymbol(o->msg, i, msg_getSymbol(m, i+1)); break; + case HV_MSG_SYMBOL: case HV_MSG_HASH: msg_setHash(o->msg, i, msg_getHash(m, i)); break; default: break; } @@ -57,7 +57,7 @@ void cPack_onMessage(HeavyContextInterface *_c, ControlPack *o, int letIn, const default: { // rest of inlets just store values switch (msg_getType(m, 0)) { case HV_MSG_FLOAT: msg_setFloat(o->msg, letIn, msg_getFloat(m, 0)); break; - case HV_MSG_SYMBOL: msg_setSymbol(o->msg, letIn, msg_getSymbol(m, 1)); break; + case HV_MSG_SYMBOL: msg_setSymbol(o->msg, letIn, msg_getSymbol(m, 0)); break; case HV_MSG_HASH: msg_setHash(o->msg, letIn, msg_getHash(m, 0)); break; default: break; } diff --git a/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd b/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd index b4b5a325..99d52963 100644 --- a/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd +++ b/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd @@ -1,4 +1,4 @@ -#N canvas 827 239 734 565 12; +#N canvas 93 436 734 565 12; #X obj 41 35 inlet; #X obj 41 67 route read write; #X obj 41 151 route -resize; @@ -7,53 +7,50 @@ #X obj 41 264 s __hv_snd_read_resize @hv_param; #X obj 41 368 outlet; #X obj 216 368 outlet; -#N canvas 0 50 450 300 pack_id 0; -#X obj 143 254 unpack s s s; +#N canvas 1468 0 450 300 pack_id 1; #X obj 131 227 t b a; -#X obj 131 306 pack f s s s; #X obj 131 280 f \$0; #X obj 131 199 inlet; #X obj 131 332 outlet; -#X connect 0 0 2 1; -#X connect 0 1 2 2; -#X connect 0 2 2 3; -#X connect 1 0 3 0; -#X connect 1 1 0 0; -#X connect 2 0 5 0; -#X connect 3 0 2 0; -#X connect 4 0 1 0; +#X obj 143 254 unpack s s; +#X obj 131 306 pack f s s; +#X connect 0 0 1 0; +#X connect 0 1 4 0; +#X connect 1 0 5 0; +#X connect 2 0 0 0; +#X connect 4 0 5 1; +#X connect 4 1 5 2; +#X connect 5 0 3 0; #X restore 41 235 pd pack_id; -#N canvas 0 50 450 300 pack_id 0; -#X obj 143 254 unpack s s s; +#N canvas 1468 0 450 300 pack_id 0; #X obj 131 227 t b a; -#X obj 131 306 pack f s s s; #X obj 131 280 f \$0; #X obj 131 199 inlet; #X obj 131 332 outlet; -#X connect 0 0 2 1; -#X connect 0 1 2 2; -#X connect 0 2 2 3; -#X connect 1 0 3 0; -#X connect 1 1 0 0; -#X connect 2 0 5 0; -#X connect 3 0 2 0; -#X connect 4 0 1 0; +#X obj 143 254 unpack s s; +#X obj 131 306 pack f s s; +#X connect 0 0 1 0; +#X connect 0 1 4 0; +#X connect 1 0 5 0; +#X connect 2 0 0 0; +#X connect 4 0 5 1; +#X connect 4 1 5 2; +#X connect 5 0 3 0; #X restore 103 181 pd pack_id; -#N canvas 0 50 450 300 pack_id 0; -#X obj 143 254 unpack s s s; +#N canvas 1468 0 450 300 pack_id 0; #X obj 131 227 t b a; -#X obj 131 306 pack f s s s; #X obj 131 280 f \$0; #X obj 131 199 inlet; #X obj 131 332 outlet; -#X connect 0 0 2 1; -#X connect 0 1 2 2; -#X connect 0 2 2 3; -#X connect 1 0 3 0; -#X connect 1 1 0 0; -#X connect 2 0 5 0; -#X connect 3 0 2 0; -#X connect 4 0 1 0; +#X obj 143 254 unpack s s; +#X obj 131 306 pack f s s; +#X connect 0 0 1 0; +#X connect 0 1 4 0; +#X connect 1 0 5 0; +#X connect 2 0 0 0; +#X connect 4 0 5 1; +#X connect 4 1 5 2; +#X connect 5 0 3 0; #X restore 81 95 pd pack_id; #X obj 216 336 r \$0__hv_snd_info; #X obj 41 336 r \$0__hv_snd_samples; From 07b044f7c554ccd65a036d26df5e2e35a6faca01 Mon Sep 17 00:00:00 2001 From: dreamer Date: Sun, 21 Sep 2025 17:48:03 +0200 Subject: [PATCH 7/7] fix symbol position --- hvcc/generators/ir2c/static/HvControlPack.c | 2 +- hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd | 64 +++++++++++-------- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/hvcc/generators/ir2c/static/HvControlPack.c b/hvcc/generators/ir2c/static/HvControlPack.c index ade181fb..11583951 100644 --- a/hvcc/generators/ir2c/static/HvControlPack.c +++ b/hvcc/generators/ir2c/static/HvControlPack.c @@ -57,7 +57,7 @@ void cPack_onMessage(HeavyContextInterface *_c, ControlPack *o, int letIn, const default: { // rest of inlets just store values switch (msg_getType(m, 0)) { case HV_MSG_FLOAT: msg_setFloat(o->msg, letIn, msg_getFloat(m, 0)); break; - case HV_MSG_SYMBOL: msg_setSymbol(o->msg, letIn, msg_getSymbol(m, 0)); break; + case HV_MSG_SYMBOL: msg_setSymbol(o->msg, letIn, msg_getSymbol(m, 1)); break; case HV_MSG_HASH: msg_setHash(o->msg, letIn, msg_getHash(m, 0)); break; default: break; } diff --git a/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd b/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd index 99d52963..23db375a 100644 --- a/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd +++ b/hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd @@ -8,49 +8,61 @@ #X obj 41 368 outlet; #X obj 216 368 outlet; #N canvas 1468 0 450 300 pack_id 1; -#X obj 131 227 t b a; -#X obj 131 280 f \$0; -#X obj 131 199 inlet; -#X obj 131 332 outlet; -#X obj 143 254 unpack s s; -#X obj 131 306 pack f s s; +#X obj 29 50 t b a; +#X obj 29 103 f \$0; +#X obj 29 22 inlet; +#X obj 29 228 outlet; +#X obj 41 77 unpack s s; +#X obj 29 202 pack f s s; +#X msg 41 132 symbol \$1; +#X msg 91 161 symbol \$1; #X connect 0 0 1 0; #X connect 0 1 4 0; #X connect 1 0 5 0; #X connect 2 0 0 0; -#X connect 4 0 5 1; -#X connect 4 1 5 2; +#X connect 4 0 6 0; +#X connect 4 1 7 0; #X connect 5 0 3 0; +#X connect 6 0 5 1; +#X connect 7 0 5 2; #X restore 41 235 pd pack_id; -#N canvas 1468 0 450 300 pack_id 0; -#X obj 131 227 t b a; -#X obj 131 280 f \$0; -#X obj 131 199 inlet; -#X obj 131 332 outlet; -#X obj 143 254 unpack s s; -#X obj 131 306 pack f s s; +#N canvas 1468 0 450 300 pack_id 1; +#X obj 42 56 t b a; +#X obj 42 109 f \$0; +#X obj 42 28 inlet; +#X obj 42 234 outlet; +#X obj 54 83 unpack s s; +#X obj 42 208 pack f s s; +#X msg 54 138 symbol \$1; +#X msg 104 167 symbol \$1; #X connect 0 0 1 0; #X connect 0 1 4 0; #X connect 1 0 5 0; #X connect 2 0 0 0; -#X connect 4 0 5 1; -#X connect 4 1 5 2; +#X connect 4 0 6 0; +#X connect 4 1 7 0; #X connect 5 0 3 0; +#X connect 6 0 5 1; +#X connect 7 0 5 2; #X restore 103 181 pd pack_id; -#N canvas 1468 0 450 300 pack_id 0; -#X obj 131 227 t b a; -#X obj 131 280 f \$0; -#X obj 131 199 inlet; -#X obj 131 332 outlet; -#X obj 143 254 unpack s s; -#X obj 131 306 pack f s s; +#N canvas 1468 0 450 300 pack_id 1; +#X obj 57 69 t b a; +#X obj 57 122 f \$0; +#X obj 57 41 inlet; +#X obj 57 247 outlet; +#X obj 69 96 unpack s s; +#X obj 57 221 pack f s s; +#X msg 69 151 symbol \$1; +#X msg 119 180 symbol \$1; #X connect 0 0 1 0; #X connect 0 1 4 0; #X connect 1 0 5 0; #X connect 2 0 0 0; -#X connect 4 0 5 1; -#X connect 4 1 5 2; +#X connect 4 0 6 0; +#X connect 4 1 7 0; #X connect 5 0 3 0; +#X connect 6 0 5 1; +#X connect 7 0 5 2; #X restore 81 95 pd pack_id; #X obj 216 336 r \$0__hv_snd_info; #X obj 41 336 r \$0__hv_snd_samples;