Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Features:
* Allow externing graphical arrays using `@hv_table`
* DPF: support and documentation for Audio Unit format (macOS)
* Daisy: support display parameters using `@hv_param`
* Objects: support `[soundfiler]` (DPF only)

Cleanup:

Expand Down
7 changes: 7 additions & 0 deletions docs/03.gen.dpf.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,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
Expand Down
2 changes: 2 additions & 0 deletions docs/09.supported_vanilla_objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <sup>3</sup> |
| spigot | |
| sqrt | |
| stripnote | |
Expand All @@ -120,6 +121,7 @@ Here is a list of [unsupported Pd objects](10.unsupported_vanilla_objects.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. Supports setting send/receive configuration, see [Getting Started](02.getting_started.md#gui-objects)
3. `[soundfiler]` is currently only supported for [dpf](03.gen.dpf.md)

## Signal Objects

Expand Down
9 changes: 7 additions & 2 deletions hvcc/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,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:
Expand Down Expand Up @@ -161,7 +161,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(
Expand Down
15 changes: 15 additions & 0 deletions hvcc/generators/c2dpf/templates/HeavyDPF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "Heavy_{{name}}.h"
#include "{{class_name}}.hpp"
#include "DistrhoPluginUtils.hpp"
#include <string>
#include <set>
{% if meta.denormals is sameas false %}
#include "extra/ScopedDenormalDisable.hpp"
Expand Down Expand Up @@ -37,6 +39,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<int> mrtSet {
MIDI_RT_CLOCK,
Expand All @@ -59,6 +65,14 @@ 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);
Expand Down Expand Up @@ -167,6 +181,7 @@ void {{class_name}}::setOutputParameter(uint32_t sendHash, const HvMessage *m)
{%- endif %}
}

{% include 'sndFileOperator.cpp' %}

// -------------------------------------------------------------------
// Process
Expand Down
2 changes: 2 additions & 0 deletions hvcc/generators/c2dpf/templates/HeavyDPF.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "DistrhoPlugin.hpp"
#include "DistrhoPluginInfo.h"
#include "Heavy_{{name}}.hpp"
#include "tinywav.h"

START_NAMESPACE_DISTRHO

Expand Down Expand Up @@ -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:
// -------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions hvcc/generators/c2dpf/templates/Makefile_plugin
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
141 changes: 141 additions & 0 deletions hvcc/generators/c2dpf/templates/sndFileOperator.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
}
4 changes: 2 additions & 2 deletions hvcc/generators/c2owl/c2owl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand All @@ -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'],
Expand Down
3 changes: 2 additions & 1 deletion hvcc/interpreters/pd2hv/PdSendObject.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 or '@owl' in self.obj_args:
try:
pd_raw_args = parse_pd_raw_args(self.obj_args)
self.__attributes.update(pd_raw_args)
Expand Down
78 changes: 78 additions & 0 deletions hvcc/interpreters/pd2hv/libs/pd/soundfiler.pd
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#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;
#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 1468 0 450 300 pack_id 1;
#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 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 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 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 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 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;
#X connect 0 0 1 0;
#X connect 1 0 2 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;
Loading