Skip to content

Commit

Permalink
Merge branch 'v0.12.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
dromer committed Jun 21, 2024
2 parents 948e213 + ccc5b55 commit 079a59e
Show file tree
Hide file tree
Showing 29 changed files with 625 additions and 122 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.11.0
current_version = 0.12.0

[bumpversion:file:setup.cfg]

Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
CHANGELOG
=====

0.12.0
-----

* Core: parse remote/send messages
* Core: MIDI i/o added to memoryPool
* Core: support `[else/knob]` as `[float]`
* Daisy: set heavy context after hw.init()
* OWL: add Polytouchin and Polytouchout
* JS: webmidi input
* Docs: add instructions for loading custom samples in JS
* Small Bugfixes:
* MIDI out objects in output Paremeters
* JS: AudioWorklet fillTableWithFloatBuffer

0.11.0
-----

Expand Down
4 changes: 3 additions & 1 deletion docs/02.getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ This list will be continuously epanded to document differences in object behavio
* Sliders and number inputs are converted to `[f ]` and thus do not store send/receive/initialization/etc. settings.
* Heavy does not accept arguments and control connections to: `[rzero~]`, `[rzero_rev~]`, `[czero~]`, `[czero_rev~]`. In Heavy, these objects accept only signal inputs. Arguments and control connections are ignored.
* On the `[select]` object it is currently not possible to set the arguments via the right inlet (internally a hardcoded switch_case is used).
* Heavy supports remote/send messages, however empty messages are currently removed. So the typical `[; bla 1(` multiline message needs to contain at least something on the first line: `[_; bla 1(`.
* `[metro]` and `[timer]` objects do not accept tempo messages or unit arguments.
* Certain filters are sensitive to ‘blowing up’ at very low or very high cutoff frequencies and/or resonances, due to the filter coefficients not being perfectly represented with a finite number of bits. While Pure data natively uses 64 bits, platforms like `OWL` and `Daisy` that use 32 bit float are more sensitive to this. For example, the Pure data `[lp~]`, `[bp~]` and `[hp~]` filters are implemented with biquads which are prone to fail or distort with cutoff frequencies less than around 200 Hz (at 48kHz sample rate).
* `[snapshot~]` does not respond within the same control flow as it executes in signal context. Its output happens on the next audio cycle, so additional care for this control flow needs to be taken into account if you depend on synchronous execution.
* Certain filters are sensitive to ‘blowing up’ at very low or very high cutoff frequencies and/or resonances, due to the filter coefficients not being perfectly represented with a finite number of bits. While Pure data natively uses 64 bits, platforms like `OWL` and `Daisy` that use 32 bit float are more sensitive to this. For example, the Pure data `[bp~]` filter is implemented with a biquad which is prone to fail or distort with cutoff frequencies less than around 200 Hz (at 48kHz sample rate).
* Heavy does not support multichannel connections.
31 changes: 31 additions & 0 deletions docs/03.gen.javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,34 @@ The JS target supports [exposing event and parameter](02.getting_started#exposin
```

Note: these are calls directly to the `AudioLib` so make sure to include `.audiolib` when sending events or messages.

## Loading Custom Samples

If you have a table that is externed, using the `@hv_param` annotation, it can be used to load audio files from the web page. The table will be resized to fit this sample data.

`[table array1 100 @hv_table]`

The webAudioContext can load any url, but typically this will be a local path. Modify your html using the following:

```js
// Sample loading
function loadAudio(url) {
var rq = new XMLHttpRequest();
rq.open("GET", url, true);
rq.responseType = "arraybuffer";
rq.send();

rq.onload = function() {
var audioData = rq.response;
loader.webAudioContext.decodeAudioData(audioData, function(buffer){
loader.fillTableWithFloatBuffer("array1", buffer.getChannelData(0));
});
}
}
```

This can then be called from a user action or any other mechanism that works for you:

```html
<button style="padding: 10px;" type="button" id="loadButton" onclick="loadAudio('custom_sample.wav');">Load Audio</button>
```
3 changes: 3 additions & 0 deletions docs/09.supported_vanilla_objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ del
delay
div
exp
else/knob
f
float
floatatom
Expand Down Expand Up @@ -129,6 +130,7 @@ biquad~
bp~
catch~
clip~
complex-mod~
cos~
cpole~
czero_rev~
Expand All @@ -142,6 +144,7 @@ delwrite~
env~
exp~
ftom~
hilbert~
hip~
inlet~
line~
Expand Down
2 changes: 0 additions & 2 deletions docs/10.unsupported_vanilla_objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,10 @@ value
block~
bob~
bonk~
complex-mod~
expr~
fexpr~
fft~
framp~
hilbert~
ifft~
log~
loop~
Expand Down
73 changes: 71 additions & 2 deletions hvcc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,57 @@ def check_extern_name_conflicts(extern_type: str, extern_list: List, results: Or
"capital letters are not the only difference.")


def check_midi_objects(hvir: Dict) -> Dict:
in_midi = []
out_midi = []

midi_in_objs = [
'__hv_bendin',
'__hv_ctlin',
'__hv_midiin',
'__hv_midirealtimein',
'__hv_notein',
'__hv_pgmin',
'__hv_polytouchin',
'__hv_touchin',
]

midi_out_objs = [
'__hv_bendout',
'__hv_ctlout',
'__hv_midiout',
'__hv_midioutport',
'__hv_noteout',
'__hv_pgmout',
'__hv_polytouchout',
'__hv_touchout',
]

for key in hvir['control']['receivers'].keys():
if key in midi_in_objs:
in_midi.append(key)

for key in hvir['control']['sendMessage']:
if key.get('name'):
if key['name'] in midi_out_objs:
out_midi.append(key['name'])

return {
'in': in_midi,
'out': out_midi
}


def filter_midi_from_out_parameters(output_parameter_list: List, midi_out_objects: List) -> List:
new_out_list = []

for item in output_parameter_list:
if not item[0] in midi_out_objects:
new_out_list.append(item)

return new_out_list


def generate_extern_info(hvir: Dict, results: OrderedDict) -> Dict:
""" Simplifies the receiver/send and table lists by only containing values
externed with @hv_param, @hv_event or @hv_table
Expand Down Expand Up @@ -113,6 +164,12 @@ def generate_extern_info(hvir: Dict, results: OrderedDict) -> Dict:
table_list.sort(key=lambda x: x[0])
check_extern_name_conflicts("table", table_list, results)

# Exposed midi objects
midi_objects = check_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'])

return {
"parameters": {
"in": in_parameter_list,
Expand All @@ -122,12 +179,24 @@ def generate_extern_info(hvir: Dict, results: OrderedDict) -> Dict:
"in": in_event_list,
"out": out_event_list
},
"midi": {
"in": midi_objects['in'],
"out": midi_objects['out']
},
"tables": table_list,
# generate patch heuristics to ensure enough memory allocated for the patch
"memoryPoolSizesKb": {
"internal": 10, # TODO(joe): should this increase if there are a lot of internal connections?
"inputQueue": max(2, int(len(in_parameter_list) + len(in_event_list) / 4)),
"outputQueue": max(2, int(len(out_parameter_list) + len(out_event_list) / 4)),
"inputQueue": max(2, int(
len(in_parameter_list) +
(len(in_event_list) / 4) +
len(midi_objects['in']) # TODO(dreamer): should this depend on the MIDI type?
)),
"outputQueue": max(2, int(
len(out_parameter_list) +
(len(out_event_list) / 4) +
len(midi_objects['out'])
)),
}
}

Expand Down
1 change: 1 addition & 0 deletions hvcc/generators/c2daisy/c2daisy.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def compile(
component_glue['displayprocess'] = board_info['displayprocess']
component_glue['debug_printing'] = daisy_meta.get('debug_printing', False)
component_glue['usb_midi'] = daisy_meta.get('usb_midi', False)
component_glue['pool_sizes_kb'] = externs["memoryPoolSizesKb"]

# samplerate
samplerate = daisy_meta.get('samplerate', 48000)
Expand Down
44 changes: 23 additions & 21 deletions hvcc/generators/c2daisy/templates/HeavyDaisy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ using namespace daisy;

json2daisy::Daisy{{ class_name|capitalize }} hardware;

Heavy_{{patch_name}} hv(SAMPLE_RATE);
Heavy_{{patch_name}}* hv;

void audiocallback(daisy::AudioHandle::InputBuffer in, daisy::AudioHandle::OutputBuffer out, size_t size);
static void sendHook(HeavyContextInterface *c, const char *receiverName, uint32_t receiverHash, const HvMessage * m);
Expand All @@ -50,8 +50,8 @@ daisy::MidiUsbHandler midiusb;
{% endif %}
// int midiOutCount;
// uint8_t* midiOutData;
void CallbackWriteIn(Heavy_{{patch_name}}& hv);
void LoopWriteIn(Heavy_{{patch_name}}& hv);
void CallbackWriteIn(Heavy_{{patch_name}}* hv);
void LoopWriteIn(Heavy_{{patch_name}}* hv);
void CallbackWriteOut();
void LoopWriteOut();
void PostProcess();
Expand Down Expand Up @@ -100,7 +100,7 @@ DaisyHvParamOut DaisyOutputParameters[DaisyNumOutputParameters] = {
void HandleMidiMessage(MidiEvent m)
{
for (int i = 0; i <= 2; ++i) {
hv.sendMessageToReceiverV(HV_HASH_MIDIIN, 0, "ff",
hv->sendMessageToReceiverV(HV_HASH_MIDIIN, 0, "ff",
(float) m.data[i],
(float) m.channel);
}
Expand Down Expand Up @@ -132,52 +132,52 @@ void HandleMidiMessage(MidiEvent m)
break;
}

hv.sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0, "ff",
hv->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0, "ff",
(float) srtType);
break;
}
case NoteOff: {
NoteOnEvent p = m.AsNoteOn();
hv.sendMessageToReceiverV(HV_HASH_NOTEIN, 0, "fff",
hv->sendMessageToReceiverV(HV_HASH_NOTEIN, 0, "fff",
(float) p.note, // pitch
(float) 0, // velocity
(float) p.channel);
break;
}
case NoteOn: {
NoteOnEvent p = m.AsNoteOn();
hv.sendMessageToReceiverV(HV_HASH_NOTEIN, 0, "fff",
hv->sendMessageToReceiverV(HV_HASH_NOTEIN, 0, "fff",
(float) p.note, // pitch
(float) p.velocity, // velocity
(float) p.channel);
break;
}
case PolyphonicKeyPressure: { // polyphonic aftertouch
PolyphonicKeyPressureEvent p = m.AsPolyphonicKeyPressure();
hv.sendMessageToReceiverV(HV_HASH_POLYTOUCHIN, 0, "fff",
hv->sendMessageToReceiverV(HV_HASH_POLYTOUCHIN, 0, "fff",
(float) p.pressure, // pressure
(float) p.note, // note
(float) p.channel);
break;
}
case ControlChange: {
ControlChangeEvent p = m.AsControlChange();
hv.sendMessageToReceiverV(HV_HASH_CTLIN, 0, "fff",
hv->sendMessageToReceiverV(HV_HASH_CTLIN, 0, "fff",
(float) p.value, // value
(float) p.control_number, // cc number
(float) p.channel);
break;
}
case ProgramChange: {
ProgramChangeEvent p = m.AsProgramChange();
hv.sendMessageToReceiverV(HV_HASH_PGMIN, 0, "ff",
hv->sendMessageToReceiverV(HV_HASH_PGMIN, 0, "ff",
(float) p.program,
(float) p.channel);
break;
}
case ChannelPressure: {
ChannelPressureEvent p = m.AsChannelPressure();
hv.sendMessageToReceiverV(HV_HASH_TOUCHIN, 0, "ff",
hv->sendMessageToReceiverV(HV_HASH_TOUCHIN, 0, "ff",
(float) p.pressure,
(float) p.channel);
break;
Expand All @@ -186,7 +186,7 @@ void HandleMidiMessage(MidiEvent m)
PitchBendEvent p = m.AsPitchBend();
// combine 7bit lsb and msb into 32bit int
hv_uint32_t value = (((hv_uint32_t) m.data[1]) << 7) | ((hv_uint32_t) m.data[0]);
hv.sendMessageToReceiverV(HV_HASH_BENDIN, 0, "ff",
hv->sendMessageToReceiverV(HV_HASH_BENDIN, 0, "ff",
(float) value,
(float) p.channel);
break;
Expand All @@ -200,6 +200,8 @@ void HandleMidiMessage(MidiEvent m)
int main(void)
{
hardware.Init(true);
hv = new Heavy_{{patch_name}}(SAMPLE_RATE, {{pool_sizes_kb.internal}}, {{pool_sizes_kb.inputQueue}}, {{pool_sizes_kb.outputQueue}});

{% if samplerate %}
hardware.SetAudioSampleRate({{samplerate}});
{% endif %}
Expand All @@ -220,12 +222,12 @@ int main(void)
hardware.StartAudio(audiocallback);
{% if debug_printing %}
hardware.som.StartLog();
hv.setPrintHook(printHook);
hv->setPrintHook(printHook);

uint32_t now = System::GetNow();
uint32_t log_time = System::GetNow();
{% endif %}
hv.setSendHook(sendHook);
hv->setSendHook(sendHook);

for(;;)
{
Expand Down Expand Up @@ -288,7 +290,7 @@ void audiocallback(daisy::AudioHandle::InputBuffer in, daisy::AudioHandle::Outpu
hardware.ProcessAllControls();
CallbackWriteIn(hv);
{% endif %}
hv.process((float**)in, (float**)out, size);
hv->process((float**)in, (float**)out, size);
{% if output_parameters|length > 0 %}
CallbackWriteOut();
{% endif %}
Expand Down Expand Up @@ -472,29 +474,29 @@ static void printHook(HeavyContextInterface *c, const char *printLabel, const ch
/** Sends signals from the Daisy hardware to the PD patch via the receive objects during the main loop
*
*/
void LoopWriteIn(Heavy_{{patch_name}}& hv)
void LoopWriteIn(Heavy_{{patch_name}}* hv)
{
{% for param in loop_write_in %}
{% if param.bool %}
if ({{param.process}})
hv.sendBangToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}});
hv->sendBangToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}});
{% else %}
hv.sendFloatToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}}, {{param.process}});
hv->sendFloatToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}}, {{param.process}});
{% endif %}
{% endfor %}
}

/** Sends signals from the Daisy hardware to the PD patch via the receive objects during the audio callback
*
*/
void CallbackWriteIn(Heavy_{{patch_name}}& hv)
void CallbackWriteIn(Heavy_{{patch_name}}* hv)
{
{% for param in callback_write_in %}
{% if param.bool %}
if ({{param.process}})
hv.sendBangToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}});
hv->sendBangToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}});
{% else %}
hv.sendFloatToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}}, {{param.process}});
hv->sendFloatToReceiver((uint32_t) HV_{{patch_name|upper}}_PARAM_IN_{{param.hash_enum|upper}}, {{param.process}});
{% endif %}
{% endfor %}
}
Expand Down
6 changes: 3 additions & 3 deletions hvcc/generators/c2dpf/templates/Makefile_plugin
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ BUILD_CXX_FLAGS += -I ../../{{dpf_path}}{{dependency}}
{%- endfor %}
{%- endif %}

LINK_FLAGS += -lpthread
CFLAGS += -Wno-unused-parameter -std=c11
CXXFLAGS += -Wno-unused-parameter
BUILD_C_FLAGS += -Wno-unused-parameter -std=c11 -fno-strict-aliasing -pthread
BUILD_CXX_FLAGS += -Wno-unused-parameter -fno-strict-aliasing -pthread
LINK_FLAGS += -pthread

{% if meta.plugin_formats is defined %}
{%- for format in meta.plugin_formats %}
Expand Down
Loading

0 comments on commit 079a59e

Please sign in to comment.